Skip to content

Commit da549ff

Browse files
committed
Properly return an error instead of failing silently when we cannot write without response to a characteristic that doesnt support write without response
1 parent 2feac94 commit da549ff

2 files changed

Lines changed: 35 additions & 14 deletions

File tree

CentralPeripheralDemo/CentralView.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ struct CentralView: View {
119119
if let error = demo.connectError {
120120
Text("Error: \(String(describing: error))")
121121
}
122+
}
122123

124+
Section("Discovered peripherals") {
123125
ForEach(demo.peripherals) { discovery in
124126
Button(discovery.peripheral.name ?? "<nil>") {
125127
demo.connect(discovery)
@@ -217,7 +219,11 @@ struct PeripheralDeviceView: View {
217219
case let .success(value)?:
218220
Text("Wrote at \(String(describing: value))")
219221
case let .failure(error)?:
220-
Text("Error: \(String(describing: error))")
222+
if let error = error as? LocalizedError, let errorDescription = error.errorDescription {
223+
Text("Error: \(errorDescription)")
224+
} else {
225+
Text("Error: \(String(describing: error))")
226+
}
221227
case nil:
222228
EmptyView()
223229
}

Sources/CombineCoreBluetooth/Peripheral/Live+Peripheral.swift

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -117,24 +117,39 @@ extension Peripheral {
117117

118118
_writeValueForCharacteristic: { (value, characteristic, writeType) in
119119
if writeType == .withoutResponse {
120-
// Return an empty publisher here, since we never expect to receive a response when writing using a .withoutResponse type. This will ignore errors we might get, but that will be resolved in a later version.
121-
return Empty()
120+
if characteristic.properties.contains(.writeWithoutResponse) {
121+
// Return an empty publisher here, since we never expect to receive a response.
122+
return Empty()
123+
.handleEvents(receiveSubscription: { (sub) in
124+
cbperipheral.writeValue(value, for: characteristic, type: writeType)
125+
})
126+
.eraseToAnyPublisher()
127+
} else {
128+
// a responseless-write against a characteristic that doesn't support a respoonse is silently ignored
129+
// by core bluetooth and never sends to the peripheral, so surface that case with an error.
130+
return Fail(
131+
error: NSError(
132+
domain: CBATTErrorDomain,
133+
code: CBATTError.writeNotPermitted.rawValue,
134+
userInfo: [
135+
NSLocalizedDescriptionKey: "Writing without response is not permitted."
136+
]
137+
)
138+
)
139+
.eraseToAnyPublisher()
140+
}
141+
} else {
142+
return delegate
143+
.didWriteValueForCharacteristic
144+
.filterFirstValueOrThrow(where: {
145+
$0.uuid == characteristic.uuid
146+
})
122147
.handleEvents(receiveSubscription: { (sub) in
123148
cbperipheral.writeValue(value, for: characteristic, type: writeType)
124149
})
150+
.shareCurrentValue()
125151
.eraseToAnyPublisher()
126152
}
127-
128-
return delegate
129-
.didWriteValueForCharacteristic
130-
.filterFirstValueOrThrow(where: {
131-
$0.uuid == characteristic.uuid
132-
})
133-
.handleEvents(receiveSubscription: { (sub) in
134-
cbperipheral.writeValue(value, for: characteristic, type: writeType)
135-
})
136-
.shareCurrentValue()
137-
.eraseToAnyPublisher()
138153
},
139154

140155
_setNotifyValue: { (enabled, characteristic) in

0 commit comments

Comments
 (0)