Swift support for NetworkPlugin (#201)

Summary:
Solves #173

- [x] Add plugins in the sonarkit xcodeproj so that its easy to debug
- [x] Get rid off the c++ in the headers
- [x] Add example to hit network requests and validate

Have a look at the screen shot below ️

<img width="1677" alt="screen shot 2018-08-02 at 12 46 07 pm" src="https://user-images.githubusercontent.com/3865908/43581809-22efe4fe-9652-11e8-9424-f279d07c5c81.png">
Pull Request resolved: https://github.com/facebook/flipper/pull/201

Reviewed By: danielbuechele

Differential Revision: D9132157

Pulled By: priteshrnandgaonkar

fbshipit-source-id: 2b425506961f02eb2bf629c2bcab0da6e7ce5bb0
This commit is contained in:
Pritesh Nandgaonkar
2018-08-06 12:34:08 -07:00
committed by Facebook Github Bot
parent 134a0d96c5
commit 0c60347593
20 changed files with 863 additions and 147 deletions

View File

@@ -7,7 +7,7 @@ target 'SampleSwift' do
pod 'SonarKit', '~> 0.6'
# Layout and network plugins are not yet supported for swift projects
# pod 'SonarKit/SonarKitLayoutComponentKitSupport', '~> 0.0.2'
# pod 'SonarKit/SKIOSNetworkPlugin', '~> 0.0.2'
pod 'SonarKit/SKIOSNetworkPlugin', '~> 0.6'
post_install do |installer|
installer.pods_project.targets.each do |target|

View File

@@ -7,7 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
076C8BE348B2F2F251103C6F /* libPods-SampleSwift.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 116D6234DE1F7298C4C2836E /* libPods-SampleSwift.a */; };
532FF2DF211316ED00FC5A10 /* NetworkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 532FF2DE211316ED00FC5A10 /* NetworkViewController.swift */; };
629899EF3F77DD9CC9462399 /* libPods-SampleSwift.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 54D075F591C612F4A46C4C50 /* libPods-SampleSwift.a */; };
A19C402720E20023004BF1F7 /* Icons.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A19C402620E20023004BF1F7 /* Icons.xcassets */; };
A1EC522D20DED61B007C6977 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EC522C20DED61B007C6977 /* AppDelegate.swift */; };
A1EC522F20DED61B007C6977 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1EC522E20DED61B007C6977 /* ViewController.swift */; };
@@ -16,8 +17,10 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
116D6234DE1F7298C4C2836E /* libPods-SampleSwift.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SampleSwift.a"; sourceTree = BUILT_PRODUCTS_DIR; };
37AC1E08B3D4DC8183172E21 /* Pods-SampleSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleSwift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SampleSwift/Pods-SampleSwift.debug.xcconfig"; sourceTree = "<group>"; };
532FF2DE211316ED00FC5A10 /* NetworkViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkViewController.swift; sourceTree = "<group>"; };
54D075F591C612F4A46C4C50 /* libPods-SampleSwift.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SampleSwift.a"; sourceTree = BUILT_PRODUCTS_DIR; };
59BC319BD0C305C87CCBB954 /* Pods-SampleSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleSwift.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SampleSwift/Pods-SampleSwift.debug.xcconfig"; sourceTree = "<group>"; };
76A1EA0FA19A4343BF5468B9 /* Pods-SampleSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleSwift.release.xcconfig"; path = "Pods/Target Support Files/Pods-SampleSwift/Pods-SampleSwift.release.xcconfig"; sourceTree = "<group>"; };
A19C402620E20023004BF1F7 /* Icons.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Icons.xcassets; sourceTree = SOURCE_ROOT; };
A1EC522920DED61B007C6977 /* SampleSwift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleSwift.app; sourceTree = BUILT_PRODUCTS_DIR; };
A1EC522C20DED61B007C6977 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -25,7 +28,6 @@
A1EC523620DED61C007C6977 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
A1EC523820DED61C007C6977 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A1EC523E20DEDC00007C6977 /* MainStoryBoard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryBoard.storyboard; sourceTree = "<group>"; };
B4D379232B75ED516DAC4010 /* Pods-SampleSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleSwift.release.xcconfig"; path = "Pods/Target Support Files/Pods-SampleSwift/Pods-SampleSwift.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -33,37 +35,20 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
076C8BE348B2F2F251103C6F /* libPods-SampleSwift.a in Frameworks */,
629899EF3F77DD9CC9462399 /* libPods-SampleSwift.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
3AAFD4FB7381DCF64BCC28F8 /* Frameworks */ = {
isa = PBXGroup;
children = (
116D6234DE1F7298C4C2836E /* libPods-SampleSwift.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
86D600B594F09E018D741296 /* Pods */ = {
isa = PBXGroup;
children = (
37AC1E08B3D4DC8183172E21 /* Pods-SampleSwift.debug.xcconfig */,
B4D379232B75ED516DAC4010 /* Pods-SampleSwift.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
A1EC522020DED61B007C6977 = {
isa = PBXGroup;
children = (
A1EC522B20DED61B007C6977 /* SampleSwift */,
A1EC522A20DED61B007C6977 /* Products */,
86D600B594F09E018D741296 /* Pods */,
3AAFD4FB7381DCF64BCC28F8 /* Frameworks */,
D64C1609A23F7F455EB46170 /* Pods */,
FCE001267EA01B99C44EBA11 /* Frameworks */,
);
sourceTree = "<group>";
};
@@ -84,10 +69,28 @@
A1EC522E20DED61B007C6977 /* ViewController.swift */,
A1EC523520DED61C007C6977 /* LaunchScreen.storyboard */,
A1EC523820DED61C007C6977 /* Info.plist */,
532FF2DE211316ED00FC5A10 /* NetworkViewController.swift */,
);
path = SampleSwift;
sourceTree = "<group>";
};
D64C1609A23F7F455EB46170 /* Pods */ = {
isa = PBXGroup;
children = (
59BC319BD0C305C87CCBB954 /* Pods-SampleSwift.debug.xcconfig */,
76A1EA0FA19A4343BF5468B9 /* Pods-SampleSwift.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
FCE001267EA01B99C44EBA11 /* Frameworks */ = {
isa = PBXGroup;
children = (
54D075F591C612F4A46C4C50 /* libPods-SampleSwift.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -95,7 +98,7 @@
isa = PBXNativeTarget;
buildConfigurationList = A1EC523B20DED61C007C6977 /* Build configuration list for PBXNativeTarget "SampleSwift" */;
buildPhases = (
EC305E9596C92E7DA323DDC1 /* [CP] Check Pods Manifest.lock */,
FEB6C3EADB1D3B088347CC11 /* [CP] Check Pods Manifest.lock */,
A1EC522520DED61B007C6977 /* Sources */,
A1EC522620DED61B007C6977 /* Frameworks */,
A1EC522720DED61B007C6977 /* Resources */,
@@ -156,7 +159,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
EC305E9596C92E7DA323DDC1 /* [CP] Check Pods Manifest.lock */ = {
FEB6C3EADB1D3B088347CC11 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -182,6 +185,7 @@
buildActionMask = 2147483647;
files = (
A1EC522F20DED61B007C6977 /* ViewController.swift in Sources */,
532FF2DF211316ED00FC5A10 /* NetworkViewController.swift in Sources */,
A1EC522D20DED61B007C6977 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -316,7 +320,7 @@
};
A1EC523C20DED61C007C6977 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 37AC1E08B3D4DC8183172E21 /* Pods-SampleSwift.debug.xcconfig */;
baseConfigurationReference = 59BC319BD0C305C87CCBB954 /* Pods-SampleSwift.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
@@ -341,8 +345,38 @@
"$(inherited)",
"@executable_path/Frameworks",
);
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"-DFB_SONARKIT_ENABLED=1",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-l\"CocoaAsyncSocket\"",
"-l\"DoubleConversion\"",
"-l\"Folly\"",
"-l\"PeerTalk\"",
"-l\"RSocket\"",
"-l\"Sonar\"",
"-l\"SonarKit\"",
"-l\"crypto\"",
"-l\"event\"",
"-l\"event_core\"",
"-l\"event_extra\"",
"-l\"event_pthreads\"",
"-l\"glog\"",
"-l\"ssl\"",
"-l\"stdc++\"",
"-framework",
"\"CFNetwork\"",
"-framework",
"\"Security\"",
"-DFB_SONARKIT_ENABLED=1",
);
OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/SonarKit/SonarKit.modulemap\" -Xcc -DFB_SONARKIT_ENABLED";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.flipper.SampleSwift;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -350,7 +384,7 @@
};
A1EC523D20DED61C007C6977 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B4D379232B75ED516DAC4010 /* Pods-SampleSwift.release.xcconfig */;
baseConfigurationReference = 76A1EA0FA19A4343BF5468B9 /* Pods-SampleSwift.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
@@ -375,6 +409,35 @@
"$(inherited)",
"@executable_path/Frameworks",
);
OTHER_CPLUSPLUSFLAGS = (
"$(OTHER_CFLAGS)",
"-DFB_SONARKIT_ENABLED=1",
);
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
"-l\"CocoaAsyncSocket\"",
"-l\"DoubleConversion\"",
"-l\"Folly\"",
"-l\"PeerTalk\"",
"-l\"RSocket\"",
"-l\"Sonar\"",
"-l\"SonarKit\"",
"-l\"crypto\"",
"-l\"event\"",
"-l\"event_core\"",
"-l\"event_extra\"",
"-l\"event_pthreads\"",
"-l\"glog\"",
"-l\"ssl\"",
"-l\"stdc++\"",
"-framework",
"\"CFNetwork\"",
"-framework",
"\"Security\"",
"-DFB_SONARKIT_ENABLED=1",
);
OTHER_SWIFT_FLAGS = "$(inherited) -Xcc -fmodule-map-file=\"${PODS_ROOT}/Headers/Public/SonarKit/SonarKit.modulemap\" -Xcc -DFB_SONARKIT_ENABLED";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.flipper.SampleSwift;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;

View File

@@ -17,7 +17,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
// let layoutDescriptorMapper = SKDescriptorMapper(defaults: ())
// SonarKitLayoutComponentKitSupport.setUp(descriptorMapper: layoutDescriptorMapper)
// client.addPlugin(SonarKitLayoutPlugin(rootNode: application, descriptorMapper: layoutDescriptorMapper))
// client.addPlugin(SonarKitNetworkPlugin(networkAdapter: SKIOSNetworkAdapter()))
client?.add(SonarKitNetworkPlugin(networkAdapter: SKIOSNetworkAdapter()))
client?.start()
let storyboard = UIStoryboard(name: "MainStoryBoard", bundle: nil)

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -31,7 +31,7 @@
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="tappedNetworkInspector:" destination="2r2-64-LPh" eventType="touchUpInside" id="6Ta-mA-Ybz"/>
<segue destination="qMe-JX-czP" kind="push" id="Poz-iX-aCZ"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xh0-Q4-60g">
@@ -44,9 +44,6 @@
<state key="normal" title="ComponentKit Layout">
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="tappedComponentKitLayout:" destination="2r2-64-LPh" eventType="touchUpInside" id="amo-e0-rJv"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -72,7 +69,7 @@
<!--Network View Controller-->
<scene sceneID="Zgm-Mh-TPB">
<objects>
<viewController storyboardIdentifier="NetworkViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="qMe-JX-czP" customClass="NetworkViewController" sceneMemberID="viewController">
<viewController storyboardIdentifier="NetworkViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="qMe-JX-czP" customClass="NetworkViewController" customModule="SampleSwift" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="1kD-re-kO3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@@ -93,7 +90,7 @@
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="tappedGetAPI:" destination="qMe-JX-czP" eventType="touchUpInside" id="Xb9-5E-G2l"/>
<action selector="tappedGetAPI:" destination="qMe-JX-czP" eventType="touchUpInside" id="jq4-HI-nDK"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Fel-3h-2Ts">
@@ -107,7 +104,7 @@
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="tappedPOSTAPI:" destination="qMe-JX-czP" eventType="touchUpInside" id="kv5-Bd-q86"/>
<action selector="tappedPOSTAPI:" destination="qMe-JX-czP" eventType="touchUpInside" id="gqv-I8-C9f"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cYg-iA-yAD">
@@ -122,7 +119,7 @@
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="tappedGithubLitho:" destination="qMe-JX-czP" eventType="touchUpInside" id="SPR-ez-foo"/>
<action selector="tappedFetchFBLitho:" destination="qMe-JX-czP" eventType="touchUpInside" id="bLR-k5-ebI"/>
</connections>
</button>
</subviews>
@@ -153,6 +150,7 @@
</constraints>
<viewLayoutGuide key="safeArea" id="dLV-zh-N2b"/>
</view>
<navigationItem key="navigationItem" id="foE-rU-54N"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="jLg-IP-htV" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>

View File

@@ -0,0 +1,88 @@
// Copyright 2004-present Facebook. All Rights Reserved.
import UIKit
class NetworkViewController: UIViewController {
@IBAction func tappedGetAPI(_ sender: UIButton) {
let getURL = URL(string: "https://demo9512366.mockable.io/FlipperGet")!
let dataTask = URLSession.shared.dataTask(with: getURL){ [weak self] (data, response, error) in
guard let strongSelf = self else { return }
guard let dataUnwrapped = data else {
strongSelf.showAlert(message: "Received no data in GET API")
return
}
if let errorUnwrapped = error {
strongSelf.showAlert(message: "Received error in GET API Error:\(errorUnwrapped.localizedDescription)")
return
}
let dict = try! JSONSerialization.jsonObject(with: dataUnwrapped, options: JSONSerialization.ReadingOptions.init(rawValue: 0)) as! [String: String]
// As sonar cannot detect print() in Logs
NSLog("MSG-GET: \(dict["msg"] ?? "Did not find msg key in the received response")")
strongSelf.showAlert(message: "Received response from GET API, please check the sonar network plugin for detailed response")
}
dataTask.resume()
}
@IBAction func tappedPOSTAPI(_ sender: UIButton) {
guard let postURL = URL(string: "https://demo9512366.mockable.io/FlipperPost") else {
showAlert(message: "Check the POST URL")
return
}
var postRequest = URLRequest(url: postURL)
postRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
postRequest.addValue("application/json", forHTTPHeaderField: "Accept")
let dict = ["app" : "Flipper", "remarks": "Its Awesome"]
postRequest.httpBody = try! JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions.init(rawValue: 0))
postRequest.httpMethod = "POST"
let dataTask = URLSession.shared.dataTask(with: postRequest){ [weak self] (data, response, error) in
guard let strongSelf = self else { return }
guard let dataUnwrapped = data else {
strongSelf.showAlert(message: "Received no data in POST API")
return
}
if let errorUnwrapped = error {
strongSelf.showAlert(message: "Received error in POST API Error:\(errorUnwrapped.localizedDescription)")
return
}
let dict = try! JSONSerialization.jsonObject(with: dataUnwrapped, options: JSONSerialization.ReadingOptions.init(rawValue: 0)) as! [String: String]
// As sonar cannot detect print() in Logs
NSLog("MSG-POST: \(dict["msg"] ?? "Did not find msg key in the received response")")
strongSelf.showAlert(message: "Received response from POST API, please check the sonar network plugin for detailed response")
}
dataTask.resume()
}
@IBAction func tappedFetchFBLitho(_ sender: UIButton) {
let imageURL = URL(string: "https://raw.githubusercontent.com/facebook/litho/master/docs/static/logo.png")!
let dataTask = URLSession.shared.dataTask(with: imageURL){ [weak self] (data, response, error) in
guard let strongSelf = self else { return }
guard let _ = data else {
strongSelf.showAlert(message: "Received no data in Images API")
return
}
if let errorUnwrapped = error {
strongSelf.showAlert(message: "Received error in Images API Error:\(errorUnwrapped.localizedDescription)")
return
}
// As sonar cannot detect print() in Logs
NSLog("Got Image")
strongSelf.showAlert(message: "Received Litho Image")
}
dataTask.resume()
}
func showAlert(message: String) {
let alertController = UIAlertController.init(title: "Flipper", message: message, preferredStyle: .alert);
let alertAction = UIAlertAction(title: "Okay", style: UIAlertActionStyle.default, handler: nil)
alertController.addAction(alertAction)
present(alertController, animated: true, completion: nil)
}
}