@@ -19,16 +19,25 @@ struct ProductInfo: Codable {
19
19
}
20
20
}
21
21
22
+ enum ConnectionState : String , Codable {
23
+ case discovered
24
+ // from the moment we try to connect until after we are paired (or until either step fails).
25
+ case connecting
26
+ case connected
27
+ // something went wrong, see connectionError.
28
+ case error
29
+ }
30
+
22
31
struct State {
23
32
var bluetoothAvailable : Bool
33
+ var scanning : Bool
24
34
var discoveredPeripherals : [ UUID : PeripheralMetadata ]
25
- // true from the moment we try to connect until after we are paired (or until either step fails).
26
- var connecting : Bool
27
35
}
28
36
29
37
struct PeripheralMetadata {
30
38
let peripheral : CBPeripheral
31
39
let discoveredDate : Date
40
+ var connectionState : ConnectionState
32
41
var connectionError : String ? = nil
33
42
}
34
43
@@ -43,14 +52,18 @@ var pairedDeviceIdentifiers: Set<String> {
43
52
}
44
53
45
54
class BluetoothManager : NSObject , ObservableObject , CBCentralManagerDelegate , CBPeripheralDelegate {
46
- private var state : State = State ( bluetoothAvailable: false , discoveredPeripherals: [ : ] , connecting: false )
55
+ private var state : State = State (
56
+ bluetoothAvailable: false ,
57
+ scanning: false ,
58
+ discoveredPeripherals: [ : ]
59
+ )
47
60
48
61
var centralManager : CBCentralManager !
49
62
var connectedPeripheral : CBPeripheral ?
50
63
var pWriter : CBCharacteristic ?
51
64
var pReader : CBCharacteristic ?
52
65
var pProduct : CBCharacteristic ?
53
-
66
+
54
67
private var isPaired : Bool = false
55
68
56
69
// Peripherals in this set will not be auto-connected even if previously paired.
@@ -73,10 +86,12 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
73
86
}
74
87
75
88
func connect( to peripheralID: UUID ) {
76
- guard let metadata = state. discoveredPeripherals [ peripheralID] else { return }
89
+ guard var metadata = state. discoveredPeripherals [ peripheralID] else { return }
77
90
centralManager. stopScan ( )
78
- state. discoveredPeripherals [ peripheralID] ? . connectionError = nil
79
- state. connecting = true
91
+ metadata. connectionError = nil
92
+ metadata. connectionState = . connecting
93
+ state. discoveredPeripherals [ peripheralID] = metadata
94
+ state. scanning = false
80
95
updateBackendState ( )
81
96
centralManager. connect ( metadata. peripheral, options: nil )
82
97
}
@@ -86,6 +101,7 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
86
101
!centralManager. isScanning,
87
102
connectedPeripheral == nil else { return }
88
103
state. discoveredPeripherals. removeAll ( )
104
+ state. scanning = true
89
105
updateBackendState ( )
90
106
centralManager. scanForPeripherals (
91
107
withServices: [ CBUUID ( string: " e1511a45-f3db-44c0-82b8-6c880790d1f1 " ) ] ,
@@ -114,7 +130,8 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
114
130
if state. discoveredPeripherals [ identifier] == nil {
115
131
state. discoveredPeripherals [ identifier] = PeripheralMetadata (
116
132
peripheral: peripheral,
117
- discoveredDate: Date ( )
133
+ discoveredDate: Date ( ) ,
134
+ connectionState: . discovered
118
135
)
119
136
print ( " BLE: discovered \( peripheral. name ?? " unknown device " ) " )
120
137
if let data = advertisementData [ " kCBAdvDataManufacturerData " ] as? Data {
@@ -149,11 +166,12 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
149
166
150
167
func centralManager( _ central: CBCentralManager , didFailToConnect peripheral: CBPeripheral , error: Error ? ) {
151
168
let errorMessage = error? . localizedDescription ?? " unknown error "
169
+ state. discoveredPeripherals [ peripheral. identifier] ? . connectionState = . error
152
170
state. discoveredPeripherals [ peripheral. identifier] ? . connectionError = errorMessage
153
- state. connecting = false
154
- dontAutoConnectSet. insert ( peripheral. identifier)
155
171
updateBackendState ( )
172
+ dontAutoConnectSet. insert ( peripheral. identifier)
156
173
print ( " BLE: connection failed to \( peripheral. name ?? " unknown device " ) : \( errorMessage) " )
174
+ restartScan ( )
157
175
}
158
176
159
177
func peripheral( _ peripheral: CBPeripheral , didDiscoverServices error: Error ? ) {
@@ -240,29 +258,28 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
240
258
// Add to paired devices
241
259
pairedDeviceIdentifiers. insert ( peripheral. identifier. uuidString)
242
260
}
243
- state. connecting = false
261
+ state. discoveredPeripherals [ peripheral . identifier ] ? . connectionState = . connected
244
262
updateBackendState ( )
245
263
// Invoke device manager to scan now, which will make it detect the device being connected
246
264
// (or disconnected, in case the product string indicates that) now instead of waiting for
247
265
// the next scan.
248
266
MobileserverUsbUpdate ( )
249
267
}
250
268
}
251
-
269
+
252
270
func handleDisconnect( ) {
253
271
connectedPeripheral = nil
254
272
pReader = nil
255
273
pWriter = nil
256
274
pProduct = nil
257
275
state. discoveredPeripherals. removeAll ( )
258
- state. connecting = false
259
- isPaired = false
276
+ isPaired = false
260
277
updateBackendState ( )
261
-
278
+
262
279
// Have the backend scan right away, which will make it detect that we disconnected.
263
280
// Otherwise there would be up to a second of delay (the backend device manager scan interval).
264
281
MobileserverUsbUpdate ( )
265
-
282
+
266
283
restartScan ( )
267
284
}
268
285
@@ -299,7 +316,7 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
299
316
let value = pProduct. value else {
300
317
return nil
301
318
}
302
-
319
+
303
320
if value. isEmpty {
304
321
return nil
305
322
}
@@ -320,25 +337,31 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
320
337
struct PeripheralJSON : Codable {
321
338
let identifier : String
322
339
let name : String
340
+ let connectionState : ConnectionState
323
341
let connectionError : String ?
324
342
}
325
343
326
344
struct StateJSON : Codable {
327
345
let bluetoothAvailable : Bool
346
+ let scanning : Bool
328
347
let peripherals : [ PeripheralJSON ]
329
- let connecting : Bool
330
348
}
331
349
332
350
// Convert discoveredPeripherals to the JSON structure
333
351
let peripherals = Array ( state. discoveredPeripherals. values) . map { metadata in
334
352
PeripheralJSON (
335
353
identifier: metadata. peripheral. identifier. uuidString,
336
354
name: metadata. peripheral. name ?? " BitBox " ,
355
+ connectionState: metadata. connectionState,
337
356
connectionError: metadata. connectionError
338
357
)
339
358
}
340
359
341
- let state = StateJSON ( bluetoothAvailable: state. bluetoothAvailable, peripherals: peripherals, connecting: state. connecting)
360
+ let state = StateJSON (
361
+ bluetoothAvailable: state. bluetoothAvailable,
362
+ scanning: state. scanning,
363
+ peripherals: peripherals
364
+ )
342
365
343
366
do {
344
367
let encoder = JSONEncoder ( )
0 commit comments