Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dispatching delegates in designated queues #273

Merged
merged 3 commits into from
Mar 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cartfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
github "weichsel/ZIPFoundation" ~> 0.9
Original file line number Diff line number Diff line change
Expand Up @@ -44,30 +44,32 @@ internal class DFUServiceSelector : BaseDFUExecutor, DFUStarterPeripheralDelegat
typealias DFUPeripheralType = DFUStarterPeripheral

internal let initiator: DFUServiceInitiator
internal let logger: LoggerHelper
internal let controller: DFUServiceController
internal let peripheral: DFUStarterPeripheral
internal var error: (error: DFUError, message: String)?

init(initiator: DFUServiceInitiator, controller: DFUServiceController) {
self.initiator = initiator
self.logger = LoggerHelper(initiator.logger, initiator.loggerQueue)
self.controller = controller
self.peripheral = DFUStarterPeripheral(initiator)
self.peripheral = DFUStarterPeripheral(initiator, logger)

self.peripheral.delegate = self
}

func start() {
DispatchQueue.main.async(execute: {
self.delegate?.dfuStateDidChange(to: .connecting)
})
delegate {
$0.dfuStateDidChange(to: .connecting)
}
peripheral.start()
}

func peripheralDidSelectedExecutor(_ ExecutorType: DFUExecutorAPI.Type) {
// Release the cyclic reference
peripheral.destroy()

let executor = ExecutorType.init(initiator)
let executor = ExecutorType.init(initiator, logger)
controller.executor = executor
executor.start()
}
Expand Down
23 changes: 21 additions & 2 deletions iOSDFULibrary/Classes/Implementation/DFUServiceInitiator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ import CoreBluetooth
internal var target : CBPeripheral!
internal var file : DFUFirmware?

internal var queue : DispatchQueue
internal var delegateQueue : DispatchQueue
internal var progressDelegateQueue : DispatchQueue
internal var loggerQueue : DispatchQueue

//MARK: - Public variables

