-
Notifications
You must be signed in to change notification settings - Fork 755
/
ConnectionViewController.swift
340 lines (290 loc) · 15.9 KB
/
ConnectionViewController.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
/*
* Copyright 2010-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
import UIKit
import AWSIoT
import AWSMobileClient
class ConnectionViewController: UIViewController, UITextViewDelegate {
let IOT_CERT = "IoT Cert"
let IOT_WEBSOCKET = "IoT Websocket"
var connectIoTDataWebSocket: UIButton!
var activityIndicatorView: UIActivityIndicatorView!
var logTextView: UITextView!
var connectButton: UIButton!
@objc var connected = false
@objc var publishViewController : UIViewController!
@objc var subscribeViewController : UIViewController!
@objc var configurationViewController : UIViewController!
@objc var iotDataManager: AWSIoTDataManager!
@objc var iotManager: AWSIoTManager!
@objc var iot: AWSIoT!
func mqttEventCallback( _ status: AWSIoTMQTTStatus ) {
DispatchQueue.main.async {
let tabBarViewController = self.tabBarController as! IoTSampleTabBarController
print("connection status = \(status.rawValue)")
switch status {
case .connecting:
tabBarViewController.mqttStatus = "Connecting..."
print( tabBarViewController.mqttStatus )
self.logTextView.text = tabBarViewController.mqttStatus
case .connected:
tabBarViewController.mqttStatus = "Connected"
print( tabBarViewController.mqttStatus )
self.connectButton.setTitle( "Disconnect \(self.IOT_CERT)", for:UIControl.State())
self.activityIndicatorView.stopAnimating()
self.connected = true
self.connectButton.isEnabled = true
let uuid = UUID().uuidString;
let defaults = UserDefaults.standard
let certificateId = defaults.string( forKey: "certificateId")
self.logTextView.text = "Using certificate:\n\(certificateId!)\n\n\nClient ID:\n\(uuid)"
tabBarViewController.viewControllers = [ self, self.publishViewController, self.subscribeViewController ]
case .disconnected:
tabBarViewController.mqttStatus = "Disconnected"
print( tabBarViewController.mqttStatus )
self.activityIndicatorView.stopAnimating()
self.logTextView.text = nil
case .connectionRefused:
tabBarViewController.mqttStatus = "Connection Refused"
print( tabBarViewController.mqttStatus )
self.activityIndicatorView.stopAnimating()
self.logTextView.text = tabBarViewController.mqttStatus
case .connectionError:
tabBarViewController.mqttStatus = "Connection Error"
print( tabBarViewController.mqttStatus )
self.activityIndicatorView.stopAnimating()
self.logTextView.text = tabBarViewController.mqttStatus
case .protocolError:
tabBarViewController.mqttStatus = "Protocol Error"
print( tabBarViewController.mqttStatus )
self.activityIndicatorView.stopAnimating()
self.logTextView.text = tabBarViewController.mqttStatus
default:
tabBarViewController.mqttStatus = "Unknown State"
print("unknown state: \(status.rawValue)")
self.activityIndicatorView.stopAnimating()
self.logTextView.text = tabBarViewController.mqttStatus
}
NotificationCenter.default.post( name: Notification.Name(rawValue: "connectionStatusChanged"), object: self )
}
}
@objc func connectButtonPressed(_ sender: UIButton) {
sender.isEnabled = false
if (connected == false) {
handleConnectViaCert()
} else {
handleDisconnect()
DispatchQueue.main.async {
sender.setTitle("Connect \(self.IOT_CERT)", for:UIControl.State())
sender.isEnabled = true
}
}
}
func handleConnectViaCert() {
self.connectIoTDataWebSocket.isHidden = true
activityIndicatorView.startAnimating()
let defaults = UserDefaults.standard
let certificateId = defaults.string( forKey: "certificateId")
if (certificateId == nil) {
DispatchQueue.main.async {
self.logTextView.text = "No identity available, searching bundle..."
}
let certificateIdInBundle = searchForExistingCertificateIdInBundle()
if (certificateIdInBundle == nil) {
DispatchQueue.main.async {
self.logTextView.text = "No identity found in bundle, creating one..."
}
createCertificateIdAndStoreinNSUserDefaults(onSuccess: {generatedCertificateId in
let uuid = UUID().uuidString
self.logTextView.text = "Using certificate: \(generatedCertificateId)"
self.iotDataManager.connect( withClientId: uuid, cleanSession:true, certificateId:generatedCertificateId, statusCallback: self.mqttEventCallback)
}, onFailure: {error in
print("Received error: \(error)")
})
}
} else {
let uuid = UUID().uuidString;
// Connect to the AWS IoT data plane service w/ certificate
iotDataManager.connect( withClientId: uuid, cleanSession:true, certificateId:certificateId!, statusCallback: self.mqttEventCallback)
}
}
func searchForExistingCertificateIdInBundle() -> String? {
let defaults = UserDefaults.standard
// No certificate ID has been stored in the user defaults; check to see if any .p12 files
// exist in the bundle.
let myBundle = Bundle.main
let myImages = myBundle.paths(forResourcesOfType: "p12" as String, inDirectory:nil)
let uuid = UUID().uuidString
guard let certId = myImages.first else {
let certificateId = defaults.string(forKey: "certificateId")
return certificateId
}
// A PKCS12 file may exist in the bundle. Attempt to load the first one
// into the keychain (the others are ignored), and set the certificate ID in the
// user defaults as the filename. If the PKCS12 file requires a passphrase,
// you'll need to provide that here; this code is written to expect that the
// PKCS12 file will not have a passphrase.
guard let data = try? Data(contentsOf: URL(fileURLWithPath: certId)) else {
print("[ERROR] Found PKCS12 File in bundle, but unable to use it")
let certificateId = defaults.string( forKey: "certificateId")
return certificateId
}
DispatchQueue.main.async {
self.logTextView.text = "found identity \(certId), importing..."
}
if AWSIoTManager.importIdentity( fromPKCS12Data: data, passPhrase:"", certificateId:certId) {
// Set the certificate ID and ARN values to indicate that we have imported
// our identity from the PKCS12 file in the bundle.
defaults.set(certId, forKey:"certificateId")
defaults.set("from-bundle", forKey:"certificateArn")
DispatchQueue.main.async {
self.logTextView.text = "Using certificate: \(certId))"
self.iotDataManager.connect( withClientId: uuid,
cleanSession:true,
certificateId:certId,
statusCallback: self.mqttEventCallback)
}
}
let certificateId = defaults.string( forKey: "certificateId")
return certificateId
}
func createCertificateIdAndStoreinNSUserDefaults(onSuccess: @escaping (String)->Void,
onFailure: @escaping (Error) -> Void) {
let defaults = UserDefaults.standard
// Now create and store the certificate ID in NSUserDefaults
let csrDictionary = [ "commonName": CertificateSigningRequestCommonName,
"countryName": CertificateSigningRequestCountryName,
"organizationName": CertificateSigningRequestOrganizationName,
"organizationalUnitName": CertificateSigningRequestOrganizationalUnitName]
self.iotManager.createKeysAndCertificate(fromCsr: csrDictionary) { (response) -> Void in
guard let response = response else {
DispatchQueue.main.async {
self.connectButton.isEnabled = true
self.activityIndicatorView.stopAnimating()
self.logTextView.text = "Unable to create keys and/or certificate, check values in Constants.swift"
}
onFailure(NSError(domain: "No response on iotManager.createKeysAndCertificate", code: -2, userInfo: nil))
return
}
defaults.set(response.certificateId, forKey:"certificateId")
defaults.set(response.certificateArn, forKey:"certificateArn")
let certificateId = response.certificateId
print("response: [\(String(describing: response))]")
let attachPrincipalPolicyRequest = AWSIoTAttachPrincipalPolicyRequest()
attachPrincipalPolicyRequest?.policyName = POLICY_NAME
attachPrincipalPolicyRequest?.principal = response.certificateArn
// Attach the policy to the certificate
self.iot.attachPrincipalPolicy(attachPrincipalPolicyRequest!).continueWith (block: { (task) -> AnyObject? in
if let error = task.error {
print("Failed: [\(error)]")
onFailure(error)
} else {
print("result: [\(String(describing: task.result))]")
DispatchQueue.main.asyncAfter(deadline: .now()+2, execute: {
if let certificateId = certificateId {
onSuccess(certificateId)
} else {
onFailure(NSError(domain: "Unable to generate certificate id", code: -1, userInfo: nil))
}
})
}
return nil
})
}
}
func handleDisconnect() {
let tabBarViewController = tabBarController as! IoTSampleTabBarController
self.connectButton.isHidden = false
self.connectIoTDataWebSocket.isHidden = false
activityIndicatorView.startAnimating()
logTextView.text = "Disconnecting..."
DispatchQueue.global(qos: DispatchQoS.QoSClass.default).async {
self.iotDataManager.disconnect();
DispatchQueue.main.async {
self.connected = false
tabBarViewController.viewControllers = [ self, self.configurationViewController ]
}
}
}
/*
* This function sets two methods for to connecting to IoT
* 1. Which uses a websocket to connect to an account specific endpoint you can get from the IoT Core Console
* 2. Which requests for a cert to the IoT control-plane and then uses that cert to connect to the data plane
* We would expect most users of the AWS IoT SDK for iOS to use one or the other, but not both. Nonetheless, we have
* both of these examples in the same view controller.
*/
override func viewDidLoad() {
super.viewDidLoad()
setupViewComponents()
let tabBarViewController = tabBarController as! IoTSampleTabBarController
publishViewController = tabBarViewController.viewControllers![1]
subscribeViewController = tabBarViewController.viewControllers![2]
configurationViewController = tabBarViewController.viewControllers![3]
tabBarViewController.viewControllers = [ self, configurationViewController ]
logTextView.resignFirstResponder()
let credentialsProvider = AWSCognitoCredentialsProvider(regionType:AWS_REGION,
identityPoolId:IDENTITY_POOL_ID)
initializeControlPlane(credentialsProvider: credentialsProvider)
initializeDataPlane(credentialsProvider: credentialsProvider)
}
func initializeControlPlane(credentialsProvider: AWSCredentialsProvider) {
//Initialize control plane
// Initialize the Amazon Cognito credentials provider
let controlPlaneServiceConfiguration = AWSServiceConfiguration(region:AWS_REGION, credentialsProvider:credentialsProvider)
//IoT control plane seem to operate on iot.<region>.amazonaws.com
//Set the defaultServiceConfiguration so that when we call AWSIoTManager.default(), it will get picked up
AWSServiceManager.default().defaultServiceConfiguration = controlPlaneServiceConfiguration
iotManager = AWSIoTManager.default()
iot = AWSIoT.default()
}
func initializeDataPlane(credentialsProvider: AWSCredentialsProvider) {
//Initialize Dataplane:
// IoT Dataplane must use your account specific IoT endpoint
let iotEndPoint = AWSEndpoint(urlString: IOT_ENDPOINT)
// Configuration for AWSIoT data plane APIs
let iotDataConfiguration = AWSServiceConfiguration(region: AWS_REGION,
endpoint: iotEndPoint,
credentialsProvider: credentialsProvider)
//IoTData manager operates on xxxxxxx-iot.<region>.amazonaws.com
AWSIoTDataManager.register(with: iotDataConfiguration!, forKey: AWS_IOT_DATA_MANAGER_KEY)
iotDataManager = AWSIoTDataManager(forKey: AWS_IOT_DATA_MANAGER_KEY)
}
func setupViewComponents() {
let labelHeight = 44 * 4
let buttonWidth = 300
let buttonHeight = 44
let spacer = 10
let x = 44
var y = 100
connectIoTDataWebSocket = UIButton(frame: CGRect(x: x, y: y, width: buttonWidth, height: buttonHeight))
connectIoTDataWebSocket.setTitle("Connect \(self.IOT_WEBSOCKET)", for: .normal)
connectIoTDataWebSocket.tintColor = .black
connectIoTDataWebSocket.backgroundColor = .lightGray
y += spacer + buttonHeight
connectButton = UIButton(frame: CGRect(x: x, y: y, width: buttonWidth, height: buttonHeight))
connectButton.setTitle("Connect \(self.IOT_CERT)", for: .normal)
connectButton.tintColor = .black
connectButton.backgroundColor = .lightGray
y += spacer + buttonHeight
logTextView = UITextView(frame: CGRect(x: 5, y: y, width: 300, height: labelHeight))
y += spacer + labelHeight
activityIndicatorView = UIActivityIndicatorView(frame: CGRect(x: x, y: y, width: 44, height: 44))
connectIoTDataWebSocket.addTarget(self, action: #selector(didTapConnectIoTDataWebSocket(_:)), for: .touchUpInside)
connectButton.addTarget(self, action: #selector(connectButtonPressed(_:)), for: .touchUpInside)
self.view.addSubview(connectIoTDataWebSocket)
self.view.addSubview(connectButton)
self.view.addSubview(logTextView)
self.view.addSubview(activityIndicatorView)
}
}