diff --git a/iOSDFULibrary/Classes/Implementation/GenericDFU/DFUExecutor.swift b/iOSDFULibrary/Classes/Implementation/GenericDFU/DFUExecutor.swift
index 322b0d25..2e7b53d4 100644
--- a/iOSDFULibrary/Classes/Implementation/GenericDFU/DFUExecutor.swift
+++ b/iOSDFULibrary/Classes/Implementation/GenericDFU/DFUExecutor.swift
@@ -117,7 +117,9 @@ extension BaseDFUExecutor {
func error(_ error: DFUError, didOccurWithMessage message: String) {
if error == .bluetoothDisabled {
- delegate{ $0.dfuError(.bluetoothDisabled, didOccurWithMessage: message) }
+ delegate {
+ $0.dfuError(.bluetoothDisabled, didOccurWithMessage: message)
+ }
// Release the cyclic reference.
peripheral.destroy()
return
diff --git a/iOSDFULibrary/Classes/Implementation/GenericDFU/DFUPeripheral.swift b/iOSDFULibrary/Classes/Implementation/GenericDFU/DFUPeripheral.swift
index 6b6cb25a..bbc7e83a 100644
--- a/iOSDFULibrary/Classes/Implementation/GenericDFU/DFUPeripheral.swift
+++ b/iOSDFULibrary/Classes/Implementation/GenericDFU/DFUPeripheral.swift
@@ -101,8 +101,8 @@ internal class BaseDFUPeripheral
: NSObject, BaseDF
internal let experimentalButtonlessServiceInSecureDfuEnabled: Bool
/// Default error callback.
internal var defaultErrorCallback: ErrorCallback {
- return { (error, message) in
- self.delegate?.error(error, didOccurWithMessage: message)
+ return { [weak self] error, message in
+ self?.delegate?.error(error, didOccurWithMessage: message)
}
}
@@ -155,23 +155,23 @@ internal class BaseDFUPeripheral | : NSObject, BaseDF
}
self.peripheral = peripheral
- if peripheral.state != .connected {
- connect()
- } else {
+ switch peripheral.state {
+ case .connected:
let name = peripheral.name ?? "Unknown device"
logger.i("Connected to \(name)")
- let dfuService = findDfuService(in: peripheral.services)
- if dfuService == nil {
+ if let dfuService = findDfuService(in: peripheral.services) {
+ // A DFU service was found, congratulations!
+ logger.i("Services discovered")
+ peripheralDidDiscoverDfuService(dfuService)
+ } else {
// DFU service has not been found, but it doesn't matter it's not
// there. Perhaps the user's application didn't discover it.
// Let's discover DFU services.
discoverServices()
- } else {
- // A DFU service was found, congratulations!
- logger.i("Services discovered")
- peripheralDidDiscoverDfuService(dfuService!)
}
+ default:
+ connect()
}
}
@@ -181,13 +181,14 @@ internal class BaseDFUPeripheral | : NSObject, BaseDF
}
func disconnect() {
- if peripheral!.state == .connected {
+ guard let peripheral = peripheral else { return }
+ if peripheral.state == .connected {
logger.v("Disconnecting...")
} else {
logger.v("Cancelling connection...")
}
logger.d("centralManager.cancelPeripheralConnection(peripheral)")
- centralManager.cancelPeripheralConnection(peripheral!)
+ centralManager.cancelPeripheralConnection(peripheral)
}
func destroy() {
@@ -239,10 +240,11 @@ internal class BaseDFUPeripheral | : NSObject, BaseDF
stateAsString = "Unknown"
}
logger.d("[Callback] Central Manager did update state to: \(stateAsString)")
- if central.state == .poweredOn {
+ switch central.state {
+ case .poweredOn:
// We are now ready to rumble!
start()
- } else {
+ default:
// The device has been already disconnected if it was connected.
delegate?.error(.bluetoothDisabled, didOccurWithMessage: "Bluetooth adapter powered off")
destroy()
@@ -339,11 +341,10 @@ internal class BaseDFUPeripheral | : NSObject, BaseDF
// MARK: - Peripheral Delegate methods
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
- guard error == nil else {
+ if let error = error {
logger.e("Services discovery failed")
- logger.e(error!)
- delegate?.error(.serviceDiscoveryFailed, didOccurWithMessage:
- "Services discovery failed")
+ logger.e(error)
+ delegate?.error(.serviceDiscoveryFailed, didOccurWithMessage: "Services discovery failed")
return
}
@@ -370,8 +371,7 @@ internal class BaseDFUPeripheral | : NSObject, BaseDF
logger.d("Did you connect to the correct target? It might be that the previous services were cached: toggle Bluetooth from iOS settings to clear cache. Also, ensure the device contains the Service Changed characteristic")
// The device does not support DFU, nor buttonless jump
- delegate?.error(.deviceNotSupported, didOccurWithMessage:
- "DFU Service not found")
+ delegate?.error(.deviceNotSupported, didOccurWithMessage: "DFU Service not found")
return
}
// A DFU service was found, congratulations!
@@ -409,7 +409,7 @@ internal class BaseDFUPeripheral | : NSObject, BaseDF
This method should reset the device, preferably switching it to application mode.
*/
func resetDevice() {
- if peripheral != nil && peripheral!.state != .disconnected {
+ if let peripheral = peripheral, peripheral.state != .disconnected {
disconnect()
} else {
peripheralDidDisconnect()
@@ -424,27 +424,17 @@ internal class BaseDFUPeripheral | : NSObject, BaseDF
- returns: A `DFUService` type if a DFU service has been found, or `nil` if
services are `nil` or the list does not contain any supported DFU Service.
*/
- private func findDfuService(in services:[CBService]?) -> CBService? {
- if let services = services {
- for service in services {
- // Skip the experimental Buttonless DFU Service if this feature wasn't enabled.
- if experimentalButtonlessServiceInSecureDfuEnabled && service.matches(uuid: uuidHelper.buttonlessExperimentalService) {
- // The experimental Buttonless DFU Service for Secure DFU has been found.
- return service
- }
-
- if service.matches(uuid: uuidHelper.secureDFUService) {
- // Secure DFU Service has been found.
- return service
- }
-
- if service.matches(uuid: uuidHelper.legacyDFUService) {
- // Legacy DFU Service has been found.
- return service
- }
- }
+ private func findDfuService(in services: [CBService]?) -> CBService? {
+ return services?.first { service in
+ // The experimental Buttonless DFU Service for Secure DFU has been found.
+ // Skip the experimental Buttonless DFU Service if this feature wasn't enabled.
+ (experimentalButtonlessServiceInSecureDfuEnabled &&
+ service.matches(uuid: uuidHelper.buttonlessExperimentalService)) ||
+ // Secure DFU Service has been found.
+ service.matches(uuid: uuidHelper.secureDFUService) ||
+ // Legacy DFU Service has been found.
+ service.matches(uuid: uuidHelper.legacyDFUService)
}
- return nil
}
/**
@@ -651,7 +641,7 @@ internal class BaseCommonDFUPeripheral | 0xFFFF bytes (LegacyDFUService:L446).
- let bytesReceived: UInt32 = data.asValue(offset: 1)
- self.bytesReceived = bytesReceived
+ self.bytesReceived = data.asValue(offset: 1)
}
}
@@ -307,9 +306,9 @@ internal struct PacketReceiptNotification {
func peripheral(_ peripheral: CBPeripheral,
didUpdateNotificationStateFor characteristic: CBCharacteristic,
error: Error?) {
- if error != nil {
+ if let error = error {
logger.e("Enabling notifications failed. Check if Service Changed service is enabled.")
- logger.e(error!)
+ logger.e(error)
// Note:
// Error 253: Unknown ATT error.
// This most proably is a caching issue. Check if your device had
@@ -317,11 +316,12 @@ internal struct PacketReceiptNotification {
// app and bootloader modes. For bonded devices make sure it sends
// the Service Changed indication after connecting.
report?(.enablingControlPointFailed, "Enabling notifications failed")
- } else {
- logger.v("Notifications enabled for \(characteristic.uuid.uuidString)")
- logger.a("DFU Control Point notifications enabled")
- success?()
+ return
}
+
+ logger.v("Notifications enabled for \(characteristic.uuid.uuidString)")
+ logger.a("DFU Control Point notifications enabled")
+ success?()
}
func peripheral(_ peripheral: CBPeripheral,
@@ -336,11 +336,14 @@ internal struct PacketReceiptNotification {
guard self.characteristic.isEqual(characteristic) else {
return
}
+ guard let request = request else {
+ return
+ }
- if error != nil {
+ if let error = error {
if !resetSent {
logger.e("Writing to characteristic failed. Check if Service Changed characteristic is enabled.")
- logger.e(error!)
+ logger.e(error)
// Note:
// Error 3: Writing is not permitted
// This most proably is caching issue. Check if your device had
@@ -352,32 +355,33 @@ internal struct PacketReceiptNotification {
// When a 'JumpToBootloader', 'Activate and Reset' or 'Reset'
// command is sent the device may reset before sending the acknowledgement.
// This is not a blocker, as the device did disconnect and reset successfully.
- logger.a("\(request!.description) request sent")
+ logger.a("\(request.description) request sent")
logger.w("Device disconnected before sending ACK")
- logger.w(error!)
+ logger.w(error)
success?()
}
- } else {
- logger.i("Data written to \(characteristic.uuid.uuidString)")
-
- switch request! {
- case .startDfu(_), .startDfu_v1, .validateFirmware:
- logger.a("\(request!.description) request sent")
- // do not call success until we get a notification
- case .jumpToBootloader, .activateAndReset, .reset, .packetReceiptNotificationRequest(_):
- logger.a("\(request!.description) request sent")
- // there will be no notification send after these requests, call
- // `success()` immediatelly (for `.receiveFirmwareImage` the notification
- // will be sent after firmware upload is complete)
+ return
+ }
+
+ logger.i("Data written to \(characteristic.uuid.uuidString)")
+
+ switch request {
+ case .startDfu(_), .startDfu_v1, .validateFirmware:
+ logger.a("\(request.description) request sent")
+ // do not call success until we get a notification
+ case .jumpToBootloader, .activateAndReset, .reset, .packetReceiptNotificationRequest(_):
+ logger.a("\(request.description) request sent")
+ // there will be no notification send after these requests, call
+ // `success()` immediatelly (for `.receiveFirmwareImage` the notification
+ // will be sent after firmware upload is complete)
+ success?()
+ case .initDfuParameters(_), .initDfuParameters_v1:
+ // Log was created before sending the Op Code.
+ // Do not call success until we get a notification.
+ break
+ case .receiveFirmwareImage:
+ if proceed == nil {
success?()
- case .initDfuParameters(_), .initDfuParameters_v1:
- // Log was created before sending the Op Code.
- // Do not call success until we get a notification.
- break
- case .receiveFirmwareImage:
- if proceed == nil {
- success?()
- }
}
}
}
@@ -390,48 +394,51 @@ internal struct PacketReceiptNotification {
return
}
- if error != nil {
+ if let error = error {
// This characteristic is never read, the error may only pop up when
// notification is received.
logger.e("Receiving notification failed")
- logger.e(error!)
+ logger.e(error)
report?(.receivingNotificationFailed, "Receiving notification failed")
- } else {
- // During the upload we may get either a Packet Receipt Notification,
- // or a Response with status code.
- if proceed != nil {
- if let prn = PacketReceiptNotification(characteristic.value!) {
- proceed!(prn.bytesReceived)
- return
- }
+ return
+ }
+
+ guard let characteristicValue = characteristic.value else { return }
+
+ // During the upload we may get either a Packet Receipt Notification,
+ // or a Response with status code.
+ if proceed != nil {
+ if let prn = PacketReceiptNotification(characteristicValue) {
+ proceed!(prn.bytesReceived)
+ return
}
- // Otherwise...
- logger.i("Notification received from \(characteristic.uuid.uuidString), value (0x): \(characteristic.value!.hexString)")
+ }
+ // Otherwise...
+ logger.i("Notification received from \(characteristic.uuid.uuidString), value (0x): \(characteristicValue.hexString)")
+
+ // Parse response received.
+ let response = Response(characteristicValue)
+ if let response = response {
+ logger.a("\(response.description) received")
- // Parse response received.
- let response = Response(characteristic.value!)
- if let response = response {
- logger.a("\(response.description) received")
-
- if response.status == .success {
- switch response.requestOpCode! {
- case .initDfuParameters:
- logger.a("Initialize DFU Parameters completed")
- case .receiveFirmwareImage:
- let interval = CFAbsoluteTimeGetCurrent() - uploadStartTime! as CFTimeInterval
- logger.a("Upload completed in \(interval.format(".2")) seconds")
- default:
- break
- }
- success?()
- } else {
- logger.e("Error \(response.status!.code): \(response.status!.description)")
- report?(DFUError(rawValue: Int(response.status!.rawValue))!, response.status!.description)
+ if response.status == .success {
+ switch response.requestOpCode {
+ case .initDfuParameters:
+ logger.a("Initialize DFU Parameters completed")
+ case .receiveFirmwareImage:
+ let interval = CFAbsoluteTimeGetCurrent() - uploadStartTime! as CFTimeInterval
+ logger.a("Upload completed in \(interval.format(".2")) seconds")
+ default:
+ break
}
+ success?()
} else {
- logger.e("Unknown response received: 0x\(characteristic.value!.hexString)")
- report?(.unsupportedResponse, "Unsupported response received: 0x\(characteristic.value!.hexString)")
+ logger.e("Error \(response.status.code): \(response.status.description)")
+ report?(DFUError(rawValue: Int(response.status.rawValue))!, response.status.description)
}
+ } else {
+ logger.e("Unknown response received: 0x\(characteristicValue.hexString)")
+ report?(.unsupportedResponse, "Unsupported response received: 0x\(characteristicValue.hexString)")
}
}
diff --git a/iOSDFULibrary/Classes/Implementation/LegacyDFU/Characteristics/DFUPacket.swift b/iOSDFULibrary/Classes/Implementation/LegacyDFU/Characteristics/DFUPacket.swift
index c5c73fa0..775edf81 100644
--- a/iOSDFULibrary/Classes/Implementation/LegacyDFU/Characteristics/DFUPacket.swift
+++ b/iOSDFULibrary/Classes/Implementation/LegacyDFU/Characteristics/DFUPacket.swift
@@ -69,10 +69,10 @@ internal class DFUPacket: DFUCharacteristic {
// Get the peripheral object
let peripheral = characteristic.service.peripheral
- var data = Data(capacity: 12)
- data += size.softdevice.littleEndian
- data += size.bootloader.littleEndian
- data += size.application.littleEndian
+ let data = Data()
+ + size.softdevice.littleEndian
+ + size.bootloader.littleEndian
+ + size.application.littleEndian
let packetUUID = characteristic.uuid.uuidString
@@ -91,9 +91,8 @@ internal class DFUPacket: DFUCharacteristic {
// Get the peripheral object.
let peripheral = characteristic.service.peripheral
- var data = Data(capacity: 4)
- data += size.application.littleEndian
-
+ let data = Data() + size.application.littleEndian
+
let packetUUID = characteristic.uuid.uuidString
logger.v("Writing image size (\(size.application)b) to characteristic \(packetUUID)...")
@@ -167,14 +166,15 @@ internal class DFUPacket: DFUCharacteristic {
lastTime = startTime
// Notify progress delegate that upload has started (0%).
- queue.async(execute: {
+ queue.async {
progress?.dfuProgressDidChange(
for: firmware.currentPart,
outOf: firmware.parts,
to: 0,
currentSpeedBytesPerSecond: 0.0,
- avgSpeedBytesPerSecond: 0.0)
- })
+ avgSpeedBytesPerSecond: 0.0
+ )
+ }
}
while packetsToSendNow > 0 {
@@ -212,14 +212,15 @@ internal class DFUPacket: DFUCharacteristic {
lastTime = now
bytesSentSinceProgessNotification = bytesSent
- queue.async(execute: {
+ queue.async {
progress?.dfuProgressDidChange(
for: firmware.currentPart,
outOf: firmware.parts,
to: Int(currentProgress),
currentSpeedBytesPerSecond: currentSpeed,
- avgSpeedBytesPerSecond: avgSpeed)
- })
+ avgSpeedBytesPerSecond: avgSpeed
+ )
+ }
progressReported = currentProgress
}
}
diff --git a/iOSDFULibrary/Classes/Implementation/LegacyDFU/Characteristics/DFUVersion.swift b/iOSDFULibrary/Classes/Implementation/LegacyDFU/Characteristics/DFUVersion.swift
index d89d3cd0..38567c0e 100644
--- a/iOSDFULibrary/Classes/Implementation/LegacyDFU/Characteristics/DFUVersion.swift
+++ b/iOSDFULibrary/Classes/Implementation/LegacyDFU/Characteristics/DFUVersion.swift
@@ -86,27 +86,28 @@ internal typealias VersionCallback = (_ major: UInt8, _ minor: UInt8) -> Void
return
}
- if error != nil {
+ if let error = error {
logger.e("Reading DFU Version characteristic failed")
- logger.e(error!)
+ logger.e(error)
report?(.readingVersionFailed, "Reading DFU Version characteristic failed")
- } else {
- let data = characteristic.value
- logger.i("Read Response received from \(characteristic.uuid.uuidString), value\(data != nil && data!.count > 0 ? " (0x): " + data!.hexString : ": 0 bytes")")
-
- // Validate data length
- if data == nil || data!.count != 2 {
- logger.w("Invalid value: 2 bytes expected")
- report?(.readingVersionFailed, "Unsupported DFU Version: \(data != nil && data!.count > 0 ? "0x" + data!.hexString : "no value")")
- return
- }
-
- // Read major and minor
- let minor: UInt8 = data![0]
- let major: UInt8 = data![1]
-
- logger.a("Version number read: \(major).\(minor)")
- success?(major, minor)
+ return
}
+
+ let data = characteristic.value
+ logger.i("Read Response received from \(characteristic.uuid.uuidString), value\(data != nil && data!.count > 0 ? " (0x): " + data!.hexString : ": 0 bytes")")
+
+ // Validate data length
+ if data?.count != 2 {
+ logger.w("Invalid value: 2 bytes expected")
+ report?(.readingVersionFailed, "Unsupported DFU Version: \(data != nil && data!.count > 0 ? "0x" + data!.hexString : "no value")")
+ return
+ }
+
+ // Read major and minor
+ let minor: UInt8 = data![0]
+ let major: UInt8 = data![1]
+
+ logger.a("Version number read: \(major).\(minor)")
+ success?(major, minor)
}
}
diff --git a/iOSDFULibrary/Classes/Implementation/LegacyDFU/Peripherals/LegacyDFUPeripheral.swift b/iOSDFULibrary/Classes/Implementation/LegacyDFU/Peripherals/LegacyDFUPeripheral.swift
index 12a0bca9..ef23c6a5 100644
--- a/iOSDFULibrary/Classes/Implementation/LegacyDFU/Peripherals/LegacyDFUPeripheral.swift
+++ b/iOSDFULibrary/Classes/Implementation/LegacyDFU/Peripherals/LegacyDFUPeripheral.swift
@@ -44,7 +44,7 @@ internal class LegacyDFUPeripheral : BaseCommonDFUPeripheral Bool {
- let applicationMode = dfuService!.isInApplicationMode() ?? !forceDfu
+ let applicationMode = dfuService?.isInApplicationMode() ?? !forceDfu
if applicationMode {
logger.w("Application with buttonless update found")
@@ -82,12 +82,13 @@ internal class LegacyDFUPeripheral : BaseCommonDFUPeripheral Bool {
- if !aborted && paused && firmware != nil {
+ guard let dfuPacketCharacteristic = dfuPacketCharacteristic,
+ let firmware = firmware,
+ let progressQueue = progressQueue,
+ !aborted && paused else {
paused = false
- // onSuccess and onError callbacks are still kept by dfuControlPointCharacteristic.
- dfuPacketCharacteristic!.sendNext(packetReceiptNotificationNumber,
- packetsOf: firmware!,
- andReportProgressTo: progressDelegate,
- on: progressQueue!)
return paused
}
paused = false
+ // onSuccess and onError callbacks are still kept by dfuControlPointCharacteristic.
+ dfuPacketCharacteristic.sendNext(packetReceiptNotificationNumber,
+ packetsOf: firmware,
+ andReportProgressTo: progressDelegate,
+ on: progressQueue)
return paused
}
@@ -123,8 +126,7 @@ import CoreBluetooth
// When upload has been started and paused, we have to send the Reset command
// here as the device will not get a Packet Receipt Notification. If it hasn't
// been paused, the Reset command will be sent after receiving it, on line 380.
- if paused && firmware != nil {
- let _report = report!
+ if let _report = report, paused && firmware != nil {
firmware = nil
success = nil
report = nil
@@ -192,8 +194,8 @@ import CoreBluetooth
// - we must be in the DFU mode already (otherwise the device would be useless...).
// Note: On iOS the Generic Access and Generic Attribute services (nor HID Service)
// are not returned during service discovery.
- let services = service.peripheral.services!
- if services.count == 1 {
+ let services = service.peripheral.services
+ if services?.count == 1 {
return false
}
// If there are more services than just DFU Service, the state is uncertain.
@@ -226,7 +228,7 @@ import CoreBluetooth
func enableControlPoint(onSuccess success: @escaping Callback,
onError report: @escaping ErrorCallback) {
if !aborted {
- dfuControlPointCharacteristic!.enableNotifications(onSuccess: success, onError: report)
+ dfuControlPointCharacteristic?.enableNotifications(onSuccess: success, onError: report)
} else {
sendReset(onError: report)
}
@@ -239,7 +241,7 @@ import CoreBluetooth
*/
func jumpToBootloaderMode(onError report: @escaping ErrorCallback) {
if !aborted {
- dfuControlPointCharacteristic!.send(Request.jumpToBootloader,
+ dfuControlPointCharacteristic?.send(Request.jumpToBootloader,
onSuccess: nil, onError: report)
} else {
sendReset(onError: report)
@@ -248,8 +250,8 @@ import CoreBluetooth
/**
This methods sends the Start DFU command with the firmware type to the DFU Control
- Point characterristic, followed by the sizes of each firware component
- (each as UInt32, Little Endian).
+ Point characterristic, followed by the sizes of each firware component:
+ softdevice, bootloader, application (each as UInt32, Little Endian).
- parameter type: The type of the current firmware part.
- parameter size: The sizes of firmware components in the current part.
@@ -257,7 +259,8 @@ import CoreBluetooth
- parameter report: A callback called when a response with an error status is received.
*/
func sendDfuStart(withFirmwareType type: UInt8, andSize size: DFUFirmwareSize,
- onSuccess success: @escaping Callback, onError report: @escaping ErrorCallback) {
+ onSuccess success: @escaping Callback,
+ onError report: @escaping ErrorCallback) {
guard !aborted else {
sendReset(onError: report)
return
@@ -276,20 +279,25 @@ import CoreBluetooth
// it would receive a response with status = 6 (Operation failed) after sending
// some firmware packets. Delay 1 sec seems to work while 600 ms was too short.
// The time seems to be required to prepare flash(?).
- let sendStartDfu = {
+ let sendStartDfu = { [weak self] in
+ guard let self = self else { return }
// 1. Sends the Start DFU command with the firmware type to DFU Control Point
// characteristic.
// 2. Sends firmware sizes to DFU Packet characteristic.
// 3. Receives response notification and calls onSuccess or onError.
- self.dfuControlPointCharacteristic!.send(Request.startDfu(type: type), onSuccess: success) { (error, aMessage) in
- if error == .remoteLegacyDFUInvalidState {
- self.targetPeripheral!.shouldReconnect = true
- self.sendReset(onError: report)
- return
+ self.dfuControlPointCharacteristic?.send(Request.startDfu(type: type),
+ onSuccess: success,
+ onError: { [weak self] error, message in
+ guard let self = self else { return }
+ if error == .remoteLegacyDFUInvalidState {
+ self.targetPeripheral?.shouldReconnect = true
+ self.sendReset(onError: report)
+ return
+ }
+ report(error, message)
}
- report(error, aMessage)
- }
- self.dfuPacketCharacteristic!.sendFirmwareSize(size)
+ )
+ self.dfuPacketCharacteristic?.sendFirmwareSize(size)
}
if version != nil {
// The legacy DFU bootloader from SDK 7.0+ does not require delay.
@@ -304,15 +312,15 @@ import CoreBluetooth
/**
This methods sends the old Start DFU command (without the firmware type) to the
- DFU Control Point characterristic, followed by the application size
- (UInt32, Little Endian).
+ DFU Control Point characterristic, followed by the application size (UInt32, Little Endian).
- parameter size: The sizes of firmware components in the current part.
- parameter success: A callback called when a response with status Success is received.
- parameter report: A callback called when a response with an error status is received.
*/
func sendStartDfu(withFirmwareSize size: DFUFirmwareSize,
- onSuccess success: @escaping Callback, onError report: @escaping ErrorCallback) {
+ onSuccess success: @escaping Callback,
+ onError report: @escaping ErrorCallback) {
guard !aborted else {
sendReset(onError: report)
return
@@ -320,21 +328,24 @@ import CoreBluetooth
// See comment in sendDfuStart(withFirmwareType:andSize:onSuccess:onError) above
logger.d("wait(1000)")
- queue.asyncAfter(deadline: .now() + .milliseconds(1000)) {
+ queue.asyncAfter(deadline: .now() + .milliseconds(1000)) { [weak self] in
+ guard let self = self else { return }
// 1. Sends the Start DFU command with the firmware type to the DFU Control Point
// characteristic.
// 2. Sends firmware sizes to the DFU Packet characteristic.
// 3. Receives response notification and calls onSuccess or onError.
- self.dfuControlPointCharacteristic!.send(Request.startDfu_v1, onSuccess: success) {
- (error, aMessage) in
- if error == .remoteLegacyDFUInvalidState {
- self.targetPeripheral!.shouldReconnect = true
- self.sendReset(onError: report)
- return
- }
- report(error, aMessage)
- }
- self.dfuPacketCharacteristic!.sendFirmwareSize_v1(size)
+ self.dfuControlPointCharacteristic?.send(Request.startDfu_v1,
+ onSuccess: success,
+ onError: { [weak self] error, message in
+ guard let self = self else { return }
+ if error == .remoteLegacyDFUInvalidState {
+ self.targetPeripheral!.shouldReconnect = true
+ self.sendReset(onError: report)
+ return
+ }
+ report(error, message)
+ })
+ self.dfuPacketCharacteristic?.sendFirmwareSize_v1(size)
}
}
@@ -352,7 +363,8 @@ import CoreBluetooth
- parameter report: A callback called when a response with an error status is received.
*/
func sendInitPacket(_ data: Data,
- onSuccess success: @escaping Callback, onError report: @escaping ErrorCallback) {
+ onSuccess success: @escaping Callback,
+ onError report: @escaping ErrorCallback) {
guard !aborted else {
sendReset(onError: report)
return
@@ -378,11 +390,16 @@ import CoreBluetooth
// Therefore, there are 2 commands to the DFU Control Point required: one
// before we start sending init packet, and another one the whole init packet
// is sent. After sending the second packet a notification will be received.
- dfuControlPointCharacteristic!.send(Request.initDfuParameters(req: InitDfuParametersRequest.receiveInitPacket), onSuccess: nil, onError: report)
- dfuPacketCharacteristic!.sendInitPacket(data)
- dfuControlPointCharacteristic!.send(Request.initDfuParameters(req: InitDfuParametersRequest.initPacketComplete), onSuccess: success,
- onError: {
- error, message in
+ dfuControlPointCharacteristic?.send(
+ Request.initDfuParameters(req: InitDfuParametersRequest.receiveInitPacket),
+ onSuccess: nil,
+ onError: report
+ )
+ dfuPacketCharacteristic?.sendInitPacket(data)
+ dfuControlPointCharacteristic?.send(
+ Request.initDfuParameters(req: InitDfuParametersRequest.initPacketComplete),
+ onSuccess: success,
+ onError: { error, message in
if error == .remoteLegacyDFUOperationFailed {
// Init packet validation failed. The device type, revision, app
// version or SoftDevice version does not match values specified
@@ -400,9 +417,9 @@ import CoreBluetooth
// After receiving this packet the DFU target was sending a notification with
// status.
if data.count == 2 {
- dfuControlPointCharacteristic!.send(Request.initDfuParameters_v1,
+ dfuControlPointCharacteristic?.send(Request.initDfuParameters_v1,
onSuccess: success, onError: report)
- dfuPacketCharacteristic!.sendInitPacket(data)
+ dfuPacketCharacteristic?.sendInitPacket(data)
} else {
// After sending the Extended Init Packet, the DFU would fail on CRC
// validation eventually.
@@ -436,7 +453,7 @@ import CoreBluetooth
onError report: @escaping ErrorCallback) {
if !aborted {
packetReceiptNotificationNumber = prnValue
- dfuControlPointCharacteristic!.send(Request.packetReceiptNotificationRequest(number: prnValue),
+ dfuControlPointCharacteristic?.send(Request.packetReceiptNotificationRequest(number: prnValue),
onSuccess: success, onError: report)
} else {
sendReset(onError: report)
@@ -472,11 +489,13 @@ import CoreBluetooth
// 2. Sends firmware to the DFU Packet characteristic. If number > 0 it will receive
// Packet Receit Notifications every number packets.
// 3. Receives response notification and calls onSuccess or onError.
- dfuControlPointCharacteristic!.send(Request.receiveFirmwareImage,
- onSuccess: {
+ dfuControlPointCharacteristic?.send(Request.receiveFirmwareImage,
+ onSuccess: { [weak self] in
+ guard let self = self else { return }
// Register callbacks for Packet Receipt Notifications/Responses.
- self.dfuControlPointCharacteristic!.waitUntilUploadComplete(
- onSuccess: {
+ self.dfuControlPointCharacteristic?.waitUntilUploadComplete(
+ onSuccess: { [weak self] in
+ guard let self = self else { return }
// Upload is completed, release the temporary parameters.
self.firmware = nil
self.report = nil
@@ -484,8 +503,8 @@ import CoreBluetooth
self.progressQueue = nil
success()
},
- onPacketReceiptNofitication: {
- bytesReceived in
+ onPacketReceiptNofitication: { [weak self] bytesReceived in
+ guard let self = self else { return }
// This callback is called from SecureDFUControlPoint in 2 cases:
// when a PRN is received (bytesReceived contains number of bytes
// reported), or when the iOS reports the
@@ -499,14 +518,21 @@ import CoreBluetooth
// Each time a PRN is received, send next bunch of packets
if !self.paused && !self.aborted {
- let bytesSent = self.dfuPacketCharacteristic!.bytesSent
+ guard let dfuPacketCharacteristic = self.dfuPacketCharacteristic else {
+ self.firmware = nil
+ self.report = nil
+ self.progressDelegate = nil
+ self.progressQueue = nil
+ return
+ }
+ let bytesSent = dfuPacketCharacteristic.bytesSent
// Due to https://github.com/NordicSemiconductor/IOS-Pods-DFU-Library/issues/54
// only 16 least significant bits are verified.
if peripheralIsReadyToSendWriteWithoutRequest ||
(bytesSent & 0xFFFF) == (bytesReceived! & 0xFFFF) {
- self.dfuPacketCharacteristic!.sendNext(self.packetReceiptNotificationNumber,
- packetsOf: firmware,
- andReportProgressTo: progress, on: queue)
+ dfuPacketCharacteristic.sendNext(self.packetReceiptNotificationNumber,
+ packetsOf: firmware,
+ andReportProgressTo: progress, on: queue)
} else {
// Target device deported invalid number of bytes received
report(.bytesLost, "\(bytesSent) bytes were sent while \(bytesReceived!) bytes were reported as received")
@@ -521,8 +547,8 @@ import CoreBluetooth
self.sendReset(onError: report)
}
},
- onError: {
- error, message in
+ onError: { [weak self] error, message in
+ guard let self = self else { return }
// Upload failed, release the temporary parameters.
self.firmware = nil
self.report = nil
@@ -533,10 +559,11 @@ import CoreBluetooth
)
// ...and start sending firmware.
if !self.paused && !self.aborted {
- let start = {
+ let start = { [weak self] in
+ guard let self = self else { return }
self.logger.a("Uploading firmware...")
self.logger.v("Sending firmware to DFU Packet characteristic...")
- self.dfuPacketCharacteristic!.sendNext(self.packetReceiptNotificationNumber,
+ self.dfuPacketCharacteristic?.sendNext(self.packetReceiptNotificationNumber,
packetsOf: firmware,
andReportProgressTo: progress, on: queue)
}
@@ -558,7 +585,8 @@ import CoreBluetooth
self.sendReset(onError: report)
}
},
- onError: report)
+ onError: report
+ )
}
/**
@@ -570,7 +598,7 @@ import CoreBluetooth
func sendValidateFirmwareRequest(onSuccess success: @escaping Callback,
onError report: @escaping ErrorCallback) {
if !aborted {
- dfuControlPointCharacteristic!.send(Request.validateFirmware,
+ dfuControlPointCharacteristic?.send(Request.validateFirmware,
onSuccess: success, onError: report)
} else {
sendReset(onError: report)
@@ -585,7 +613,7 @@ import CoreBluetooth
*/
func sendActivateAndResetRequest(onError report: @escaping ErrorCallback) {
if !aborted {
- dfuControlPointCharacteristic!.send(Request.activateAndReset,
+ dfuControlPointCharacteristic?.send(Request.activateAndReset,
onSuccess: nil, onError: report)
} else {
sendReset(onError: report)
@@ -600,7 +628,7 @@ import CoreBluetooth
- parameter report: A callback called when writing characteristic failed.
*/
func sendReset(onError report: @escaping ErrorCallback) {
- dfuControlPointCharacteristic!.send(Request.reset, onSuccess: nil, onError: report)
+ dfuControlPointCharacteristic?.send(Request.reset, onSuccess: nil, onError: report)
}
// MARK: - Private service API methods
@@ -614,13 +642,13 @@ import CoreBluetooth
*/
private func readDfuVersion(onSuccess success: @escaping Callback,
onError report: @escaping ErrorCallback) {
- dfuVersionCharacteristic!.readVersion(
- onSuccess: {
- major, minor in
+ dfuVersionCharacteristic?.readVersion(
+ onSuccess: { [weak self] major, minor in
+ guard let self = self else { return }
self.version = (major, minor)
success()
},
- onError:report
+ onError: report
)
}
@@ -693,7 +721,12 @@ import CoreBluetooth
_report?(.readingVersionFailed, "DFU Version found, but does not have the Read property")
return
}
- readDfuVersion(onSuccess: _success!, onError: _report!)
+ guard let _success = _success,
+ let _report = _report else {
+ // This should never happen.
+ return
+ }
+ readDfuVersion(onSuccess: _success, onError: _report)
} else {
// Else... proceed.
version = nil
diff --git a/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/ButtonlessDFU.swift b/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/ButtonlessDFU.swift
index b383bdd9..cbb1ac2c 100644
--- a/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/ButtonlessDFU.swift
+++ b/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/ButtonlessDFU.swift
@@ -93,28 +93,28 @@ internal enum ButtonlessDFURequest {
}
internal struct ButtonlessDFUResponse {
- let opCode : ButtonlessDFUOpCode?
- let requestOpCode : ButtonlessDFUOpCode?
- let status : ButtonlessDFUResultCode?
+ let opCode : ButtonlessDFUOpCode
+ let requestOpCode : ButtonlessDFUOpCode
+ let status : ButtonlessDFUResultCode
init?(_ data: Data) {
// The correct response is always 3 bytes long: Response Op Code,
// Request Op Code and Status.
- let opCode : UInt8 = data[0]
- let requestOpCode : UInt8 = data[1]
- let status : UInt8 = data[2]
-
- self.opCode = ButtonlessDFUOpCode(rawValue: opCode)
- self.requestOpCode = ButtonlessDFUOpCode(rawValue: requestOpCode)
- self.status = ButtonlessDFUResultCode(rawValue: status)
-
- if self.opCode != .responseCode || self.requestOpCode == nil || self.status == nil {
+ guard data.count == 3,
+ let opCode = ButtonlessDFUOpCode(rawValue: data[0]),
+ let requestOpCode = ButtonlessDFUOpCode(rawValue: data[1]),
+ let status = ButtonlessDFUResultCode(rawValue: data[2]),
+ opCode == .responseCode else {
return nil
}
+
+ self.opCode = opCode
+ self.requestOpCode = requestOpCode
+ self.status = status
}
var description: String {
- return "Response (Op Code = \(requestOpCode!.rawValue), Status = \(status!.rawValue))"
+ return "Response (Op Code = \(requestOpCode.rawValue), Status = \(status.rawValue))"
}
}
@@ -224,38 +224,38 @@ internal class ButtonlessDFU : NSObject, CBPeripheralDelegate, DFUCharacteristic
func peripheral(_ peripheral: CBPeripheral,
didUpdateNotificationStateFor characteristic: CBCharacteristic,
error: Error?) {
- if error != nil {
+ if let error = error {
if characteristic.properties.contains(.indicate) {
logger.e("Enabling indications failed")
- logger.e(error!)
+ logger.e(error)
report?(.enablingControlPointFailed, "Enabling indications failed")
} else {
logger.e("Enabling notifications failed")
- logger.e(error!)
+ logger.e(error)
report?(.enablingControlPointFailed, "Enabling notifications failed")
}
+ return
+ }
+ if characteristic.properties.contains(.indicate) {
+ logger.v("Indications enabled for \(characteristic.uuid.uuidString)")
+ logger.a("Buttonless DFU indications enabled")
} else {
- if characteristic.properties.contains(.indicate) {
- logger.v("Indications enabled for \(characteristic.uuid.uuidString)")
- logger.a("Buttonless DFU indications enabled")
- } else {
- logger.v("Notifications enabled for \(characteristic.uuid.uuidString)")
- logger.a("Buttonless DFU notifications enabled")
- }
- success?()
+ logger.v("Notifications enabled for \(characteristic.uuid.uuidString)")
+ logger.a("Buttonless DFU notifications enabled")
}
+ success?()
}
func peripheral(_ peripheral: CBPeripheral,
didWriteValueFor characteristic: CBCharacteristic,
error: Error?) {
- if error != nil {
+ if let error = error {
logger.e("Writing to characteristic failed")
- logger.e(error!)
+ logger.e(error)
report?(.writingCharacteristicFailed, "Writing to characteristic failed")
- } else {
- logger.i("Data written to \(characteristic.uuid.uuidString)")
+ return
}
+ logger.i("Data written to \(characteristic.uuid.uuidString)")
}
func peripheral(_ peripheral: CBPeripheral,
@@ -266,37 +266,42 @@ internal class ButtonlessDFU : NSObject, CBPeripheralDelegate, DFUCharacteristic
return
}
- if error != nil {
+ if let error = error {
// This characteristic is never read, the error may only pop up when notification/indication
// is received.
logger.e("Receiving response failed")
- logger.e(error!)
+ logger.e(error)
report?(.receivingNotificationFailed, "Receiving response failed")
+ return
+ }
+
+ guard let characteristicValue = characteristic.value else { return }
+
+ if characteristic.properties.contains(.indicate) {
+ logger.i("Indication received from \(characteristic.uuid.uuidString), value (0x):\(characteristicValue.hexString)")
} else {
- if characteristic.properties.contains(.indicate) {
- logger.i("Indication received from \(characteristic.uuid.uuidString), value (0x):\(characteristic.value!.hexString)")
- } else {
- logger.i("Notification received from \(characteristic.uuid.uuidString), value (0x):\(characteristic.value!.hexString)")
- }
-
- // Parse response received.
- let dfuResponse = ButtonlessDFUResponse(characteristic.value!)
- if let dfuResponse = dfuResponse {
- if dfuResponse.status == .success {
- logger.a("\(dfuResponse.description) received")
- success?()
- } else {
- logger.e("Error \(dfuResponse.status!.code): \(dfuResponse.status!.description)")
- // The returned errod code is incremented by 90 or 9000 to match Buttonless DFU or
- // Experimental Buttonless DFU remote codes.
- // See DFUServiceDelegate.swift -> DFUError.
- let offset = characteristic.uuid.isEqual(uuidHelper.buttonlessExperimentalCharacteristic) ? 9000 : 90
- report?(DFUError(rawValue: Int(dfuResponse.status!.code) + offset)!, dfuResponse.status!.description)
- }
- } else {
- logger.e("Unknown response received: 0x\(characteristic.value!.hexString)")
- report?(.unsupportedResponse, "Unsupported response received: 0x\(characteristic.value!.hexString)")
- }
+ logger.i("Notification received from \(characteristic.uuid.uuidString), value (0x):\(characteristicValue.hexString)")
}
+
+ // Parse response received.
+ let dfuResponse = ButtonlessDFUResponse(characteristicValue)
+ guard let dfuResponse = dfuResponse else {
+ logger.e("Unknown response received: 0x\(characteristicValue.hexString)")
+ report?(.unsupportedResponse, "Unsupported response received: 0x\(characteristicValue.hexString)")
+ return
+ }
+
+ guard dfuResponse.status == .success else {
+ logger.e("Error \(dfuResponse.status.code): \(dfuResponse.status.description)")
+ // The returned errod code is incremented by 90 or 9000 to match Buttonless DFU or
+ // Experimental Buttonless DFU remote codes.
+ // See DFUServiceDelegate.swift -> DFUError.
+ let offset = characteristic.uuid.isEqual(uuidHelper.buttonlessExperimentalCharacteristic) ? 9000 : 90
+ report?(DFUError(rawValue: Int(dfuResponse.status.code) + offset)!, dfuResponse.status.description)
+ return
+ }
+
+ logger.a("\(dfuResponse.description) received")
+ success?()
}
}
diff --git a/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/SecureDFUControlPoint.swift b/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/SecureDFUControlPoint.swift
index e58e39b6..f8a0380b 100644
--- a/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/SecureDFUControlPoint.swift
+++ b/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/SecureDFUControlPoint.swift
@@ -176,32 +176,35 @@ internal enum SecureDFUResultCode : UInt8 {
}
}
-internal typealias SecureDFUResponseCallback = (_ response : SecureDFUResponse?) -> Void
+internal typealias SecureDFUResponseCallback = (_ response : SecureDFUResponse) -> Void
internal struct SecureDFUResponse {
- let opCode : SecureDFUOpCode?
- let requestOpCode : SecureDFUOpCode?
- let status : SecureDFUResultCode?
+ let opCode : SecureDFUOpCode
+ let requestOpCode : SecureDFUOpCode
+ let status : SecureDFUResultCode
let maxSize : UInt32?
let offset : UInt32?
let crc : UInt32?
let error : SecureDFUExtendedErrorCode?
init?(_ data: Data) {
- let opCode : UInt8 = data[0]
- let requestOpCode : UInt8 = data[1]
- let status : UInt8 = data[2]
-
- self.opCode = SecureDFUOpCode(rawValue: opCode)
- self.requestOpCode = SecureDFUOpCode(rawValue: requestOpCode)
- self.status = SecureDFUResultCode(rawValue: status)
+ // The response has at least 3 bytes.
+ guard data.count >= 3,
+ let opCode = SecureDFUOpCode(rawValue: data[0]),
+ let requestOpCode = SecureDFUOpCode(rawValue: data[1]),
+ let status = SecureDFUResultCode(rawValue: data[2]),
+ opCode == .responseCode else {
+ return nil
+ }
- // Parse response data in case of a success
- if self.status == .success {
- switch self.requestOpCode {
- case .some(.readObjectInfo):
+ switch status {
+ case .success:
+ // Parse response data in case of a success.
+ switch requestOpCode {
+ case .readObjectInfo:
// The correct reponse for Read Object Info has additional 12 bytes:
// Max Object Size, Offset and CRC.
+ guard data.count == 15 else { return nil }
let maxSize : UInt32 = data.asValue(offset: 3)
let offset : UInt32 = data.asValue(offset: 7)
let crc : UInt32 = data.asValue(offset: 11)
@@ -210,9 +213,10 @@ internal struct SecureDFUResponse {
self.offset = offset
self.crc = crc
self.error = nil
- case .some(.calculateChecksum):
+ case .calculateChecksum:
// The correct reponse for Calculate Checksum has additional 8 bytes:
// Offset and CRC.
+ guard data.count == 11 else { return nil }
let offset : UInt32 = data.asValue(offset: 3)
let crc : UInt32 = data.asValue(offset: 7)
@@ -221,84 +225,85 @@ internal struct SecureDFUResponse {
self.crc = crc
self.error = nil
default:
- self.maxSize = 0
- self.offset = 0
- self.crc = 0
+ self.maxSize = nil
+ self.offset = nil
+ self.crc = nil
self.error = nil
}
- } else if self.status == .extendedError {
+ case .extendedError:
// If extended error was received, parse the extended error code
// The correct response for Read Error request has 4 bytes.
// The 4th byte is the extended error code.
- let error : UInt8 = data[3]
+ guard data.count == 4,
+ let error = SecureDFUExtendedErrorCode(rawValue: data[3]) else {
+ return nil
+ }
- self.maxSize = 0
- self.offset = 0
- self.crc = 0
- self.error = SecureDFUExtendedErrorCode(rawValue: error)
- } else {
- self.maxSize = 0
- self.offset = 0
- self.crc = 0
+ self.maxSize = nil
+ self.offset = nil
+ self.crc = nil
+ self.error = error
+ default:
+ self.maxSize = nil
+ self.offset = nil
+ self.crc = nil
self.error = nil
}
-
- if self.opCode != .responseCode || self.requestOpCode == nil || self.status == nil {
- return nil
- }
+
+ self.opCode = opCode
+ self.requestOpCode = requestOpCode
+ self.status = status
}
var description: String {
- if status == .success {
+ switch status {
+ case .extendedError:
+ if let error = error {
+ return "Response (Op Code = \(requestOpCode.rawValue), Status = \(status.rawValue), Extended Error \(error.rawValue) = \(error.description))"
+ }
+ return "Response (Op Code = \(requestOpCode.rawValue), Status = \(status.rawValue), Unsupported Extended Error value)"
+ case .success:
switch requestOpCode {
- case .some(.readObjectInfo):
+ case .readObjectInfo:
// Max size for a command object is usually around 256. Let's say 1024,
// just to be sure. This is only for logging, so may be wrong.
return String(format: "\(maxSize! > 1024 ? "Data" : "Command") object info (Max size = \(maxSize!), Offset = \(offset!), CRC = %08X)", crc!)
- case .some(.calculateChecksum):
+ case .calculateChecksum:
return String(format: "Checksum (Offset = \(offset!), CRC = %08X)", crc!)
default:
// Other responses are either not logged, or logged by service or executor,
// so this 'default' should never be called.
break
}
- } else if status == .extendedError {
- if let error = error {
- return "Response (Op Code = \(requestOpCode!.rawValue), Status = \(status!.rawValue), Extended Error \(error.rawValue) = \(error.description))"
- } else {
- return "Response (Op Code = \(requestOpCode!.rawValue), Status = \(status!.rawValue), Unsupported Extended Error value)"
- }
+ fallthrough
+ default:
+ return "Response (Op Code = \(requestOpCode.rawValue), Status = \(status.rawValue))"
}
- return "Response (Op Code = \(requestOpCode!.rawValue), Status = \(status!.rawValue))"
}
}
internal struct SecureDFUPacketReceiptNotification {
- let opCode : SecureDFUOpCode?
- let requestOpCode : SecureDFUOpCode?
- let resultCode : SecureDFUResultCode?
+ let opCode : SecureDFUOpCode
+ let requestOpCode : SecureDFUOpCode
+ let resultCode : SecureDFUResultCode
let offset : UInt32
let crc : UInt32
init?(_ data: Data) {
- let opCode : UInt8 = data[0]
- let requestOpCode : UInt8 = data[1]
- let resultCode : UInt8 = data[2]
-
- self.opCode = SecureDFUOpCode(rawValue: opCode)
- self.requestOpCode = SecureDFUOpCode(rawValue: requestOpCode)
- self.resultCode = SecureDFUResultCode(rawValue: resultCode)
-
- if self.opCode != .responseCode {
- return nil
- }
- if self.requestOpCode != .calculateChecksum {
- return nil
- }
- if self.resultCode != .success {
+ guard data.count == 11,
+ let opCode = SecureDFUOpCode(rawValue: data[0]),
+ let requestOpCode = SecureDFUOpCode(rawValue: data[1]),
+ let resultCode = SecureDFUResultCode(rawValue: data[2]),
+ opCode == .responseCode,
+ requestOpCode == .calculateChecksum,
+ resultCode == .success else {
return nil
}
+ self.opCode = opCode
+ self.requestOpCode = requestOpCode
+ self.resultCode = resultCode
+
let offset : UInt32 = data.asValue(offset: 3)
let crc : UInt32 = data.asValue(offset: 7)
@@ -446,9 +451,9 @@ internal class SecureDFUControlPoint : NSObject, CBPeripheralDelegate, DFUCharac
// MARK: - Peripheral Delegate callbacks
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
- if error != nil {
+ if let error = error {
logger.e("Enabling notifications failed. Check if Service Changed service is enabled.")
- logger.e(error!)
+ logger.e(error)
// Note:
// Error 253: Unknown ATT error.
// This most proably is caching issue. Check if your device had Service Changed
@@ -456,11 +461,11 @@ internal class SecureDFUControlPoint : NSObject, CBPeripheralDelegate, DFUCharac
// For bonded devices make sure it sends the Service Changed indication after
// connecting.
report?(.enablingControlPointFailed, "Enabling notifications failed")
- } else {
- logger.v("Notifications enabled for \(characteristic.uuid.uuidString)")
- logger.a("Secure DFU Control Point notifications enabled")
- success?()
+ return
}
+ logger.v("Notifications enabled for \(characteristic.uuid.uuidString)")
+ logger.a("Secure DFU Control Point notifications enabled")
+ success?()
}
func peripheral(_ peripheral: CBPeripheral,
@@ -474,9 +479,9 @@ internal class SecureDFUControlPoint : NSObject, CBPeripheralDelegate, DFUCharac
return
}
- if error != nil {
+ if let error = error {
logger.e("Writing to characteristic failed. Check if Service Changed service is enabled.")
- logger.e(error!)
+ logger.e(error)
// Note:
// Error 3: Writing is not permitted.
// This most proably is caching issue. Check if your device had Service Changed
@@ -484,9 +489,9 @@ internal class SecureDFUControlPoint : NSObject, CBPeripheralDelegate, DFUCharac
// is a specially a case in SDK 12.x, where it was disabled by default.
// For bonded devices make sure it sends the Service Changed indication after connecting.
report?(.writingCharacteristicFailed, "Writing to characteristic failed")
- } else {
- logger.i("Data written to \(characteristic.uuid.uuidString)")
+ return
}
+ logger.i("Data written to \(characteristic.uuid.uuidString)")
}
func peripheral(_ peripheral: CBPeripheral,
@@ -497,53 +502,56 @@ internal class SecureDFUControlPoint : NSObject, CBPeripheralDelegate, DFUCharac
return
}
- if error != nil {
+ if let error = error {
// This characteristic is never read, the error may only pop up when notification
// is received.
logger.e("Receiving notification failed")
- logger.e(error!)
+ logger.e(error)
report?(.receivingNotificationFailed, "Receiving notification failed")
- } else {
- // During the upload we may get either a Packet Receipt Notification, or a Response
- // with status code.
- if proceed != nil {
- if let prn = SecureDFUPacketReceiptNotification(characteristic.value!) {
- proceed!(prn.offset) // The CRC is not verified after receiving a PRN, only the offset is.
- return
- }
- }
- // Otherwise...
- logger.i("Notification received from \(characteristic.uuid.uuidString), value (0x): \(characteristic.value!.hexString)")
+ return
+ }
+
+ guard let characteristicValue = characteristic.value else { return }
+
+ // During the upload we may get either a Packet Receipt Notification, or a Response
+ // with status code.
+ if let proceed = proceed,
+ let prn = SecureDFUPacketReceiptNotification(characteristicValue) {
+ proceed(prn.offset) // The CRC is not verified after receiving a PRN, only the offset is.
+ return
+ }
+ // Otherwise...
+ logger.i("Notification received from \(characteristic.uuid.uuidString), value (0x): \(characteristicValue.hexString)")
- // Parse response received.
- let dfuResponse = SecureDFUResponse(characteristic.value!)
- if let dfuResponse = dfuResponse {
- if dfuResponse.status == .success {
- switch dfuResponse.requestOpCode! {
- case .readObjectInfo, .calculateChecksum:
- logger.a("\(dfuResponse.description) received")
- response?(dfuResponse)
- case .createObject, .setPRNValue, .execute:
- // Don't log, executor or service will do it for us.
- success?()
- default:
- logger.a("\(dfuResponse.description) received")
- success?()
- }
- } else if dfuResponse.status == .extendedError {
- // An extended error was received.
- logger.e("Error \(dfuResponse.error!.code): \(dfuResponse.error!.description)")
- // The returned errod code is incremented by 20 to match Secure DFU remote codes.
- report?(DFUError(rawValue: Int(dfuResponse.error!.code) + 20)!, dfuResponse.error!.description)
- } else {
- logger.e("Error \(dfuResponse.status!.code): \(dfuResponse.status!.description)")
- // The returned errod code is incremented by 10 to match Secure DFU remote codes.
- report?(DFUError(rawValue: Int(dfuResponse.status!.code) + 10)!, dfuResponse.status!.description)
- }
- } else {
- logger.e("Unknown response received: 0x\(characteristic.value!.hexString)")
- report?(.unsupportedResponse, "Unsupported response received: 0x\(characteristic.value!.hexString)")
+ // Parse response received.
+ guard let dfuResponse = SecureDFUResponse(characteristicValue) else {
+ logger.e("Unknown response received: 0x\(characteristicValue.hexString)")
+ report?(.unsupportedResponse, "Unsupported response received: 0x\(characteristicValue.hexString)")
+ return
+ }
+
+ switch dfuResponse.status {
+ case .success:
+ switch dfuResponse.requestOpCode {
+ case .readObjectInfo, .calculateChecksum:
+ logger.a("\(dfuResponse.description) received")
+ response?(dfuResponse)
+ case .createObject, .setPRNValue, .execute:
+ // Don't log, executor or service will do it for us.
+ success?()
+ default:
+ logger.a("\(dfuResponse.description) received")
+ success?()
}
+ case .extendedError:
+ // An extended error was received.
+ logger.e("Error \(dfuResponse.error!.code): \(dfuResponse.error!.description)")
+ // The returned errod code is incremented by 20 to match Secure DFU remote codes.
+ report?(DFUError(rawValue: Int(dfuResponse.error!.code) + 20)!, dfuResponse.error!.description)
+ default:
+ logger.e("Error \(dfuResponse.status.code): \(dfuResponse.status.description)")
+ // The returned errod code is incremented by 10 to match Secure DFU remote codes.
+ report?(DFUError(rawValue: Int(dfuResponse.status.code) + 10)!, dfuResponse.status.description)
}
}
diff --git a/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/SecureDFUPacket.swift b/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/SecureDFUPacket.swift
index 9c9c2f1f..b5f7dafa 100644
--- a/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/SecureDFUPacket.swift
+++ b/iOSDFULibrary/Classes/Implementation/SecureDFU/Characteristics/SecureDFUPacket.swift
@@ -143,14 +143,15 @@ internal class SecureDFUPacket: DFUCharacteristic {
totalBytesSentSinceProgessNotification = totalBytesSentWhenDfuStarted
// Notify progress delegate that upload has started (0%).
- queue.async(execute: {
+ queue.async {
progress?.dfuProgressDidChange(
for: firmware.currentPart,
outOf: firmware.parts,
to: 0,
currentSpeedBytesPerSecond: 0.0,
- avgSpeedBytesPerSecond: 0.0)
- })
+ avgSpeedBytesPerSecond: 0.0
+ )
+ }
}
let originalPacketsToSendNow = packetsToSendNow
@@ -189,14 +190,15 @@ internal class SecureDFUPacket: DFUCharacteristic {
totalBytesSentSinceProgessNotification = totalBytesSent
// Notify progress delegate of overall progress.
- queue.async(execute: {
+ queue.async {
progress?.dfuProgressDidChange(
for: firmware.currentPart,
outOf: firmware.parts,
to: Int(currentProgress),
currentSpeedBytesPerSecond: currentSpeed,
- avgSpeedBytesPerSecond: avgSpeed)
- })
+ avgSpeedBytesPerSecond: avgSpeed
+ )
+ }
progressReported = currentProgress
}
diff --git a/iOSDFULibrary/Classes/Implementation/SecureDFU/DFU/SecureDFUServiceInitiator.swift b/iOSDFULibrary/Classes/Implementation/SecureDFU/DFU/SecureDFUServiceInitiator.swift
index 9ae0b1c5..d87c83cb 100644
--- a/iOSDFULibrary/Classes/Implementation/SecureDFU/DFU/SecureDFUServiceInitiator.swift
+++ b/iOSDFULibrary/Classes/Implementation/SecureDFU/DFU/SecureDFUServiceInitiator.swift
@@ -36,8 +36,7 @@ import CoreBluetooth
// The firmware file must be specified before calling `start(...)`.
guard let _ = file else {
delegateQueue.async {
- self.delegate?.dfuError(.fileNotSpecified, didOccurWithMessage:
- "Firmware not specified")
+ self.delegate?.dfuError(.fileNotSpecified, didOccurWithMessage: "Firmware not specified")
}
return nil
}
diff --git a/iOSDFULibrary/Classes/Implementation/SecureDFU/Peripheral/SecureDFUPeripheral.swift b/iOSDFULibrary/Classes/Implementation/SecureDFU/Peripheral/SecureDFUPeripheral.swift
index f6c4f985..fd2389eb 100644
--- a/iOSDFULibrary/Classes/Implementation/SecureDFU/Peripheral/SecureDFUPeripheral.swift
+++ b/iOSDFULibrary/Classes/Implementation/SecureDFU/Peripheral/SecureDFUPeripheral.swift
@@ -73,14 +73,14 @@ internal class SecureDFUPeripheral : BaseCommonDFUPeripheral Bool {
- let applicationMode = dfuService!.isInApplicationMode() ?? !forceDfu
+ let applicationMode = dfuService?.isInApplicationMode() ?? !forceDfu
if applicationMode {
logger.w("Application with buttonless update found")
@@ -97,15 +97,16 @@ internal class SecureDFUPeripheral : BaseCommonDFUPeripheral, of firmware: DFUFirmware,
andReportProgressTo progress: DFUProgressDelegate?,
on queue: DispatchQueue) {
- dfuService!.sendNextObject(from: range, of: firmware,
+ dfuService?.sendNextObject(from: range, of: firmware,
andReportProgressTo: progress, on: queue,
onSuccess: { self.delegate?.peripheralDidReceiveObject() },
onError: defaultErrorCallback
@@ -228,7 +229,7 @@ internal class SecureDFUPeripheral : BaseCommonDFUPeripheral ~15 packets without
// PRNs may lead to buffer overflow.
- dfuService!.sendInitPacket(withdata: packetData)
+ dfuService?.sendInitPacket(withdata: packetData)
self.delegate?.peripheralDidReceiveInitPacket()
}
@@ -253,10 +254,10 @@ internal class SecureDFUPeripheral : BaseCommonDFUPeripheral Bool {
- if !aborted && paused && firmware != nil {
+ guard let dfuPacketCharacteristic = dfuPacketCharacteristic,
+ let range = range,
+ let firmware = firmware,
+ let progressQueue = progressQueue,
+ let success = success,
+ !aborted && paused else {
paused = false
- dfuPacketCharacteristic!.sendNext(packetReceiptNotificationNumber ?? 0,
- packetsFrom: range!, of: firmware!,
- andReportProgressTo: progressDelegate, on: progressQueue!,
- andCompletionTo: success!)
return paused
}
paused = false
+ dfuPacketCharacteristic.sendNext(packetReceiptNotificationNumber ?? 0,
+ packetsFrom: range, of: firmware,
+ andReportProgressTo: progressDelegate, on: progressQueue,
+ andCompletionTo: success)
return paused
}
@@ -111,8 +116,7 @@ import CoreBluetooth
// When upload has been started and paused, we have to send the Reset command here
// as the device will not get a Packet Receipt Notification. If it hasn't been paused,
// the Reset command will be sent after receiving it, on line 292.
- if paused && firmware != nil {
- let _report = report!
+ if let _report = report, paused && firmware != nil {
firmware = nil
range = nil
success = nil
@@ -164,13 +168,12 @@ import CoreBluetooth
onError report: @escaping ErrorCallback) {
if !aborted {
// Support for Buttonless DFU Service
- if buttonlessDfuCharacteristic != nil {
- buttonlessDfuCharacteristic!.enable(onSuccess: success, onError: report)
+ if let buttonlessDfuCharacteristic = buttonlessDfuCharacteristic {
+ buttonlessDfuCharacteristic.enable(onSuccess: success, onError: report)
return
}
// End
- dfuControlPointCharacteristic!.enableNotifications(onSuccess: success,
- onError: report)
+ dfuControlPointCharacteristic?.enableNotifications(onSuccess: success, onError: report)
} else {
sendReset(onError: report)
}
@@ -185,7 +188,7 @@ import CoreBluetooth
func readCommandObjectInfo(onReponse response: @escaping SecureDFUResponseCallback,
onError report: @escaping ErrorCallback) {
if !aborted {
- dfuControlPointCharacteristic!.send(.readCommandObjectInfo,
+ dfuControlPointCharacteristic?.send(.readCommandObjectInfo,
onResponse: response, onError: report)
} else {
sendReset(onError: report)
@@ -201,7 +204,7 @@ import CoreBluetooth
func readDataObjectInfo(onReponse response: @escaping SecureDFUResponseCallback,
onError report: @escaping ErrorCallback) {
if !aborted {
- dfuControlPointCharacteristic!.send(.readDataObjectInfo,
+ dfuControlPointCharacteristic?.send(.readDataObjectInfo,
onResponse: response, onError: report)
} else {
sendReset(onError: report)
@@ -216,11 +219,12 @@ import CoreBluetooth
- parameter report: Method called when an error occurred.
*/
- func createCommandObject(withLength length: UInt32, onSuccess success: @escaping Callback,
+ func createCommandObject(withLength length: UInt32,
+ onSuccess success: @escaping Callback,
onError report: @escaping ErrorCallback) {
if !aborted {
- dfuControlPointCharacteristic!.send(.createCommandObject(withSize: length),
- onSuccess: success, onError:report)
+ dfuControlPointCharacteristic?.send(.createCommandObject(withSize: length),
+ onSuccess: success, onError: report)
} else {
sendReset(onError: report)
}
@@ -233,10 +237,11 @@ import CoreBluetooth
- parameter success: Method called when the object has been created.
- parameter report: Method called when an error occurred.
*/
- func createDataObject(withLength length: UInt32, onSuccess success: @escaping Callback,
+ func createDataObject(withLength length: UInt32,
+ onSuccess success: @escaping Callback,
onError report: @escaping ErrorCallback) {
if !aborted {
- dfuControlPointCharacteristic!.send(.createDataObject(withSize: length),
+ dfuControlPointCharacteristic?.send(.createDataObject(withSize: length),
onSuccess: success, onError:report)
} else {
sendReset(onError: report)
@@ -259,7 +264,8 @@ import CoreBluetooth
} else {
packetReceiptNotificationNumber = newValue
dfuControlPointCharacteristic?.send(.setPacketReceiptNotification(value: newValue),
- onSuccess: {
+ onSuccess: { [weak self] in
+ guard let self = self else { return }
if newValue > 0 {
self.logger.a("Packet Receipt Notif enabled (Op Code = 2, Value = \(newValue))")
} else {
@@ -281,7 +287,7 @@ import CoreBluetooth
func calculateChecksumCommand(onSuccess response: @escaping SecureDFUResponseCallback,
onError report: @escaping ErrorCallback) {
if !aborted {
- dfuControlPointCharacteristic!.send(SecureDFURequest.calculateChecksumCommand,
+ dfuControlPointCharacteristic?.send(SecureDFURequest.calculateChecksumCommand,
onResponse: response, onError: report)
} else {
sendReset(onError: report)
@@ -312,7 +318,7 @@ import CoreBluetooth
private func sendReset(onError report: @escaping ErrorCallback) {
aborted = true
// There is no command to reset a Secure DFU device. We can just disconnect.
- targetPeripheral!.disconnect()
+ targetPeripheral?.disconnect()
}
//MARK: - Packet commands
@@ -325,7 +331,7 @@ import CoreBluetooth
- parameter packetData: Data to be sent as Init Packet.
*/
func sendInitPacket(withdata packetData: Data){
- dfuPacketCharacteristic!.sendInitPacket(packetData)
+ dfuPacketCharacteristic?.sendInitPacket(packetData)
}
/**
@@ -352,8 +358,8 @@ import CoreBluetooth
self.progressDelegate = progressDelegate
self.progressQueue = queue
- self.report = {
- error, message in
+ let _report: ErrorCallback = { [weak self] error, message in
+ guard let self = self else { return }
self.firmware = nil
self.range = nil
self.success = nil
@@ -362,19 +368,25 @@ import CoreBluetooth
self.progressQueue = nil
report(error, message)
}
- self.success = {
+ let _success: Callback = { [weak self] in
+ guard let self = self else { return }
self.firmware = nil
self.range = nil
self.success = nil
self.report = nil
self.progressDelegate = nil
self.progressQueue = nil
- self.dfuControlPointCharacteristic!.peripheralDidReceiveObject()
+ self.dfuControlPointCharacteristic?.peripheralDidReceiveObject()
success()
- } as Callback
+ }
+ self.report = _report
+ self.success = _success
- dfuControlPointCharacteristic!.waitUntilUploadComplete(onSuccess: self.success!,
- onPacketReceiptNofitication: { bytesReceived in
+ dfuControlPointCharacteristic?.waitUntilUploadComplete(
+ onSuccess: _success,
+ onPacketReceiptNofitication: { [weak self] bytesReceived in
+ guard let self = self,
+ let dfuPacketCharacteristic = self.dfuPacketCharacteristic else { return }
// This callback is called from SecureDFUControlPoint in 2 cases: when a PRN
// is received (`bytesReceived` contains number of bytes reported), or when the
// iOS reports the `peripheralIsReady(toSendWriteWithoutResponse:)` callback
@@ -387,12 +399,12 @@ import CoreBluetooth
}
if !self.paused && !self.aborted {
- let bytesSent = self.dfuPacketCharacteristic!.bytesSent + UInt32(range.lowerBound)
+ let bytesSent = dfuPacketCharacteristic.bytesSent + UInt32(range.lowerBound)
if peripheralIsReadyToSendWriteWithoutRequest || bytesSent == bytesReceived! {
- self.dfuPacketCharacteristic!.sendNext(self.packetReceiptNotificationNumber ?? 0,
- packetsFrom: range, of: firmware,
- andReportProgressTo: progressDelegate, on: queue,
- andCompletionTo: self.success!)
+ dfuPacketCharacteristic.sendNext(self.packetReceiptNotificationNumber ?? 0,
+ packetsFrom: range, of: firmware,
+ andReportProgressTo: progressDelegate, on: queue,
+ andCompletionTo: _success)
} else {
// Target device deported invalid number of bytes received
report(.bytesLost, "\(bytesSent) bytes were sent while \(bytesReceived!) bytes were reported as received")
@@ -405,19 +417,21 @@ import CoreBluetooth
self.progressDelegate = nil
self.sendReset(onError: report)
}
- }, onError: self.report!)
+ },
+ onError: _report
+ )
// A new object is started, reset counters before sending the next object.
// It must be done even if the upload was paused, otherwise it would be
// resumed from a wrong place.
- dfuPacketCharacteristic!.resetCounters()
+ dfuPacketCharacteristic?.resetCounters()
if !paused && !aborted {
- // ...and start sending firmware if
- dfuPacketCharacteristic!.sendNext(packetReceiptNotificationNumber ?? 0,
+ // ...and start sending firmware.
+ dfuPacketCharacteristic?.sendNext(packetReceiptNotificationNumber ?? 0,
packetsFrom: range, of: firmware,
andReportProgressTo: progressDelegate, on: queue,
- andCompletionTo: self.success!)
+ andCompletionTo: _success)
} else if aborted {
self.firmware = nil
self.range = nil
@@ -439,9 +453,9 @@ import CoreBluetooth
self.success = nil
self.report = nil
- guard error == nil else {
+ if let error = error {
logger.e("Characteristics discovery failed")
- logger.e(error!)
+ logger.e(error)
_report?(.serviceDiscoveryFailed, "Characteristics discovery failed")
return
}
@@ -562,26 +576,31 @@ import CoreBluetooth
onError report: @escaping ErrorCallback) {
if !aborted {
func enterBootloader() {
+ guard let buttonlessDfuCharacteristic = buttonlessDfuCharacteristic else { return }
// The method above may reset the device before it sents a response to
// the request. We will call the success callback right here.
success()
- self.buttonlessDfuCharacteristic!.send(ButtonlessDFURequest.enterBootloader,
- onSuccess: nil, onError: report)
+ buttonlessDfuCharacteristic.send(ButtonlessDFURequest.enterBootloader,
+ onSuccess: nil, onError: report)
}
// If the device may support setting alternative advertising name in the
// bootloader mode, try it.
- if let name = name, buttonlessDfuCharacteristic!.maySupportSettingName {
+ if let name = name,
+ let buttonlessDfuCharacteristic = buttonlessDfuCharacteristic,
+ buttonlessDfuCharacteristic.maySupportSettingName {
logger.v("Trying setting bootloader name to \(name)")
- buttonlessDfuCharacteristic!.send(ButtonlessDFURequest.set(name: name),
- onSuccess: {
+ buttonlessDfuCharacteristic.send(ButtonlessDFURequest.set(name: name),
+ onSuccess: { [weak self] in
+ guard let self = self else { return }
// Success. The buttonless service is from SDK 14.0+.
// The bootloader, after jumping to it, will advertise with this name.
- self.targetPeripheral!.bootloaderName = name
+ self.targetPeripheral?.bootloaderName = name
self.logger.a("Bootloader name changed successfully")
enterBootloader()
- }, onError: {
- error, message in
+ },
+ onError: { [weak self] error, message in
+ guard let self = self else { return }
if error == .remoteButtonlessDFUOpCodeNotSupported {
// Setting name is not supported. Looks like it's buttonless service
// from SDK 13. We can't rely on bootloader's name.
|