/**
Expand Down Expand Up @@ -239,28 +244,42 @@ import CoreBluetooth
// Default UUID helper with standard set of UUIDs
self.uuidHelper = DFUUuidHelper()

self.queue = DispatchQueue.main
self.delegateQueue = DispatchQueue.main
self.progressDelegateQueue = DispatchQueue.main
self.loggerQueue = DispatchQueue.main
super.init()
}

/**
Creates the DFUServiceInitializer that will allow to send an update to peripherals.

- parameter queue: The dispatch queue to run BLE operations on.
- parameter callbackQueue: The dispatch queue to invoke all delegate callbacks on.
- parameter progressQueue: The dispatch queue to invoke all progress delegate callbacks on.
- parameter loggerQueue: The dispatch queue to invoke all logger events on.

- returns: The initiator instance.

- version: Added in version 4.2 of the iOS DFU Library.
- version: Added in version 4.2 of the iOS DFU Library. Extended in 4.3 to allow setting delegate queues.
- seeAlso: peripheralSelector property - a selector used when scanning for a device in DFU Bootloader mode
in case you want to update a Softdevice and Application from a single ZIP Distribution Packet.
*/
@objc public init(queue: DispatchQueue? = nil) {
@objc public init(queue: DispatchQueue? = nil,
delegateQueue: DispatchQueue = DispatchQueue.main,
progressQueue: DispatchQueue = DispatchQueue.main,
loggerQueue: DispatchQueue = DispatchQueue.main) {
// Create a new instance of CBCentralManager
self.centralManager = CBCentralManager(delegate: nil, queue: queue)
// Default peripheral selector will choose the service UUID as a filter
self.peripheralSelector = DFUPeripheralSelector()
// Default UUID helper with standard set of UUIDs
self.uuidHelper = DFUUuidHelper()

self.queue = queue ?? DispatchQueue.main
self.delegateQueue = delegateQueue
self.progressDelegateQueue = progressQueue
self.loggerQueue = loggerQueue
super.init()
}

Expand Down
73 changes: 35 additions & 38 deletions iOSDFULibrary/Classes/Implementation/GenericDFU/DFUExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

import CoreBluetooth

typealias DelegateCallback = (DFUServiceDelegate) -> Void

internal protocol BaseExecutorAPI : class, DFUController {

/**
Expand All @@ -33,28 +35,18 @@ internal protocol BaseExecutorAPI : class, DFUController {
internal protocol BaseDFUExecutor : BaseExecutorAPI, BasePeripheralDelegate {
associatedtype DFUPeripheralType : BaseDFUPeripheralAPI

/// Target peripheral object
/// Target peripheral object.
var peripheral: DFUPeripheralType { get }
/// The DFU Service Initiator instance that was used to start the service.
var initiator: DFUServiceInitiator { get }
/// The optional logger delegate.
var logger: LoggerHelper { get }
/// If an error occurred it is set as this variable. It will be reported to the user when the device gets disconnected.
var error: (error: DFUError, message: String)? { set get }
}

extension BaseDFUExecutor {

/// The service delegate will be informed about status changes and errors.
internal var delegate: DFUServiceDelegate? {
// The delegate may change during DFU operation (by setting a new one in the initiator). Let's always use the current one.
return initiator.delegate
}

/// The progress delegate will be informed about current upload progress.
internal var progressDelegate: DFUProgressDelegate? {
// The delegate may change during DFU operation (by setting a new one in the initiator). Let's always use the current one.
return initiator.progressDelegate
}

// MARK: - DFU Controller API

func pause() -> Bool {
Expand All @@ -72,38 +64,38 @@ extension BaseDFUExecutor {
// MARK: - BasePeripheralDelegate API

func peripheralDidFailToConnect() {
DispatchQueue.main.async(execute: {
self.delegate?.dfuError(.failedToConnect, didOccurWithMessage: "Device failed to connect")
})
delegate {
$0.dfuError(.failedToConnect, didOccurWithMessage: "Device failed to connect")
}
// Release the cyclic reference
peripheral.destroy()
}

func peripheralDidDisconnect() {
// The device is now disconnected. Check if there was an error that needs to be reported now
DispatchQueue.main.async(execute: {
delegate {
if let error = self.error {
self.delegate?.dfuError(error.error, didOccurWithMessage: error.message)
$0.dfuError(error.error, didOccurWithMessage: error.message)
} else {
self.delegate?.dfuError(.deviceDisconnected, didOccurWithMessage: "Device disconnected unexpectedly")
$0.dfuError(.deviceDisconnected, didOccurWithMessage: "Device disconnected unexpectedly")
}
})
}
// Release the cyclic reference
peripheral.destroy()
}

func peripheralDidDisconnect(withError error: Error) {
DispatchQueue.main.async(execute: {
self.delegate?.dfuError(.deviceDisconnected, didOccurWithMessage: "\(error.localizedDescription) (code: \((error as NSError).code))")
})
delegate {
$0.dfuError(.deviceDisconnected, didOccurWithMessage: "\(error.localizedDescription) (code: \((error as NSError).code))")
}
// Release the cyclic reference
peripheral.destroy()
}

func peripheralDidDisconnectAfterAborting() {
DispatchQueue.main.async(execute: {
self.delegate?.dfuStateDidChange(to: .aborted)
})
delegate {
$0.dfuStateDidChange(to: .aborted)
}
// Release the cyclic reference
peripheral.destroy()
}
Expand All @@ -118,21 +110,27 @@ extension BaseDFUExecutor {

// MARK: - Helper functions

func logWith(_ level: LogLevel, message: String) {
initiator.logger?.logWith(level, message: message)
/// Calls the delegate method on the delegate queue given in the initiator.
func delegate(callback: @escaping DelegateCallback) {
if let delegate = initiator.delegate {
initiator.delegateQueue.async {
callback(delegate)
}
}
}
}

// MARK: -

internal protocol DFUExecutorAPI : BaseExecutorAPI {
/// Required constructor
init(_ initiator: DFUServiceInitiator)

/// Required constructor.
init(_ initiator: DFUServiceInitiator, _ logger: LoggerHelper)
}

internal protocol DFUExecutor : DFUExecutorAPI, BaseDFUExecutor, DFUPeripheralDelegate where DFUPeripheralType: DFUPeripheralAPI {

/// The firmware to be sent over-the-air
/// The firmware to be sent over-the-air.
var firmware: DFUFirmware { get }
}

Expand All @@ -144,16 +142,15 @@ extension DFUExecutor {
// Check if there is another part of the firmware that has to be sent
if firmware.hasNextPart() {
firmware.switchToNextPart()
DispatchQueue.main.async(execute: {
self.delegate?.dfuStateDidChange(to: .connecting)
})
delegate {
$0.dfuStateDidChange(to: .connecting)
}
return true
}
// If not, we are done here. Congratulations!
DispatchQueue.main.async(execute: {
// If no, the DFU operation is complete
self.delegate?.dfuStateDidChange(to: .completed)
})
delegate {
$0.dfuStateDidChange(to: .completed)
}

// Release the cyclic reference
peripheral.destroy()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ internal protocol BaseDFUPeripheralAPI : class, DFUController {
internal class BaseDFUPeripheral<TD : BasePeripheralDelegate> : NSObject, BaseDFUPeripheralAPI, CBPeripheralDelegate, CBCentralManagerDelegate {
/// Bluetooth Central Manager used to scan for the peripheral.
internal let centralManager: CBCentralManager
internal let queue: DispatchQueue
/// The DFU Target peripheral.
internal var peripheral: CBPeripheral?
/// The peripheral delegate.
Expand Down Expand Up @@ -86,9 +87,10 @@ internal class BaseDFUPeripheral<TD : BasePeripheralDelegate> : NSObject, BaseDF
/// A flag set when upload has been aborted.
fileprivate var aborted: Bool = false

init(_ initiator: DFUServiceInitiator) {
init(_ initiator: DFUServiceInitiator, _ logger: LoggerHelper) {
self.centralManager = initiator.centralManager
self.logger = LoggerHelper(initiator.logger)
self.queue = initiator.queue
self.logger = logger
self.experimentalButtonlessServiceInSecureDfuEnabled = initiator.enableUnsafeExperimentalButtonlessServiceInSecureDfu
self.uuidHelper = initiator.uuidHelper

Expand Down Expand Up @@ -487,15 +489,15 @@ internal class BaseCommonDFUPeripheral<TD : DFUPeripheralDelegate, TS : DFUServi
internal var newAddressExpected : Bool = false
internal var bootloaderName : String?

override init(_ initiator: DFUServiceInitiator) {
override init(_ initiator: DFUServiceInitiator, _ logger: LoggerHelper) {
self.peripheralSelector = initiator.peripheralSelector
super.init(initiator)
super.init(initiator, logger)
}

// MARK: - Base DFU Peripheral API

override func peripheralDidDiscoverDfuService(_ service: CBService) {
dfuService = DFUServiceType(service, logger, uuidHelper)
dfuService = DFUServiceType(service, logger, uuidHelper, queue)
dfuService!.targetPeripheral = self
dfuService!.discoverCharacteristics(
onSuccess: { self.delegate?.peripheralDidBecomeReady() },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ internal protocol DFUService : DFUController {
/**
Required constructor of a service.
*/
init(_ service: CBService, _ logger: LoggerHelper, _ dfuHelper: DFUUuidHelper)
init(_ service: CBService, _ logger: LoggerHelper, _ dfuHelper: DFUUuidHelper, _ queue: DispatchQueue)

/**
Discovers characteristics in the DFU Service. This method also reads the DFU Version characteristic if such found.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,10 @@ internal class DFUPacket: DFUCharacteristic {
Set to 0 to disable Packet Receipt Notification procedure (not recommended).
- parameter firmware: The firmware to be sent.
- parameter progress: An optional progress delegate.
- parameter queue: The queue to dispatch progress events on.
*/
func sendNext(_ prnValue: UInt16, packetsOf firmware: DFUFirmware, andReportProgressTo progress: DFUProgressDelegate?) {
func sendNext(_ prnValue: UInt16, packetsOf firmware: DFUFirmware,
andReportProgressTo progress: DFUProgressDelegate?, on queue: DispatchQueue) {
// Get the peripheral object
let peripheral = characteristic.service.peripheral

Expand All @@ -153,7 +155,7 @@ internal class DFUPacket: DFUCharacteristic {
lastTime = startTime

// Notify progress delegate that upload has started (0%)
DispatchQueue.main.async(execute: {
queue.async(execute: {
progress?.dfuProgressDidChange(
for: firmware.currentPart,
outOf: firmware.parts,
Expand Down Expand Up @@ -195,7 +197,7 @@ internal class DFUPacket: DFUCharacteristic {
lastTime = now
bytesSentSinceProgessNotification = bytesSent

DispatchQueue.main.async(execute: {
queue.async(execute: {
progress?.dfuProgressDidChange(
for: firmware.currentPart,
outOf: firmware.parts,
Expand Down
Loading