diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d296b25a..042a1d6a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ ## Changelog +- **3.1.4**: + - Bugfix: Possible crash fixed when IVI was 1 and IV Index 0 (#358). + - Bugfix: Multiple `self` weakened (#356). + - Allowing groups to be null when importing (#348, #357). + - **3.1.3**: - Improvement: Migration to Xcode 12.5. * Improvement: CryptoSwift updated from 1.3.8 to 1.4.0 (https://github.com/krzyzanowskim/CryptoSwift/releases/tag/1.4.0). diff --git a/Documentation/SETTING_UP.md b/Documentation/SETTING_UP.md index 1929aa75f..b0b22fd6a 100644 --- a/Documentation/SETTING_UP.md +++ b/Documentation/SETTING_UP.md @@ -7,7 +7,7 @@ Using CocoaPods: You can use [Swift Package Manager](https://swift.org/package-manager/) and specify dependency in `Package.swift` by adding this: ```swift -.package(url: "https://github.com/NordicSemiconductor/IOS-nRF-Mesh-Library", .upToNextMinor(from: "3.1.2")) +.package(url: "https://github.com/NordicSemiconductor/IOS-nRF-Mesh-Library", .upToNextMinor(from: "3.1.4")) ``` Also, have a look at [Swift Package Manager @ CryptoSwift](https://github.com/krzyzanowskim/CryptoSwift/blob/master/README.md#swift-package-manager). diff --git a/Example/Podfile.lock b/Example/Podfile.lock index 30f0683d0..df93714f3 100755 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,6 +1,6 @@ PODS: - CryptoSwift (1.4.0) - - nRFMeshProvision (3.1.3): + - nRFMeshProvision (3.1.4): - CryptoSwift (= 1.4.0) DEPENDENCIES: @@ -16,7 +16,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: CryptoSwift: 7cc902df1784de3b389a387756c7d710f197730c - nRFMeshProvision: 7c95bc28df2ee8ba97ce1df73d04714b5abca1ca + nRFMeshProvision: 4b3e185fd13b077c997aadca6f54806f5c577cd7 PODFILE CHECKSUM: 69a81463322ef34ca0a20b98e90da2701d94e4ec diff --git a/Example/Pods/Local Podspecs/nRFMeshProvision.podspec.json b/Example/Pods/Local Podspecs/nRFMeshProvision.podspec.json index cdacc02f7..08a14c105 100755 --- a/Example/Pods/Local Podspecs/nRFMeshProvision.podspec.json +++ b/Example/Pods/Local Podspecs/nRFMeshProvision.podspec.json @@ -1,6 +1,6 @@ { "name": "nRFMeshProvision", - "version": "3.1.3", + "version": "3.1.4", "summary": "A Bluetooth Mesh library", "description": "nRF Mesh is a Bluetooth Mesh compliant library that has many features such as provisioning, configuration and control of Bluetooth Mesh compliant nodes.", "homepage": "https://github.com/NordicSemiconductor/IOS-nRF-Mesh-Library", @@ -13,7 +13,7 @@ }, "source": { "git": "https://github.com/NordicSemiconductor/IOS-nRF-Mesh-Library.git", - "tag": "3.1.3" + "tag": "3.1.4" }, "social_media_url": "https://twitter.com/nordictweets", "platforms": { diff --git a/Example/Pods/Manifest.lock b/Example/Pods/Manifest.lock index 30f0683d0..df93714f3 100755 --- a/Example/Pods/Manifest.lock +++ b/Example/Pods/Manifest.lock @@ -1,6 +1,6 @@ PODS: - CryptoSwift (1.4.0) - - nRFMeshProvision (3.1.3): + - nRFMeshProvision (3.1.4): - CryptoSwift (= 1.4.0) DEPENDENCIES: @@ -16,7 +16,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: CryptoSwift: 7cc902df1784de3b389a387756c7d710f197730c - nRFMeshProvision: 7c95bc28df2ee8ba97ce1df73d04714b5abca1ca + nRFMeshProvision: 4b3e185fd13b077c997aadca6f54806f5c577cd7 PODFILE CHECKSUM: 69a81463322ef34ca0a20b98e90da2701d94e4ec diff --git a/Example/Pods/Target Support Files/nRFMeshProvision/nRFMeshProvision-Info.plist b/Example/Pods/Target Support Files/nRFMeshProvision/nRFMeshProvision-Info.plist index 8b511bb39..36f2c7e22 100644 --- a/Example/Pods/Target Support Files/nRFMeshProvision/nRFMeshProvision-Info.plist +++ b/Example/Pods/Target Support Files/nRFMeshProvision/nRFMeshProvision-Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 3.1.3 + 3.1.4 CFBundleSignature ???? CFBundleVersion diff --git a/Example/nRFMeshProvision/Utils/UIViewController+Alert.swift b/Example/nRFMeshProvision/Utils/UIViewController+Alert.swift index 692f5afd5..4fe0a644d 100644 --- a/Example/nRFMeshProvision/Utils/UIViewController+Alert.swift +++ b/Example/nRFMeshProvision/Utils/UIViewController+Alert.swift @@ -63,7 +63,8 @@ extension UIViewController { // TODO: Should only iPad be handled differently? How about carPlay or Apple TV? let ipad = UIDevice.current.userInterfaceIdiom == .pad let style: UIAlertController.Style = ipad ? .alert : .actionSheet - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } let alert = UIAlertController(title: title, message: message, preferredStyle: style) alert.addAction(UIAlertAction(title: "Confirm", style: .destructive, handler: handler)) alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) @@ -84,7 +85,8 @@ extension UIViewController { func presentAlert(title: String?, message: String?, cancelable: Bool = false, option action: UIAlertAction? = nil, handler: ((UIAlertAction) -> Void)? = nil) { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: cancelable ? "Cancel" : "OK", style: .cancel, handler: handler)) if let action = action { @@ -107,7 +109,8 @@ extension UIViewController { func presentRangeAlert(title: String?, message: String?, range: RangeObject? = nil, type selector: Selector? = nil, handler: ((ClosedRange) -> Void)? = nil) { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } var alert: UIAlertController? = UIAlertController(title: title, message: message, preferredStyle: .alert) alert!.addTextField { textField in textField.text = range?.lowerBound.hex @@ -165,7 +168,8 @@ extension UIViewController { func presentTextAlert(title: String?, message: String?, text: String? = "", placeHolder: String? = "", type selector: Selector? = nil, option action: UIAlertAction? = nil, cancelHandler: (() -> Void)? = nil, handler: ((String) -> Void)? = nil) { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } var alert: UIAlertController? = UIAlertController(title: title, message: message, preferredStyle: .alert) alert!.addTextField { textField in textField.text = text @@ -219,7 +223,8 @@ extension UIViewController { /// - handler: The OK or Generate button handler. func presentKeyDialog(title: String?, message: String?, key: Data? = nil, handler: ((Data) -> Void)? = nil) { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } var alert: UIAlertController? = UIAlertController(title: title, message: message, preferredStyle: .alert) alert!.addTextField { textField in textField.text = key?.hex ?? Data.random128BitKey().hex diff --git a/Example/nRFMeshProvision/View Controllers/Groups/AddGroupViewController.swift b/Example/nRFMeshProvision/View Controllers/Groups/AddGroupViewController.swift index 889bb54d0..1e68e2bb5 100644 --- a/Example/nRFMeshProvision/View Controllers/Groups/AddGroupViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/Groups/AddGroupViewController.swift @@ -147,8 +147,8 @@ private extension AddGroupViewController { } presentTextAlert(title: "Group address", message: "Hexadecimal value in range\nC000 - FEFF.", text: address?.hex, placeHolder: "Address", type: .groupAddressRequired, - option: action, cancelHandler: nil) { text in - self.address = MeshAddress(hex: text) + option: action, cancelHandler: nil) { [weak self] text in + self?.address = MeshAddress(hex: text) } } diff --git a/Example/nRFMeshProvision/View Controllers/Groups/Model/SceneServerGroupCell.swift b/Example/nRFMeshProvision/View Controllers/Groups/Model/SceneServerGroupCell.swift index 8e3bfb92f..3097c8748 100644 --- a/Example/nRFMeshProvision/View Controllers/Groups/Model/SceneServerGroupCell.swift +++ b/Example/nRFMeshProvision/View Controllers/Groups/Model/SceneServerGroupCell.swift @@ -80,8 +80,8 @@ private extension SceneServerGroupCell { } let alert = UIAlertController(title: "Select scene", message: nil, preferredStyle: .actionSheet) scenes.forEach { scene in - alert.addAction(UIAlertAction(title: scene.name, style: .default) { _ in - self.sendSceneRecall(scene.number) + alert.addAction(UIAlertAction(title: scene.name, style: .default) { [weak self] _ in + self?.sendSceneRecall(scene.number) }) } alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) diff --git a/Example/nRFMeshProvision/View Controllers/Groups/Model/SceneSetupServerGroupCell.swift b/Example/nRFMeshProvision/View Controllers/Groups/Model/SceneSetupServerGroupCell.swift index ae23d2dd5..06f150787 100644 --- a/Example/nRFMeshProvision/View Controllers/Groups/Model/SceneSetupServerGroupCell.swift +++ b/Example/nRFMeshProvision/View Controllers/Groups/Model/SceneSetupServerGroupCell.swift @@ -78,8 +78,8 @@ private extension SceneSetupServerGroupCell { } let alert = UIAlertController(title: "Select scene", message: nil, preferredStyle: .actionSheet) network.scenes.forEach { scene in - alert.addAction(UIAlertAction(title: scene.name, style: .default) { _ in - self.sendSceneStore(scene.number) + alert.addAction(UIAlertAction(title: scene.name, style: .default) { [weak self] _ in + self?.sendSceneStore(scene.number) }) } alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) diff --git a/Example/nRFMeshProvision/View Controllers/Network/Configuration/ConfigurationViewController.swift b/Example/nRFMeshProvision/View Controllers/Network/Configuration/ConfigurationViewController.swift index 58a356664..4ff815425 100644 --- a/Example/nRFMeshProvision/View Controllers/Network/Configuration/ConfigurationViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/Network/Configuration/ConfigurationViewController.swift @@ -375,7 +375,7 @@ private extension ConfigurationViewController { let alert = UIAlertController(title: "Reset Node", message: "Resetting the node will change its state back to unprovisioned state and remove it from the local database.", preferredStyle: .actionSheet) - let resetAction = UIAlertAction(title: "Reset", style: .destructive) { _ in self.resetNode() } + let resetAction = UIAlertAction(title: "Reset", style: .destructive) { [weak self] _ in self?.resetNode() } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) alert.addAction(resetAction) alert.addAction(cancelAction) @@ -390,7 +390,7 @@ private extension ConfigurationViewController { let alert = UIAlertController(title: "Remove Node", message: "The node will only be removed from the local database. It will still be able to send and receive messages from the network. Remove the node only if the device is no longer available.", preferredStyle: .actionSheet) - let resetAction = UIAlertAction(title: "Remove", style: .destructive) { _ in self.removeNode() } + let resetAction = UIAlertAction(title: "Remove", style: .destructive) { [weak self] _ in self?.removeNode() } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) alert.addAction(resetAction) alert.addAction(cancelAction) diff --git a/Example/nRFMeshProvision/View Controllers/Network/Configuration/NodeScenesViewController.swift b/Example/nRFMeshProvision/View Controllers/Network/Configuration/NodeScenesViewController.swift index 379fdf2c1..e306f96f7 100644 --- a/Example/nRFMeshProvision/View Controllers/Network/Configuration/NodeScenesViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/Network/Configuration/NodeScenesViewController.swift @@ -367,8 +367,8 @@ private extension NodeScenesViewController { return false } guard isSceneClientReady else { - let action = UIAlertAction(title: "Fix", style: .default) { [unowned self] action in - self.fixClient() + let action = UIAlertAction(title: "Fix", style: .default) { [weak self] action in + self?.fixClient() } presentAlert(title: "Client not ready", message: "Scene Client model on local node is not bound to any of the " @@ -390,8 +390,8 @@ private extension NodeScenesViewController { return false } guard isSceneClientReadyToSetup else { - let action = UIAlertAction(title: "Fix", style: .default) { [unowned self] action in - self.fixClient() + let action = UIAlertAction(title: "Fix", style: .default) { [weak self] action in + self?.fixClient() } presentAlert(title: "Client not ready", message: "Scene Client model on local node is not bound to any of the " diff --git a/Example/nRFMeshProvision/View Controllers/Network/Configuration/SetPublicationViewController.swift b/Example/nRFMeshProvision/View Controllers/Network/Configuration/SetPublicationViewController.swift index f0c39a8bc..84b01a4a2 100644 --- a/Example/nRFMeshProvision/View Controllers/Network/Configuration/SetPublicationViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/Network/Configuration/SetPublicationViewController.swift @@ -189,12 +189,13 @@ private extension SetPublicationViewController { message: "TTL = Time To Live\n\nTTL limits the number of times a message can be relayed.\nMax value is 127. Message with TTL 0 will not be relayed.", text: "5", placeHolder: "Default is 5", type: .ttlRequired, - option: UIAlertAction(title: "Use Node's default", style: .default, handler: { _ in + option: UIAlertAction(title: "Use Node's default", style: .default) { _ in self.ttl = 0xFF - }), + }, handler: { value in self.ttl = UInt8(value)! - }) + } + ) } func periodSelected(_ period: Float) { diff --git a/Example/nRFMeshProvision/View Controllers/Network/Provisioning/OobSelector.swift b/Example/nRFMeshProvision/View Controllers/Network/Provisioning/OobSelector.swift index 50d1d1374..6ddeb463a 100644 --- a/Example/nRFMeshProvision/View Controllers/Network/Provisioning/OobSelector.swift +++ b/Example/nRFMeshProvision/View Controllers/Network/Provisioning/OobSelector.swift @@ -60,23 +60,23 @@ extension OobSelector where Self: UIViewController { } let alert = UIAlertController(title: "Select OOB Type", message: nil, preferredStyle: .actionSheet) - alert.addAction(UIAlertAction(title: "No OOB", style: .destructive, handler: { _ in + alert.addAction(UIAlertAction(title: "No OOB", style: .destructive) { _ in callback(.noOob) - })) + }) if capabilities.staticOobType.contains(.staticOobInformationAvailable) { - alert.addAction(UIAlertAction(title: "Static OOB", style: .default, handler: { _ in + alert.addAction(UIAlertAction(title: "Static OOB", style: .default) { _ in callback(.staticOob) - })) + }) } if !capabilities.outputOobActions.isEmpty { - alert.addAction(UIAlertAction(title: "Output OOB", style: .default, handler: { _ in + alert.addAction(UIAlertAction(title: "Output OOB", style: .default) { _ in self.presentOutputOobOptionsDialog(for: provisioningManager, from: item, callback: callback) - })) + }) } if !capabilities.inputOobActions.isEmpty { - alert.addAction(UIAlertAction(title: "Input OOB", style: .default, handler: { _ in + alert.addAction(UIAlertAction(title: "Input OOB", style: .default) { _ in self.presentInputOobOptionsDialog(for: provisioningManager, from: item, callback: callback) - })) + }) } alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) alert.popoverPresentationController?.barButtonItem = item diff --git a/Example/nRFMeshProvision/View Controllers/Network/Provisioning/ProvisioningViewController.swift b/Example/nRFMeshProvision/View Controllers/Network/Provisioning/ProvisioningViewController.swift index 665f016a7..7b5d5bf66 100644 --- a/Example/nRFMeshProvision/View Controllers/Network/Provisioning/ProvisioningViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/Network/Provisioning/ProvisioningViewController.swift @@ -159,7 +159,8 @@ private extension ProvisioningViewController { /// Presents a dialog to edit or unbind the Provisioner Unicast Address. func presentUnicastAddressDialog() { let manager = self.provisioningManager! - let action = UIAlertAction(title: "Automatic", style: .default) { _ in + let action = UIAlertAction(title: "Automatic", style: .default) { [weak self] _ in + guard let self = self else { return } manager.unicastAddress = manager.suggestedUnicastAddress self.unicastAddressLabel.text = manager.unicastAddress?.asString() ?? "Automatic" let deviceSupported = manager.isDeviceSupported == true @@ -168,7 +169,8 @@ private extension ProvisioningViewController { } presentTextAlert(title: "Unicast address", message: "Hexadecimal value in Provisioner's range.", text: manager.unicastAddress?.hex, placeHolder: "Address", type: .unicastAddressRequired, - option: action, cancelHandler: nil) { text in + option: action, cancelHandler: nil) { [weak self] text in + guard let self = self else { return } manager.unicastAddress = Address(text, radix: 16) self.unicastAddressLabel.text = manager.unicastAddress!.asString() let deviceSupported = manager.isDeviceSupported == true @@ -181,7 +183,8 @@ private extension ProvisioningViewController { } func presentStatusDialog(message: String, animated flag: Bool = true, completion: (() -> Void)? = nil) { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } if let alert = self.alert { alert.message = message completion?() @@ -197,7 +200,8 @@ private extension ProvisioningViewController { } func dismissStatusDialog(completion: (() -> Void)? = nil) { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } if let alert = self.alert { alert.dismiss(animated: true, completion: completion) } else { @@ -208,7 +212,8 @@ private extension ProvisioningViewController { } func abort() { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } self.alert?.title = "Aborting" self.alert?.message = "Cancelling connection..." self.bearer.close() @@ -248,7 +253,8 @@ private extension ProvisioningViewController { let outputOobNotSupported = capabilities.outputOobActions.isEmpty let inputOobNotSupported = capabilities.inputOobActions.isEmpty guard (staticOobNotSupported && outputOobNotSupported && inputOobNotSupported) || authenticationMethod != nil else { - presentOobOptionsDialog(for: provisioningManager, from: provisionButton) { method in + presentOobOptionsDialog(for: provisioningManager, from: provisionButton) { [weak self] method in + guard let self = self else { return } self.authenticationMethod = method self.startProvisioning() } @@ -291,7 +297,8 @@ extension ProvisioningViewController: GattBearerDelegate { } func bearerDidOpen(_ bearer: Bearer) { - presentStatusDialog(message: "Identifying...") { + presentStatusDialog(message: "Identifying...") { [weak self] in + guard let self = self else { return } do { try self.provisioningManager!.identify(andAttractFor: ProvisioningViewController.attentionTimer) } catch { @@ -303,15 +310,17 @@ extension ProvisioningViewController: GattBearerDelegate { func bearer(_ bearer: Bearer, didClose error: Error?) { guard case .complete = provisioningManager.state else { - dismissStatusDialog { - self.presentAlert(title: "Status", message: "Device disconnected.") + dismissStatusDialog { [weak self] in + self?.presentAlert(title: "Status", message: "Device disconnected.") } return } dismissStatusDialog { - self.presentAlert(title: "Success", message: "Provisioning complete.") { _ in + self.presentAlert(title: "Success", message: "Provisioning complete.") { [weak self] _ in + guard let self = self else { return } if MeshNetworkManager.instance.save() { - self.dismiss(animated: true) { + self.dismiss(animated: true) { [weak self] in + guard let self = self else { return } let network = MeshNetworkManager.instance.meshNetwork! if let node = network.node(for: self.unprovisionedDevice) { self.delegate?.provisionerDidProvisionNewDevice(node) @@ -329,7 +338,8 @@ extension ProvisioningViewController: GattBearerDelegate { extension ProvisioningViewController: ProvisioningDelegate { func provisioningState(of unprovisionedDevice: UnprovisionedDevice, didChangeTo state: ProvisioningState) { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } switch state { case .requestingCapabilities: diff --git a/Example/nRFMeshProvision/View Controllers/Network/Provisioning/ScannerTableViewController.swift b/Example/nRFMeshProvision/View Controllers/Network/Provisioning/ScannerTableViewController.swift index 26a2a5a72..61e182eb3 100644 --- a/Example/nRFMeshProvision/View Controllers/Network/Provisioning/ScannerTableViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/Network/Provisioning/ScannerTableViewController.swift @@ -116,7 +116,7 @@ class ScannerTableViewController: UITableViewController { selectedDevice = discoveredPeripherals[indexPath.row].device alert = UIAlertController(title: "Status", message: "Connecting...", preferredStyle: .alert) - alert!.addAction(UIAlertAction(title: "Cancel", style: .cancel) { [weak self, bearer] action in + alert!.addAction(UIAlertAction(title: "Cancel", style: .cancel) { [weak self] action in action.isEnabled = false self?.alert?.title = "Aborting" self?.alert?.message = "Cancelling connection..." diff --git a/Example/nRFMeshProvision/View Controllers/ProgressCollectionViewController.swift b/Example/nRFMeshProvision/View Controllers/ProgressCollectionViewController.swift index 0d21aefac..b8f02158f 100644 --- a/Example/nRFMeshProvision/View Controllers/ProgressCollectionViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/ProgressCollectionViewController.swift @@ -57,7 +57,8 @@ class ProgressCollectionViewController: UICollectionViewController { /// - parameter message: Message to be displayed to the user. /// - parameter completion: A completion handler. func start(_ message: String, completion: @escaping () throws -> MessageHandle?) { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } do { self.messageHandle = try completion() guard let _ = self.messageHandle else { @@ -71,9 +72,9 @@ class ProgressCollectionViewController: UICollectionViewController { self.alert = UIAlertController(title: "Status", message: message, preferredStyle: .alert) - self.alert!.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in - self.messageHandle?.cancel() - self.alert = nil + self.alert!.addAction(UIAlertAction(title: "Cancel", style: .cancel) { [weak self] _ in + self?.messageHandle?.cancel() + self?.alert = nil }) self.present(self.alert!, animated: true) } @@ -83,8 +84,8 @@ class ProgressCollectionViewController: UICollectionViewController { self.alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert) - self.alert!.addAction(UIAlertAction(title: "OK", style: .cancel) { _ in - self.alert = nil + self.alert!.addAction(UIAlertAction(title: "OK", style: .cancel) { [weak self] _ in + self?.alert = nil }) self.present(self.alert!, animated: true) } diff --git a/Example/nRFMeshProvision/View Controllers/ProgressViewController.swift b/Example/nRFMeshProvision/View Controllers/ProgressViewController.swift index 329df4c41..af93f378c 100644 --- a/Example/nRFMeshProvision/View Controllers/ProgressViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/ProgressViewController.swift @@ -57,14 +57,15 @@ class ProgressViewController: UITableViewController { /// - parameter message: Message to be displayed to the user. /// - parameter completion: A completion handler. func start(_ message: String, completion: @escaping () -> Void) { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } if let alert = self.alert { alert.message = message } else { self.alert = UIAlertController(title: "Status", message: message, preferredStyle: .alert) - self.alert!.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { - _ in self.alert = nil - })) + self.alert!.addAction(UIAlertAction(title: "Cancel", style: .cancel) { + [weak self] _ in self?.alert = nil + }) self.present(self.alert!, animated: true) } @@ -78,7 +79,8 @@ class ProgressViewController: UITableViewController { /// - parameter message: Message to be displayed to the user. /// - parameter completion: A completion handler. func start(_ message: String, completion: @escaping () throws -> MessageHandle?) { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } do { self.messageHandle = try completion() guard let _ = self.messageHandle else { @@ -92,10 +94,10 @@ class ProgressViewController: UITableViewController { self.alert = UIAlertController(title: "Status", message: message, preferredStyle: .alert) - self.alert!.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in - self.messageHandle?.cancel() - self.alert = nil - self.refreshControl?.endRefreshing() + self.alert!.addAction(UIAlertAction(title: "Cancel", style: .cancel) { [weak self] _ in + self?.messageHandle?.cancel() + self?.alert = nil + self?.refreshControl?.endRefreshing() }) self.present(self.alert!, animated: true) } @@ -113,8 +115,8 @@ class ProgressViewController: UITableViewController { self.alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert) - self.alert!.addAction(UIAlertAction(title: "OK", style: .cancel) { _ in - self.alert = nil + self.alert!.addAction(UIAlertAction(title: "OK", style: .cancel) { [weak self] _ in + self?.alert = nil }) self.present(self.alert!, animated: true) } diff --git a/Example/nRFMeshProvision/View Controllers/Proxy/ProxySelectorViewController.swift b/Example/nRFMeshProvision/View Controllers/Proxy/ProxySelectorViewController.swift index 70efdf7ed..6d104b168 100644 --- a/Example/nRFMeshProvision/View Controllers/Proxy/ProxySelectorViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/Proxy/ProxySelectorViewController.swift @@ -103,10 +103,10 @@ class ProxySelectorViewController: UITableViewController { selectedDevice = bearer alert = UIAlertController(title: "Status", message: "Connecting...", preferredStyle: .alert) - alert!.addAction(UIAlertAction(title: "Cancel", style: .cancel) { action in + alert!.addAction(UIAlertAction(title: "Cancel", style: .cancel) { [weak self] action in action.isEnabled = false - self.alert!.title = "Aborting" - self.alert!.message = "Cancelling connection..." + self?.alert?.title = "Aborting" + self?.alert?.message = "Cancelling connection..." bearer.close() }) present(alert!, animated: true) { @@ -176,29 +176,30 @@ extension ProxySelectorViewController: CBCentralManagerDelegate { extension ProxySelectorViewController: GattBearerDelegate { func bearerDidConnect(_ bearer: Bearer) { - DispatchQueue.main.async { - self.alert?.message = "Discovering services..." + DispatchQueue.main.async { [weak self] in + self?.alert?.message = "Discovering services..." } } func bearerDidDiscoverServices(_ bearer: Bearer) { - DispatchQueue.main.async { - self.alert?.message = "Initializing..." + DispatchQueue.main.async { [weak self] in + self?.alert?.message = "Initializing..." } } func bearerDidOpen(_ bearer: Bearer) { MeshNetworkManager.bearer.use(proxy: bearer as! GattBearer) - DispatchQueue.main.async { - self.alert?.dismiss(animated: true) { - self.dismiss(animated: true) + DispatchQueue.main.async { [weak self] in + self?.alert?.dismiss(animated: true) { [weak self] in + self?.dismiss(animated: true) } - self.alert = nil + self?.alert = nil } } func bearer(_ bearer: Bearer, didClose error: Error?) { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } self.alert?.message = "Device disconnected" self.alert?.dismiss(animated: true) self.alert = nil diff --git a/Example/nRFMeshProvision/View Controllers/Settings/AppKeysViewController.swift b/Example/nRFMeshProvision/View Controllers/Settings/AppKeysViewController.swift index 5adfc7201..cbd45088f 100644 --- a/Example/nRFMeshProvision/View Controllers/Settings/AppKeysViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/Settings/AppKeysViewController.swift @@ -41,9 +41,10 @@ class AppKeysViewController: UITableViewController, Editable { } else { presentAlert(title: "Error", message: "No Network Key found.\n\nCreate a Network Key prior to creating an Application Key.", - option: UIAlertAction(title: "Create", style: .default, handler: { action in - self.performSegue(withIdentifier: "networkKeys", sender: nil) - })) + option: UIAlertAction(title: "Create", style: .default) { [weak self] action in + self?.performSegue(withIdentifier: "networkKeys", sender: nil) + } + ) } } @@ -55,7 +56,8 @@ class AppKeysViewController: UITableViewController, Editable { self.presentTextAlert(title: "Generate keys", message: "Specify number of application keys to generate (max 5):", placeHolder: "E.g. 3", type: .numberRequired, - cancelHandler: nil) { value in + cancelHandler: nil) { [weak self] value in + guard let self = self else { return } guard let network = MeshNetworkManager.instance.meshNetwork, let number = Int(value), number > 0 else { return diff --git a/Example/nRFMeshProvision/View Controllers/Settings/ExportViewController.swift b/Example/nRFMeshProvision/View Controllers/Settings/ExportViewController.swift index 9bb07defc..54cc09a59 100644 --- a/Example/nRFMeshProvision/View Controllers/Settings/ExportViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/Settings/ExportViewController.swift @@ -265,7 +265,8 @@ private extension ExportViewController { func exportNetwork(using exportConfiguration: ExportConfiguration) { let manager = MeshNetworkManager.instance - DispatchQueue.global(qos: .userInitiated).async { + DispatchQueue.global(qos: .userInitiated).async { [weak self] in + guard let self = self else { return } let data = manager.export(exportConfiguration) do { @@ -273,10 +274,12 @@ private extension ExportViewController { let fileURL = FileManager.default.temporaryDirectory.appendingPathComponent("\(name).json") try data.write(to: fileURL) - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } let controller = UIActivityViewController(activityItems: [fileURL], applicationActivities: nil) controller.popoverPresentationController?.barButtonItem = self.doneButton - controller.completionWithItemsHandler = { type, success, items, error in + controller.completionWithItemsHandler = { [weak self] type, success, items, error in + guard let self = self else { return } if success { self.dismiss(animated: true) } else { @@ -292,10 +295,10 @@ private extension ExportViewController { } } catch { print("Export failed: \(error)") - DispatchQueue.main.async { - self.presentAlert(title: "Error", - message: "Exporting Mesh Network configuration failed " - + "with error \(error.localizedDescription).") + DispatchQueue.main.async { [weak self] in + self?.presentAlert(title: "Error", + message: "Exporting Mesh Network configuration failed " + + "with error \(error.localizedDescription).") } } } diff --git a/Example/nRFMeshProvision/View Controllers/Settings/Provisioners/EditProvisionerViewController.swift b/Example/nRFMeshProvision/View Controllers/Settings/Provisioners/EditProvisionerViewController.swift index 6a6215fd7..d1797c216 100644 --- a/Example/nRFMeshProvision/View Controllers/Settings/Provisioners/EditProvisionerViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/Settings/Provisioners/EditProvisionerViewController.swift @@ -233,7 +233,8 @@ private extension EditProvisionerViewController { let nodeAssigned = newAddress != nil || (node != nil && !disableConfigCapabilities) let action = !nodeAssigned ? nil : UIAlertAction(title: "Unassign", style: .destructive) { action in self.confirm(title: "Disable configuration capabilities", - message: "A Provisioner without the unicast address assigned is not able to perform configuration operations.") { _ in + message: "A Provisioner without the unicast address assigned is not able to perform configuration operations.") { [weak self] _ in + guard let self = self else { return } self.disableConfigCapabilities = true self.newAddress = nil self.unicastAddressLabel.text = "Not assigned" @@ -246,7 +247,8 @@ private extension EditProvisionerViewController { } presentTextAlert(title: "Unicast address", message: "Hexadecimal value in range\n0001 - 7FFF.", text: address, placeHolder: "Address", type: .unicastAddressRequired, - option: action, cancelHandler: nil) { text in + option: action, cancelHandler: nil) { [weak self] text in + guard let self = self else { return } let address = Address(text, radix: 16) self.unicastAddressLabel.text = address!.asString() self.disableConfigCapabilities = false @@ -272,7 +274,8 @@ private extension EditProvisionerViewController { presentTextAlert(title: "Default TTL", message: "TTL = Time To Live\n\nTTL limits the number of times a message can be relayed.\nMax value is 127.", text: "\(node?.defaultTTL ?? 5)", placeHolder: "Default is 5", - type: .ttlRequired, cancelHandler: nil) { value in + type: .ttlRequired, cancelHandler: nil) { [weak self] value in + guard let self = self else { return } let ttl = UInt8(value)! self.newTtl = ttl self.ttlCell.detailTextLabel?.text = "\(ttl)" @@ -361,9 +364,9 @@ private extension EditProvisionerViewController { let nextText = next.map { " Next available address is \($0.asString())."} ?? " No available addresses. Extend the unicast address range to assign a new one." let autoAssign = next.map { nextAddress in - UIAlertAction(title: "Assign", style: .default) { _ in - self.newAddress = nextAddress - self.unicastAddressLabel.text = nextAddress.asString() + UIAlertAction(title: "Assign", style: .default) { [weak self] _ in + self?.newAddress = nextAddress + self?.unicastAddressLabel.text = nextAddress.asString() } } presentAlert(title: "Error", @@ -377,9 +380,9 @@ private extension EditProvisionerViewController { let nextText = next.map { " Next available address is \($0.asString())."} ?? " No available addresses. Extend the unicast address range to assign a new one." let autoAssign = next.map { nextAddress in - UIAlertAction(title: "Assign", style: .default) { _ in - self.newAddress = nextAddress - self.unicastAddressLabel.text = nextAddress.asString() + UIAlertAction(title: "Assign", style: .default) { [weak self] _ in + self?.newAddress = nextAddress + self?.unicastAddressLabel.text = nextAddress.asString() } } presentAlert(title: "Error", message: "The address range \(address.asString())...\((address + UInt16(count) - 1).hex) is already in use or is reserved. A unique unicast address must be assigned to each of the \(count) elements.\(nextText)", option: autoAssign) diff --git a/Example/nRFMeshProvision/View Controllers/Settings/SettingsViewController.swift b/Example/nRFMeshProvision/View Controllers/Settings/SettingsViewController.swift index 0eb87ee24..0ca631451 100644 --- a/Example/nRFMeshProvision/View Controllers/Settings/SettingsViewController.swift +++ b/Example/nRFMeshProvision/View Controllers/Settings/SettingsViewController.swift @@ -148,7 +148,7 @@ private extension SettingsViewController { message: "Resetting the network will erase all network data.\n" + "Make sure you exported it first.", preferredStyle: .actionSheet) - let resetAction = UIAlertAction(title: "Reset", style: .destructive) { _ in self.resetNetwork() } + let resetAction = UIAlertAction(title: "Reset", style: .destructive) { [weak self] _ in self?.resetNetwork() } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) alert.addAction(resetAction) alert.addAction(cancelAction) @@ -162,8 +162,8 @@ private extension SettingsViewController { message: "Importing network will override your existing settings.\n" + "Make sure you exported it first.", preferredStyle: .actionSheet) - let exportAction = UIAlertAction(title: "Export", style: .default) { _ in self.exportNetwork() } - let importAction = UIAlertAction(title: "Import", style: .destructive) { _ in self.importNetwork() } + let exportAction = UIAlertAction(title: "Export", style: .default) { [weak self] _ in self?.exportNetwork() } + let importAction = UIAlertAction(title: "Import", style: .destructive) { [weak self] _ in self?.importNetwork() } let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) alert.addAction(exportAction) alert.addAction(importAction) @@ -223,7 +223,8 @@ private extension SettingsViewController { /// Saves mesh network configuration and reloads network data on success. func saveAndReload() { if MeshNetworkManager.instance.save() { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } (UIApplication.shared.delegate as! AppDelegate).meshNetworkDidChange() self.reload() self.presentAlert(title: "Success", message: "Mesh Network configuration imported.") @@ -241,7 +242,8 @@ extension SettingsViewController: UIDocumentPickerDelegate { func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL) { let manager = MeshNetworkManager.instance - DispatchQueue.global(qos: .userInitiated).async { + DispatchQueue.global(qos: .userInitiated).async { [weak self] in + guard let self = self else { return } do { let data = try Data(contentsOf: url) let meshNetwork = try manager.import(from: data) @@ -250,17 +252,18 @@ extension SettingsViewController: UIDocumentPickerDelegate { // If it's a new network and has only one Provisioner, just save it. // Otherwise, give the user option to select one. if meshNetwork.provisioners.count > 1 { - DispatchQueue.main.async { + DispatchQueue.main.async { [weak self] in + guard let self = self else { return } let alert = UIAlertController(title: "Select Provisioner", message: "Select Provisioner instance to be used on this device:", preferredStyle: .actionSheet) alert.popoverPresentationController?.barButtonItem = self.organizeButton for provisioner in meshNetwork.provisioners { - alert.addAction(UIAlertAction(title: provisioner.name, style: .default) { action in + alert.addAction(UIAlertAction(title: provisioner.name, style: .default) { [weak self] action in // This will effectively set the Provisioner to be used // be the library. Provisioner from index 0 is the local one. meshNetwork.moveProvisioner(provisioner, toIndex: 0) - self.saveAndReload() + self?.saveAndReload() }) } self.present(alert, animated: true) @@ -272,41 +275,41 @@ extension SettingsViewController: UIDocumentPickerDelegate { } catch let DecodingError.dataCorrupted(context) { let path = context.codingPath.path print("Import failed: \(context.debugDescription) (\(path))") - DispatchQueue.main.async { - self.presentAlert(title: "Error", - message: "Importing Mesh Network configuration failed.\n" - + "\(context.debugDescription)\nPath: \(path).") + DispatchQueue.main.async { [weak self] in + self?.presentAlert(title: "Error", + message: "Importing Mesh Network configuration failed.\n" + + "\(context.debugDescription)\nPath: \(path).") } } catch let DecodingError.keyNotFound(key, context) { let path = context.codingPath.path print("Import failed: Key \(key) not found in \(path)") - DispatchQueue.main.async { - self.presentAlert(title: "Error", - message: "Importing Mesh Network configuration failed.\n" - + "No value associated with key: \(key.stringValue) in: \(path).") + DispatchQueue.main.async { [weak self] in + self?.presentAlert(title: "Error", + message: "Importing Mesh Network configuration failed.\n" + + "No value associated with key: \(key.stringValue) in: \(path).") } } catch let DecodingError.valueNotFound(value, context) { let path = context.codingPath.path print("Import failed: Value of type \(value) required in \(path)") - DispatchQueue.main.async { - self.presentAlert(title: "Error", - message: "Importing Mesh Network configuration failed.\n" - + "No value associated with key: \(path).") + DispatchQueue.main.async { [weak self] in + self?.presentAlert(title: "Error", + message: "Importing Mesh Network configuration failed.\n" + + "No value associated with key: \(path).") } } catch let DecodingError.typeMismatch(type, context) { let path = context.codingPath.path print("Import failed: Type mismatch in \(path) (\(type) was required)") - DispatchQueue.main.async { - self.presentAlert(title: "Error", - message: "Importing Mesh Network configuration failed.\n" - + "Type mismatch in: \(path). Expected: \(type).") + DispatchQueue.main.async { [weak self] in + self?.presentAlert(title: "Error", + message: "Importing Mesh Network configuration failed.\n" + + "Type mismatch in: \(path). Expected: \(type).") } } catch { print("Import failed: \(error)") - DispatchQueue.main.async { - self.presentAlert(title: "Error", - message: "Importing Mesh Network configuration failed.\n" - + "Check if the file is valid.") + DispatchQueue.main.async { [weak self] in + self?.presentAlert(title: "Error", + message: "Importing Mesh Network configuration failed.\n" + + "Check if the file is valid.") } } } diff --git a/nRFMeshProvision.podspec b/nRFMeshProvision.podspec index b9c75b655..528821104 100755 --- a/nRFMeshProvision.podspec +++ b/nRFMeshProvision.podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |s| s.name = 'nRFMeshProvision' - s.version = '3.1.3' + s.version = '3.1.4' s.summary = 'A Bluetooth Mesh library' s.description = <<-DESC nRF Mesh is a Bluetooth Mesh compliant library that has many features such as provisioning, configuration and control of Bluetooth Mesh compliant nodes. @@ -26,7 +26,4 @@ Pod::Spec.new do |s| s.source_files = 'nRFMeshProvision/Classes/**/*' s.dependency 'CryptoSwift', '= 1.4.0' s.frameworks = 'CoreBluetooth' - # Regarding the lines below see: https://stackoverflow.com/a/63955114/2115352 - # s.pod_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' } - # s.user_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' } end diff --git a/nRFMeshProvision/Classes/Mesh Model/IvIndex.swift b/nRFMeshProvision/Classes/Mesh Model/IvIndex.swift index 63f19fea6..fbae19568 100644 --- a/nRFMeshProvision/Classes/Mesh Model/IvIndex.swift +++ b/nRFMeshProvision/Classes/Mesh Model/IvIndex.swift @@ -58,7 +58,7 @@ internal struct IvIndex { /// - parameter ivi: The IVI bit of the received Network PDU. /// - returns: The IV Index to be used to decrypt the message. func index(for ivi: UInt8) -> UInt32 { - return ivi == index & 1 ? index : index - 1 + return ivi == index & 1 ? index : max(1, index) - 1 } } diff --git a/nRFMeshProvision/Classes/Mesh Model/MeshNetwork.swift b/nRFMeshProvision/Classes/Mesh Model/MeshNetwork.swift index ec09eeb61..740b210b3 100644 --- a/nRFMeshProvision/Classes/Mesh Model/MeshNetwork.swift +++ b/nRFMeshProvision/Classes/Mesh Model/MeshNetwork.swift @@ -357,7 +357,9 @@ public class MeshNetwork: Codable { debugDescription: "Device Key cannot be empty in non-partial configuration.") } nodes = ns - groups = try container.decode([Group].self, forKey: .groups) + // Groups are mandatory, but one of the Android versions didn't export empty + // list to JSON, so it may happen that Groups are `nil`. + groups = try container.decodeIfPresent([Group].self, forKey: .groups) ?? [] networkExclusions = try container.decodeIfPresent([ExclusionList].self, forKey: .networkExclusions) // Scenes are mandatory, but previous version of the library did support it, // so JSON files generated with such versions won't have "scenes" tag.