diff --git a/Sources/WalletSdk/MDoc.swift b/Sources/WalletSdk/MDoc.swift index a2e7111..cecf635 100644 --- a/Sources/WalletSdk/MDoc.swift +++ b/Sources/WalletSdk/MDoc.swift @@ -82,11 +82,11 @@ public class BLESessionManager { secKey = item as! SecKey // swiftlint:enable force_cast case errSecItemNotFound: - self.callback.update(state: .error("Key not found")) + self.callback.update(state: .error(.generic("Key not found"))) self.cancel() return case let status: - self.callback.update(state: .error("Keychain read failed: \(status)")) + self.callback.update(state: .error(.generic("Keychain read failed: \(status)"))) self.cancel() return } @@ -95,7 +95,7 @@ public class BLESessionManager { .ecdsaSignatureMessageX962SHA256, payload as CFData, &error) as Data? else { - self.callback.update(state: .error("Failed to sign message: \(error.debugDescription)")) + self.callback.update(state: .error(.generic("Failed to sign message: \(error.debugDescription)"))) self.cancel() return } @@ -103,7 +103,7 @@ public class BLESessionManager { derSignature: derSignature) self.bleManager.writeOutgoingValue(data: response) } catch { - self.callback.update(state: .error("\(error)")) + self.callback.update(state: .error(.generic("\(error)"))) self.cancel() } } @@ -115,34 +115,57 @@ extension BLESessionManager: MDocBLEDelegate { case .done: self.callback.update(state: .success) case .connected: - self.callback.update(state: .progress("Connected")) - case .progress(let message): - self.callback.update(state: .progress(message)) + self.callback.update(state: .connected) + case .uploadProgress(let value, let total): + self.callback.update(state: .uploadProgress(value, total)) case .message(let data): do { let requestData = try SpruceIDWalletSdkRs.handleRequest(state: self.state, request: data) self.sessionManager = requestData.sessionManager self.callback.update(state: .selectNamespaces(requestData.itemsRequests)) } catch { - self.callback.update(state: .error("\(error)")) + self.callback.update(state: .error(.generic("\(error)"))) self.cancel() } case .error(let error): - self.callback.update(state: .error("\(error)")) + self.callback.update(state: .error(BleSessionError(holderBleError: error))) self.cancel() } } } +public enum BleSessionError { + /// When discovery or communication with the peripheral fails + case peripheral(String) + /// When Bluetooth is unusable (e.g. unauthorized). + case bluetooth(CBCentralManager) + /// Generic unrecoverable error + case generic(String) + + init(holderBleError: MdocHolderBleError) { + switch holderBleError { + case .peripheral(let string): + self = .peripheral(string) + case .bluetooth(let string): + self = .bluetooth(string) + } + } +} + public enum BLESessionState { /// App should display the error message - case error(String) + case error(BleSessionError) /// App should display the QR code case engagingQRCode(Data) - /// App should indicate to the user that progress is being made - case progress(String) + /// App should indicate to the user that BLE connection has been made + case connected /// App should display an interactive page for the user to chose which values to reveal case selectNamespaces([ItemsRequest]) + /// App should display the fact that a certain percentage of data has been sent + /// - Parameters: + /// - 0: The number of chunks sent to far + /// - 1: The total number of chunks to be sent + case uploadProgress(Int, Int) /// App should display a success message and offer to close the page case success } diff --git a/Sources/WalletSdk/MDocBLEUtils.swift b/Sources/WalletSdk/MDocBLEUtils.swift index f05103f..a1ea982 100644 --- a/Sources/WalletSdk/MDocBLEUtils.swift +++ b/Sources/WalletSdk/MDocBLEUtils.swift @@ -11,12 +11,20 @@ let readerServer2ClientCharacteristicId = CBUUID(string: "00000007-A123-48CE-896 let readerIdentCharacteristicId = CBUUID(string: "00000008-A123-48CE-896B-4C76973373E6") let readerL2CAPCharacteristicId = CBUUID(string: "0000000B-A123-48CE-896B-4C76973373E6") +enum MdocHolderBleError { + /// When discovery or communication with the peripheral fails + case peripheral(String) + /// When Bluetooth is unusable (e.g. unauthorized). + case bluetooth(CBCentralManager) +} + enum MDocBLECallback { case done case connected case message(Data) - case error(String) - case progress(String) + case error(MdocHolderBleError) + /// Chunks sent so far and total number of chunks to be sent + case uploadProgress(Int, Int) } protocol MDocBLEDelegate: AnyObject { diff --git a/Sources/WalletSdk/MDocHolderBLECentral.swift b/Sources/WalletSdk/MDocHolderBLECentral.swift index 500e15b..9f83d3a 100644 --- a/Sources/WalletSdk/MDocHolderBLECentral.swift +++ b/Sources/WalletSdk/MDocHolderBLECentral.swift @@ -84,13 +84,12 @@ class MDocHolderBLECentral: NSObject { chunk.reverse() chunk.append(firstByte) chunk.reverse() - let percentage = 100 * writingQueueChunkIndex / writingQueueTotalChunks - self.callback.callback(message: .progress("Sending chunks: \(percentage)%")) + self.callback.callback(message: .uploadProgress(writingQueueChunkIndex, writingQueueTotalChunks)) peripheral?.writeValue(_: chunk, for: writeCharacteristic!, type: CBCharacteristicWriteType.withoutResponse) } else { - self.callback.callback(message: .progress("Sending chunks: 100%")) + self.callback.callback(message: .uploadProgress(writingQueueTotalChunks, writingQueueTotalChunks)) writingQueue = nil } } @@ -210,48 +209,10 @@ class MDocHolderBLECentral: NSObject { extension MDocHolderBLECentral: CBCentralManagerDelegate { func centralManagerDidUpdateState(_ central: CBCentralManager) { - switch central.state { - case .poweredOff: - print("Is Powered Off.") - case .poweredOn: - print("Is Powered On.") + if central.state == .poweredOn { startScanning() - case .unsupported: - print("Is Unsupported.") - case .unauthorized: - if #available(iOS 13.1, *) { - switch CBManager.authorization { - case .denied: - print("Authorization denied") - case .restricted: - print("Authorization restricted") - case .allowedAlways: - print("Authorized") - case .notDetermined: - print("Authorization not determined") - @unknown default: - print("Unknown authorization error") - } - } else { - switch central.authorization { - case .denied: - print("Authorization denied") - case .restricted: - print("Authorization restricted") - case .allowedAlways: - print("Authorized") - case .notDetermined: - print("Authorization not determined") - @unknown default: - print("Unknown authorization error") - } - } - case .unknown: - print("Unknown") - case .resetting: - print("Resetting") - @unknown default: - print("Error") + } else { + self.callback.callback(message: .error(.bluetooth(central))) } } func centralManager(_ central: CBCentralManager, @@ -275,7 +236,7 @@ extension MDocHolderBLECentral: CBPeripheralDelegate { func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { if (error) != nil { self.callback.callback( - message: MDocBLECallback.error("Error discovering services: \(error!.localizedDescription)") + message: .error(.peripheral("Error discovering services: \(error!.localizedDescription)")) ) return } @@ -290,7 +251,7 @@ extension MDocHolderBLECentral: CBPeripheralDelegate { func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { if (error) != nil { self.callback.callback( - message: MDocBLECallback.error("Error discovering characteristics: \(error!.localizedDescription)") + message: .error(.peripheral("Error discovering characteristics: \(error!.localizedDescription)")) ) return } @@ -299,7 +260,7 @@ extension MDocHolderBLECentral: CBPeripheralDelegate { do { try self.processCharacteristics(peripheral: peripheral, characteristics: characteristics) } catch { - self.callback.callback(message: MDocBLECallback.error("\(error)")) + self.callback.callback(message: .error(.peripheral("\(error)"))) centralManager?.cancelPeripheralConnection(peripheral) } } @@ -310,7 +271,7 @@ extension MDocHolderBLECentral: CBPeripheralDelegate { print("Processing data") try self.processData(peripheral: peripheral, characteristic: characteristic) } catch { - self.callback.callback(message: MDocBLECallback.error("\(error)")) + self.callback.callback(message: .error(.peripheral("\(error)"))) centralManager?.cancelPeripheralConnection(peripheral) } }