From f85c33942d88b2490e3a446edaf85cd54e013f85 Mon Sep 17 00:00:00 2001 From: Vikas Chandra Date: Thu, 11 Jun 2020 14:05:14 +0530 Subject: [PATCH] - Updated library to version 2.0.1. - Encryption/decryption of request/response within lib. - Retrying session establishment when communication fails. - Updated error cases. --- ESPProvision.podspec | 2 +- ESPProvision/ESPDevice.swift | 121 +++++++++++++++--- ESPProvision/ESPProvisionManager.swift | 2 +- .../Transport/ESPSoftAPTransport.swift | 10 +- ESPProvision/Utility/ESPErrors.swift | 12 ++ Podfile | 2 +- 6 files changed, 125 insertions(+), 24 deletions(-) diff --git a/ESPProvision.podspec b/ESPProvision.podspec index 6f63008..850fb42 100644 --- a/ESPProvision.podspec +++ b/ESPProvision.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |spec| spec.name = "ESPProvision" - spec.version = "2.0" + spec.version = "2.0.1" spec.summary = "ESP-IDF provisioning in Swift" spec.description = "It provides mechanism to provide network credentials and/or custom data to an ESP32 or ESP32-S2 devices" spec.homepage = "https://github.com/espressif/esp-idf-provisioning-ios" diff --git a/ESPProvision/ESPDevice.swift b/ESPProvision/ESPDevice.swift index 1fa6ed7..cf33781 100644 --- a/ESPProvision/ESPDevice.swift +++ b/ESPProvision/ESPDevice.swift @@ -37,6 +37,8 @@ public enum ESPProvisionStatus { case success /// Failed to provision device. case failure(ESPProvisionError) + /// Applied configuration + case configApplied } /// Class needs to conform to `ESPDeviceConnectionDelegate` protcol when trying to establish a connection. @@ -83,6 +85,7 @@ public class ESPDevice { private var provision: ESPProvision! private var softAPPassword:String? private var proofOfPossession:String? + private var retryScan = false /// Get name of current `ESPDevice`. public var name:String { @@ -197,33 +200,61 @@ public class ESPDevice { /// - completionHandler: The completion handler that is called when data transmission is successful. /// Parameter of block include response recieved from the HTTP request or error if any. public func sendData(path:String, data:Data, completionHandler: @escaping (Data?, ESPSessionError?) -> Swift.Void) { - if session == nil, !session.isEstablished { completionHandler(nil,.sessionNotEstablished) } else { - self.sendDataToDevice(path: path, data: data, completionHandler: completionHandler) + self.sendDataToDevice(path: path, data: data, retryOnce: true, completionHandler: completionHandler) } } - private func sendDataToDevice(path:String, data:Data, completionHandler: @escaping (Data?, ESPSessionError?) -> Swift.Void) { + private func sendDataToDevice(path:String, data:Data, retryOnce:Bool, completionHandler: @escaping (Data?, ESPSessionError?) -> Swift.Void) { + guard let encryptedData = securityLayer.encrypt(data: data) else { + completionHandler(nil,.securityMismatch) + return + } switch transport { case .ble: - espBleTransport.SendConfigData(path: path, data: data) { response, error in + espBleTransport.SendConfigData(path: path, data: encryptedData) { response, error in guard error == nil, response != nil else { completionHandler(nil,.sendDataError(error!)) return } - completionHandler(response,nil) + if let responseData = self.securityLayer.decrypt(data: response!) { + completionHandler(responseData, nil) + } else { + completionHandler(nil,.encryptionError) + } } default: - ESPLog.log("New SoftAp transport.") - espSoftApTransport.SendConfigData(path: path, data: data) { response, error in - guard error == nil, response != nil else { + espSoftApTransport.SendConfigData(path: path, data: encryptedData) { response, error in + + if error != nil, response == nil { + if retryOnce, self.isNetworkDisconnected(error: error!) { + DispatchQueue.main.async { + ESPLog.log("Retrying sending data to custom path...") + self.connect { status in + switch status { + case .connected: + self.sendDataToDevice(path: path, data: data, retryOnce: false, completionHandler: completionHandler) + return + default: + completionHandler(nil,.sendDataError(error!)) + return + } + } + } + } + else { completionHandler(nil,.sendDataError(error!)) - return } - completionHandler(response,nil) + } else { + if let responseData = self.securityLayer.decrypt(data: response!) { + completionHandler(responseData, nil) + } else { + completionHandler(nil,.encryptionError) + } } + } } } @@ -239,13 +270,13 @@ public class ESPDevice { if session == nil, !session.isEstablished { completionHandler(.failure(.sessionError)) } else { - provisionDevice(ssid: ssid, passPhrase: passPhrase, completionHandler: completionHandler) + provisionDevice(ssid: ssid, passPhrase: passPhrase, retryOnce: true, completionHandler: completionHandler) } } - private func provisionDevice(ssid: String, passPhrase: String = "", completionHandler: @escaping (ESPProvisionStatus) -> Void) { + private func provisionDevice(ssid: String, passPhrase: String = "", retryOnce: Bool, completionHandler: @escaping (ESPProvisionStatus) -> Void) { provision = ESPProvision(session: session) ESPLog.log("Configure wi-fi credentials in device.") provision.configureWifi(ssid: ssid, passphrase: passPhrase) { status, error in @@ -258,6 +289,7 @@ public class ESPDevice { completionHandler(.failure(.configurationError(error!))) return } + completionHandler(.configApplied) } }, wifiStatusUpdatedHandler: { wifiState, failReason, error in @@ -286,8 +318,25 @@ public class ESPDevice { } }) default: - completionHandler(.failure(.wifiStatusUnknownError)) - return + if error != nil, self.isNetworkDisconnected(error: error!) { + DispatchQueue.main.async { + self.connect { status in + switch status { + case .connected: + self.provisionDevice(ssid: ssid, passPhrase: passPhrase, retryOnce: false, completionHandler: completionHandler) + return + default: + completionHandler(.failure(.configurationError(error!))) + } + } + } + } else { + if let configError = error { + completionHandler(.failure(.configurationError(configError))) + return + } + completionHandler(.failure(.wifiStatusUnknownError)) + } } } } @@ -302,13 +351,17 @@ public class ESPDevice { /// - Parameter completionHandler: The completion handler that is called when Wi-Fi list is scanned. /// Parameter of block include list of available Wi-Fi network or error in case of failure. public func scanWifiList(completionHandler: @escaping ([ESPWifiNetwork]?,ESPWiFiScanError?) -> Void) { + retryScan = true + scanDeviceForWifiList(completionHandler: completionHandler) + } + + private func scanDeviceForWifiList(completionHandler: @escaping ([ESPWifiNetwork]?,ESPWiFiScanError?) -> Void) { self.wifiListCompletionHandler = completionHandler let scanWifiManager: ESPWiFiManager = ESPWiFiManager(session: self.session!) scanWifiManager.delegate = self wifiListCompletionHandler = completionHandler scanWifiManager.startWifiScan() } - /// Initialise session with `ESPDevice`. /// /// - Parameter completionHandler: The completion handler that is called when session is initalised @@ -419,6 +472,15 @@ public class ESPDevice { print(error) } } + + private func isNetworkDisconnected(error: Error) -> Bool { + if let nserror = error as NSError? { + if nserror.code == -1005 { + return true + } + } + return false + } } extension ESPDevice: ESPScanWifiListProtocol { @@ -427,11 +489,32 @@ extension ESPDevice: ESPScanWifiListProtocol { wifiListCompletionHandler?(wifiResult,nil) return } - wifiListCompletionHandler?(nil,nil) + if retryScan { + self.retryScan = false + switch error { + case .scanRequestError(let requestError): + if isNetworkDisconnected(error: requestError) { + DispatchQueue.main.async { + self.connect { status in + switch status { + case .connected: + self.scanDeviceForWifiList(completionHandler: self.wifiListCompletionHandler!) + default: + self.wifiListCompletionHandler?(nil,error) + } + } + } + } else { + self.wifiListCompletionHandler?(nil,error) + } + default: + self.wifiListCompletionHandler?(nil,error) + } + } else { + self.wifiListCompletionHandler?(nil,error) + } + } } - - -} extension ESPDevice: ESPBLEStatusDelegate { func peripheralConnected() { diff --git a/ESPProvision/ESPProvisionManager.swift b/ESPProvision/ESPProvisionManager.swift index c1e76a1..018f076 100644 --- a/ESPProvision/ESPProvisionManager.swift +++ b/ESPProvision/ESPProvisionManager.swift @@ -280,7 +280,7 @@ public class ESPProvisionManager: NSObject, AVCaptureMetadataOutputObjectsDelega extension ESPProvisionManager: ESPBLETransportDelegate { func peripheralsFound(peripherals: [CBPeripheral]) { - ESPLog.log("Ble devices found.") + ESPLog.log("Ble devices found :\(peripherals)") espDevices.removeAll() for peripehral in peripherals { diff --git a/ESPProvision/Transport/ESPSoftAPTransport.swift b/ESPProvision/Transport/ESPSoftAPTransport.swift index 8e4fe19..cc0a061 100644 --- a/ESPProvision/Transport/ESPSoftAPTransport.swift +++ b/ESPProvision/Transport/ESPSoftAPTransport.swift @@ -17,6 +17,7 @@ // import Foundation +import Network import NetworkExtension @@ -26,6 +27,7 @@ class ESPSoftAPTransport: ESPCommunicable { /// Instance of `ESPUtility`. var utility: ESPUtility + var session:URLSession /// Check device configuration status. /// @@ -43,6 +45,9 @@ class ESPSoftAPTransport: ESPCommunicable { init(baseUrl: String) { self.baseUrl = baseUrl utility = ESPUtility() + let config = URLSessionConfiguration.default + config.allowsCellularAccess = false + session = URLSession(configuration: config) } /// Implementation of generic HTTP Request. @@ -62,10 +67,11 @@ class ESPSoftAPTransport: ESPCommunicable { request.httpMethod = "POST" request.httpBody = data + request.timeoutInterval = 30 - ESPLog.log("URLSession request initiated...") + ESPLog.log("URLSession request initiated with data...\(data)") - let task = URLSession.shared.dataTask(with: request) { data, response, error in + let task = session.dataTask(with: request) { data, response, error in ESPLog.log("Processing response..") guard let data = data, error == nil else { ESPLog.log("Error occured on HTTP request. Error: \(error.debugDescription)") diff --git a/ESPProvision/Utility/ESPErrors.swift b/ESPProvision/Utility/ESPErrors.swift index ff609c4..d8f5e00 100644 --- a/ESPProvision/Utility/ESPErrors.swift +++ b/ESPProvision/Utility/ESPErrors.swift @@ -79,6 +79,8 @@ public enum ESPSessionError: ESPError { case versionInfoError(Error) /// The attempt to connect with ESPDevice of bluetooth capability failed. case bleFailedToConnect + /// Encryption error + case encryptionError public var description: String { switch self { @@ -96,6 +98,8 @@ public enum ESPSessionError: ESPError { return "Failed to get device version information with error: \(error.localizedDescription)" case .bleFailedToConnect: return "Failed to connect with BLE device" + case .encryptionError: + return "Unable to encrypt data" } } @@ -115,6 +119,8 @@ public enum ESPSessionError: ESPError { return 16 case .bleFailedToConnect: return 17 + case .encryptionError: + return 18 } } } @@ -200,6 +206,8 @@ public enum ESPProvisionError: ESPError { case wifiStatusNetworkNotFound /// Wi-Fi status of ESPDevice is unknown. case wifiStatusUnknownError + /// Unkown error + case unknownError public var description: String { switch self { @@ -217,6 +225,8 @@ public enum ESPProvisionError: ESPError { return "Wi-Fi status netowrk not found" case .wifiStatusUnknownError: return "Wi-Fi status unknown error" + case .unknownError: + return "Unknown error" } } @@ -236,6 +246,8 @@ public enum ESPProvisionError: ESPError { return 36 case .wifiStatusUnknownError: return 37 + case .unknownError: + return 38 } } } diff --git a/Podfile b/Podfile index c47a2aa..13ec3cd 100644 --- a/Podfile +++ b/Podfile @@ -1,5 +1,5 @@ # Uncomment the next line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '11.0' target 'ESPProvision' do # Comment the next line if you don't want to use dynamic frameworks