Skip to content

Commit

Permalink
Merge pull request #33 from StarryInternet/capture-only-needed-values
Browse files Browse the repository at this point in the history
Only capture the absolute necessary values inside closures, to avoid improperly caching or reusing of core bluetooth objects
  • Loading branch information
klundberg authored Apr 23, 2024
2 parents eaadfdb + a705e94 commit 2d42a73
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,24 +83,24 @@ public struct CentralManager: Sendable {
}

public func connect(_ peripheral: Peripheral, options: PeripheralConnectionOptions? = nil) -> AnyPublisher<Peripheral, Error> {
Publishers.Merge(
didConnectPeripheral
.filter { $0 == peripheral }
.setFailureType(to: Error.self),
didFailToConnectPeripheral
.filter { p, _ in p == peripheral }
.tryMap { _, error in
throw error ?? CentralManagerError.unknownConnectionFailure
}
)
.prefix(1)
.handleEvents(receiveSubscription: { _ in
_connectToPeripheral(peripheral, options)
}, receiveCancel: {
_cancelPeripheralConnection(peripheral)
})
.shareCurrentValue()
.eraseToAnyPublisher()
Publishers.Merge(
didConnectPeripheral
.filter { [id = peripheral.id] p in p.id == id }
.setFailureType(to: Error.self),
didFailToConnectPeripheral
.filter { [id = peripheral.id] p, _ in p.id == id }
.tryMap { _, error in
throw error ?? CentralManagerError.unknownConnectionFailure
}
)
.prefix(1)
.handleEvents(receiveSubscription: { _ in
_connectToPeripheral(peripheral, options)
}, receiveCancel: {
_cancelPeripheralConnection(peripheral)
})
.shareCurrentValue()
.eraseToAnyPublisher()
}

public func cancelPeripheralConnection(_ peripheral: Peripheral) {
Expand All @@ -117,10 +117,10 @@ public struct CentralManager: Sendable {
public func monitorConnection(for peripheral: Peripheral) -> AnyPublisher<Bool, Never> {
Publishers.Merge(
didConnectPeripheral
.filter { p in p == peripheral }
.filter { [id = peripheral.id] p in p.id == id }
.map { _ in true },
didDisconnectPeripheral
.filter { (p, error) in p == peripheral }
.filter { [id = peripheral.id] p, _ in p.id == id }
.map { _ in false }
)
.eraseToAnyPublisher()
Expand Down
42 changes: 21 additions & 21 deletions Sources/CombineCoreBluetooth/Peripheral/Interface+Peripheral.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,20 +190,20 @@ public struct Peripheral: Sendable {

private func writeValueWithResponse(_ value: Data, for characteristic: CBCharacteristic) -> AnyPublisher<Void, Error> {
didWriteValueForCharacteristic
.filterFirstValueOrThrow(where: {
$0.uuid == characteristic.uuid
})
.map { _ in }
.handleEvents(receiveSubscription: { [_writeValueForCharacteristic] _ in
_writeValueForCharacteristic(value, characteristic, .withResponse)
})
.shareCurrentValue()
.filterFirstValueOrThrow(where: { [uuid = characteristic.uuid] in
$0.uuid == uuid
})
.map { _ in }
.handleEvents(receiveSubscription: { [_writeValueForCharacteristic] _ in
_writeValueForCharacteristic(value, characteristic, .withResponse)
})
.shareCurrentValue()
}

public func setNotifyValue(_ enabled: Bool, for characteristic: CBCharacteristic) -> AnyPublisher<Void, Error> {
didUpdateNotificationState
.filterFirstValueOrThrow(where: {
$0.uuid == characteristic.uuid
.filterFirstValueOrThrow(where: { [uuid = characteristic.uuid] in
$0.uuid == uuid
})
.map { _ in }
.handleEvents(receiveSubscription: { [_setNotifyValue] _ in
Expand All @@ -214,8 +214,8 @@ public struct Peripheral: Sendable {

public func discoverDescriptors(for characteristic: CBCharacteristic) -> AnyPublisher<[CBDescriptor]?, Error> {
didDiscoverDescriptorsForCharacteristic
.filterFirstValueOrThrow(where: {
$0.uuid == characteristic.uuid
.filterFirstValueOrThrow(where: { [uuid = characteristic.uuid] in
$0.uuid == uuid
})
.map(\.descriptors)
.handleEvents(receiveSubscription: { [_discoverDescriptors] _ in
Expand All @@ -226,8 +226,8 @@ public struct Peripheral: Sendable {

public func readValue(for descriptor: CBDescriptor) -> AnyPublisher<Any?, Error> {
didUpdateValueForDescriptor
.filterFirstValueOrThrow(where: {
$0.uuid == descriptor.uuid
.filterFirstValueOrThrow(where: { [uuid = descriptor.uuid] in
$0.uuid == uuid
})
.map(\.value)
.handleEvents(receiveSubscription: { [_readValueForDescriptor] _ in
Expand All @@ -238,8 +238,8 @@ public struct Peripheral: Sendable {

public func writeValue(_ value: Data, for descriptor: CBDescriptor) -> AnyPublisher<Void, Error> {
didWriteValueForDescriptor
.filterFirstValueOrThrow(where: {
$0.uuid == descriptor.uuid
.filterFirstValueOrThrow(where: { [uuid = descriptor.uuid] in
$0.uuid == uuid
})
.map { _ in }
.handleEvents(receiveSubscription: { [_writeValueForDescriptor] _ in
Expand Down Expand Up @@ -292,7 +292,7 @@ public struct Peripheral: Sendable {
discoverCharacteristics(withUUIDs: [characteristicUUID], inServiceWithUUID: serviceUUID)
.tryMap { characteristics in
// assume core bluetooth won't send us a characteristic list without the characteristic we expect
guard let characteristic = characteristics.first(where: { characteristic in characteristic.uuid == characteristicUUID }) else {
guard let characteristic = characteristics.first(where: { c in c.uuid == characteristicUUID }) else {
throw PeripheralError.characteristicNotFound(characteristicUUID)
}
return characteristic
Expand Down Expand Up @@ -405,8 +405,8 @@ public struct Peripheral: Sendable {
public func listenForUpdates(on characteristic: CBCharacteristic) -> AnyPublisher<Data?, Error> {
didUpdateValueForCharacteristic
// not limiting to `.first()` here as callers may want long-lived listening for value changes
.filter({ (readCharacteristic, error) -> Bool in
return readCharacteristic.uuid == characteristic.uuid
.filter({ [uuid = characteristic.uuid] (readCharacteristic, error) -> Bool in
readCharacteristic.uuid == uuid
})
.selectValueOrThrowError()
.map(\.value)
Expand Down Expand Up @@ -438,8 +438,8 @@ public struct Peripheral: Sendable {
self.listenForUpdates(on: characteristic),

didUpdateNotificationState
.filterFirstValueOrThrow(where: {
$0.uuid == characteristic.uuid
.filterFirstValueOrThrow(where: { [uuid = characteristic.uuid] in
$0.uuid == uuid
})
.ignoreOutput(setOutputType: Data?.self)
)
Expand Down

0 comments on commit 2d42a73

Please sign in to comment.