Skip to content

Commit c67209f

Browse files
authored
[Feature] Revisit concurrency (#69)
* Leave Combine out of concurrency * Safeguard against CB adding Sendable conformance to CBUUID * Remove @MainActors * Update README
1 parent 69c6534 commit c67209f

File tree

7 files changed

+29
-41
lines changed

7 files changed

+29
-41
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ try await centralManager.connect(peripheral, options: nil)
4545
The central manager publishes several events. You can subscribe to them by using the `eventPublisher`.
4646

4747
```swift
48-
await centralManager.eventPublisher
48+
centralManager.eventPublisher
4949
.sink {
5050
switch $0 {
5151
case .didConnectPeripheral(let peripheral):
@@ -92,7 +92,7 @@ To get notified when a characteristic's value is updated, we provide a publisher
9292

9393
```swift
9494
let characteristicUUID = CBUUID()
95-
await peripheral.characteristicValueUpdatedPublisher
95+
peripheral.characteristicValueUpdatedPublisher
9696
.filter { $0.characteristic.uuid == characteristicUUID }
9797
.map { try? $0.parsedValue() as String? } // replace `String?` with your type
9898
.sink { value in
@@ -129,7 +129,7 @@ fetchTask.cancel()
129129
There might also be cases were you want to stop awaiting for all responses. For example, when bluetooth has been powered off. This can be done like so:
130130

131131
```swift
132-
await centralManager.eventPublisher
132+
centralManager.eventPublisher
133133
.sink {
134134
switch $0 {
135135
case .didUpdateState(let state):

Sources/CentralManager/CentralManager.swift

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ public final class CentralManager: Sendable {
3232
}
3333
}
3434

35-
@MainActor
3635
public var eventPublisher: AnyPublisher<CentralManagerEvent, Never> {
37-
get async {
38-
await self.context.eventSubject.eraseToAnyPublisher()
36+
get {
37+
self.context.eventSubject.eraseToAnyPublisher()
3938
}
4039
}
4140

@@ -241,8 +240,8 @@ extension CentralManager.DelegateWrapper: CBCentralManagerDelegate {
241240
}()
242241

243242
func centralManagerDidUpdateState(_ central: CBCentralManager) {
244-
Task { @MainActor in
245-
await self.context.eventSubject.send(.didUpdateState(state: central.state))
243+
Task {
244+
self.context.eventSubject.send(.didUpdateState(state: central.state))
246245

247246
guard let isBluetoothReadyResult = Utils.isBluetoothReady(central.state) else { return }
248247

@@ -251,9 +250,7 @@ extension CentralManager.DelegateWrapper: CBCentralManagerDelegate {
251250
}
252251

253252
func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
254-
Task { @MainActor in
255-
await self.context.eventSubject.send(.willRestoreState(state: dict))
256-
}
253+
self.context.eventSubject.send(.willRestoreState(state: dict))
257254
}
258255

259256
func centralManager(
@@ -281,7 +278,7 @@ extension CentralManager.DelegateWrapper: CBCentralManagerDelegate {
281278
}
282279

283280
func centralManager(_ cbCentralManager: CBCentralManager, didConnect peripheral: CBPeripheral) {
284-
Task { @MainActor in
281+
Task {
285282
Self.logger.info("Connected to peripheral \(peripheral.identifier)")
286283

287284
do {
@@ -292,7 +289,7 @@ extension CentralManager.DelegateWrapper: CBCentralManagerDelegate {
292289
Self.logger.info("Received onDidConnect without a continuation")
293290
}
294291

295-
await self.context.eventSubject.send(
292+
self.context.eventSubject.send(
296293
.didConnectPeripheral(peripheral: Peripheral(peripheral))
297294
)
298295
}
@@ -315,14 +312,9 @@ extension CentralManager.DelegateWrapper: CBCentralManagerDelegate {
315312
break
316313
}
317314

318-
Task { @MainActor in
319-
await self.context.eventSubject.send(
320-
.connectionEventDidOccur(
321-
connectionEvent: event,
322-
peripheral: peripheral
323-
)
324-
)
325-
}
315+
self.context.eventSubject.send(
316+
.connectionEventDidOccur(connectionEvent: event, peripheral: peripheral)
317+
)
326318
}
327319
#endif
328320

@@ -353,7 +345,7 @@ extension CentralManager.DelegateWrapper: CBCentralManagerDelegate {
353345
isReconnecting: Bool,
354346
error: Error?
355347
) {
356-
Task { @MainActor in
348+
Task {
357349
do {
358350
let result = CallbackUtils.result(for: (), error: error)
359351
try await self.context.cancelPeripheralConnectionExecutor.setWorkCompletedForKey(
@@ -364,7 +356,7 @@ extension CentralManager.DelegateWrapper: CBCentralManagerDelegate {
364356
Self.logger.info("Disconnected from \(peripheral.identifier) without a continuation")
365357
}
366358

367-
await self.context.eventSubject.send(
359+
self.context.eventSubject.send(
368360
.didDisconnectPeripheral(peripheral: Peripheral(peripheral), isReconnecting: isReconnecting, error: error)
369361
)
370362
}
@@ -375,7 +367,7 @@ extension CentralManager.DelegateWrapper: CBCentralManagerDelegate {
375367
didDisconnectPeripheral peripheral: CBPeripheral,
376368
error: Error?
377369
) {
378-
Task { @MainActor in
370+
Task {
379371
do {
380372
let result = CallbackUtils.result(for: (), error: error)
381373
try await self.context.cancelPeripheralConnectionExecutor.setWorkCompletedForKey(
@@ -386,7 +378,7 @@ extension CentralManager.DelegateWrapper: CBCentralManagerDelegate {
386378
Self.logger.info("Disconnected from \(peripheral.identifier) without a continuation")
387379
}
388380

389-
await self.context.eventSubject.send(
381+
self.context.eventSubject.send(
390382
.didDisconnectPeripheral(peripheral: Peripheral(peripheral), isReconnecting: false, error: error)
391383
)
392384
}

Sources/CentralManager/CentralManagerContext.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ actor CentralManagerContext {
2626

2727
private(set) lazy var scanForPeripheralsContext = ScanForPeripheralsContext { [weak self] isScanning in
2828
Task { [weak self] in
29-
await self?.updateIsScanning(isScanning)
29+
self?.updateIsScanning(isScanning)
3030
}
3131
}
3232

33-
private(set) lazy var eventSubject = PassthroughSubject<CentralManagerEvent, Never>()
33+
nonisolated private(set) lazy var eventSubject = PassthroughSubject<CentralManagerEvent, Never>()
3434

3535
private(set) lazy var waitUntilReadyExecutor = {
3636
let executor = AsyncSerialExecutor<Void>()

Sources/Peripheral/Peripheral.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,16 @@ public final class Peripheral: Sendable {
1414
}
1515

1616
/// Publishes characteristics that are notifying of value changes.
17-
@MainActor
1817
public var characteristicValueUpdatedPublisher: AnyPublisher<CharacteristicValueUpdateEventData, Never> {
19-
get async {
20-
await self.context.characteristicValueUpdatedSubject.eraseToAnyPublisher()
18+
get {
19+
self.context.characteristicValueUpdatedSubject.eraseToAnyPublisher()
2120
}
2221
}
2322

2423
/// Publishes when the services of the peripheral has changed.
25-
@MainActor
2624
public var invalidatedServices: AnyPublisher<[Service], Never> {
27-
get async {
28-
await self.context.invalidatedServicesSubject.eraseToAnyPublisher()
25+
get {
26+
self.context.invalidatedServicesSubject.eraseToAnyPublisher()
2927
}
3028
}
3129

Sources/Peripheral/PeripheralContext.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import Combine
66

77
/// Contains the objects necessary to track a Peripheral's commands.
88
actor PeripheralContext {
9-
private(set) lazy var characteristicValueUpdatedSubject = PassthroughSubject<CharacteristicValueUpdateEventData, Never>()
10-
private(set) lazy var invalidatedServicesSubject = PassthroughSubject<[Service], Never>()
9+
nonisolated private(set) lazy var characteristicValueUpdatedSubject = PassthroughSubject<CharacteristicValueUpdateEventData, Never>()
10+
nonisolated private(set) lazy var invalidatedServicesSubject = PassthroughSubject<[Service], Never>()
1111

1212
private(set) lazy var readRSSIExecutor = {
1313
let executor = AsyncSerialExecutor<NSNumber>()

Sources/Peripheral/PeripheralDelegate.swift

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ extension PeripheralDelegate: CBPeripheralDelegate {
6767
func peripheral(_ cbPeripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
6868
let eventData = CharacteristicValueUpdateEventData(characteristic: Characteristic(characteristic))
6969

70-
Task { @MainActor in
70+
Task {
7171
if characteristic.isNotifying {
72-
await self.context.characteristicValueUpdatedSubject.send(eventData)
72+
self.context.characteristicValueUpdatedSubject.send(eventData)
7373
}
7474

7575
do {
@@ -169,8 +169,6 @@ extension PeripheralDelegate: CBPeripheralDelegate {
169169
}
170170

171171
func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService]) {
172-
Task { @MainActor in
173-
await self.context.invalidatedServicesSubject.send(invalidatedServices.map { Service($0) })
174-
}
172+
self.context.invalidatedServicesSubject.send(invalidatedServices.map { Service($0) })
175173
}
176174
}

Sources/Utils/Extensions/CBUUID+Sendable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
import Foundation
44
import CoreBluetooth
55

6-
extension CBUUID: @unchecked Sendable {}
6+
extension CBUUID: @unchecked @retroactive Sendable {}

0 commit comments

Comments
 (0)