munim-bluetooth 0.3.2 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,14 +14,14 @@ class HybridMunimBluetooth: HybridMunimBluetoothSpec {
14
14
  // Peripheral Manager
15
15
  private var peripheralManager: CBPeripheralManager?
16
16
  private var peripheralServices: [CBMutableService] = []
17
- private var currentAdvertisingData: [String: Any] = [:]
17
+ private var currentAdvertisingData: AdvertisingDataTypes?
18
18
 
19
19
  // Central Manager
20
20
  private var centralManager: CBCentralManager?
21
21
  private var discoveredPeripherals: [String: CBPeripheral] = [:]
22
22
  private var connectedPeripherals: [String: CBPeripheral] = [:]
23
23
  private var peripheralCharacteristics: [String: [CBCharacteristic]] = [:]
24
- private var scanOptions: [String: Any]?
24
+ private var scanOptions: ScanOptions?
25
25
  private var isScanning = false
26
26
 
27
27
  // Event emitter
@@ -36,7 +36,7 @@ class HybridMunimBluetooth: HybridMunimBluetoothSpec {
36
36
 
37
37
  // MARK: - Peripheral Features
38
38
 
39
- func startAdvertising(options: [String: Any]) throws {
39
+ override func startAdvertising(options: AdvertisingOptions) throws {
40
40
  guard let peripheralManager = peripheralManager,
41
41
  peripheralManager.state == .poweredOn else {
42
42
  throw NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Bluetooth is not powered on"])
@@ -44,31 +44,33 @@ class HybridMunimBluetooth: HybridMunimBluetoothSpec {
44
44
 
45
45
  var advertisingData: [String: Any] = [:]
46
46
 
47
- // Process comprehensive advertising data
48
- if let advertisingDataDict = options["advertisingData"] as? [String: Any] {
49
- processAdvertisingData(advertisingDataDict, into: &advertisingData)
47
+ // Service UUIDs
48
+ if !options.serviceUUIDs.isEmpty {
49
+ let uuids = options.serviceUUIDs.compactMap { CBUUID(string: $0) }
50
+ advertisingData[CBAdvertisementDataServiceUUIDsKey] = uuids
50
51
  }
51
52
 
52
- // Legacy support
53
- if let localName = options["localName"] as? String {
53
+ // Local name
54
+ if let localName = options.localName {
54
55
  advertisingData[CBAdvertisementDataLocalNameKey] = localName
55
56
  }
56
57
 
57
- if let serviceUUIDs = options["serviceUUIDs"] as? [String], !serviceUUIDs.isEmpty {
58
- let uuids = serviceUUIDs.compactMap { CBUUID(string: $0) }
59
- advertisingData[CBAdvertisementDataServiceUUIDsKey] = uuids
60
- }
61
-
62
- if let manufacturerData = options["manufacturerData"] as? String,
58
+ // Manufacturer data
59
+ if let manufacturerData = options.manufacturerData,
63
60
  let data = hexStringToData(manufacturerData) {
64
61
  advertisingData[CBAdvertisementDataManufacturerDataKey] = data
65
62
  }
66
63
 
67
- currentAdvertisingData = advertisingData
64
+ // Advertising data
65
+ if let advertisingDataTypes = options.advertisingData {
66
+ processAdvertisingData(advertisingDataTypes, into: &advertisingData)
67
+ }
68
+
69
+ currentAdvertisingData = options.advertisingData
68
70
  peripheralManager.startAdvertising(advertisingData as? [String: Any])
69
71
  }
70
72
 
71
- func updateAdvertisingData(advertisingData: [String: Any]) throws {
73
+ override func updateAdvertisingData(advertisingData: AdvertisingDataTypes) throws {
72
74
  guard let peripheralManager = peripheralManager,
73
75
  peripheralManager.state == .poweredOn else {
74
76
  throw NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Bluetooth is not powered on"])
@@ -79,361 +81,353 @@ class HybridMunimBluetooth: HybridMunimBluetoothSpec {
79
81
  var newAdvertisingData: [String: Any] = [:]
80
82
  processAdvertisingData(advertisingData, into: &newAdvertisingData)
81
83
 
82
- currentAdvertisingData = newAdvertisingData
84
+ currentAdvertisingData = advertisingData
83
85
  peripheralManager.startAdvertising(newAdvertisingData as? [String: Any])
84
86
  }
85
87
 
86
- func getAdvertisingData() throws -> [String: Any] {
87
- return currentAdvertisingData
88
+ override func getAdvertisingData() throws -> Promise<AdvertisingDataTypes> {
89
+ return Promise { resolver in
90
+ resolver.resolve(self.currentAdvertisingData ?? AdvertisingDataTypes())
91
+ }
88
92
  }
89
93
 
90
- func stopAdvertising() throws {
94
+ override func stopAdvertising() throws {
91
95
  peripheralManager?.stopAdvertising()
92
- currentAdvertisingData = [:]
96
+ currentAdvertisingData = nil
93
97
  }
94
98
 
95
- func setServices(services: [[String: Any]]) throws {
99
+ override func setServices(services: [GATTService]) throws {
96
100
  peripheralServices.removeAll()
97
101
 
98
- for serviceDict in services {
99
- guard let uuidString = serviceDict["uuid"] as? String else { continue }
100
-
101
- let serviceUUID = CBUUID(string: uuidString)
102
- let service = CBMutableService(type: serviceUUID, primary: true)
102
+ for service in services {
103
+ let serviceUUID = CBUUID(string: service.uuid)
104
+ let mutableService = CBMutableService(type: serviceUUID, primary: true)
103
105
 
104
106
  var characteristics: [CBMutableCharacteristic] = []
105
107
 
106
- if let characteristicsArray = serviceDict["characteristics"] as? [[String: Any]] {
107
- for charDict in characteristicsArray {
108
- guard let charUUIDString = charDict["uuid"] as? String else { continue }
109
-
110
- let charUUID = CBUUID(string: charUUIDString)
111
- var properties: CBCharacteristicProperties = []
112
-
113
- if let propertiesArray = charDict["properties"] as? [String] {
114
- for prop in propertiesArray {
115
- switch prop {
116
- case "read":
117
- properties.insert(.read)
118
- case "write":
119
- properties.insert(.write)
120
- case "notify":
121
- properties.insert(.notify)
122
- case "indicate":
123
- properties.insert(.indicate)
124
- default:
125
- break
126
- }
127
- }
108
+ for characteristic in service.characteristics {
109
+ let charUUID = CBUUID(string: characteristic.uuid)
110
+
111
+ var properties: CBCharacteristicProperties = []
112
+ for prop in characteristic.properties {
113
+ switch prop {
114
+ case "read":
115
+ properties.insert(.read)
116
+ case "write":
117
+ properties.insert(.write)
118
+ case "writeWithoutResponse":
119
+ properties.insert(.writeWithoutResponse)
120
+ case "notify":
121
+ properties.insert(.notify)
122
+ case "indicate":
123
+ properties.insert(.indicate)
124
+ default:
125
+ break
128
126
  }
129
-
130
- var value: Data?
131
- if let valueString = charDict["value"] as? String {
132
- value = valueString.data(using: .utf8)
127
+ }
128
+
129
+ var value: Data?
130
+ if let valueString = characteristic.value {
131
+ value = hexStringToData(valueString)
132
+ if !properties.contains(.read) {
133
133
  properties.insert(.read)
134
134
  }
135
-
136
- let permissions: CBAttributePermissions = value != nil ? .readable : [.readable, .writeable]
137
-
138
- let characteristic = CBMutableCharacteristic(
139
- type: charUUID,
140
- properties: properties,
141
- value: value,
142
- permissions: permissions
143
- )
144
-
145
- characteristics.append(characteristic)
146
135
  }
136
+
137
+ let permissions: CBAttributePermissions = value != nil ? .readable : [.readable, .writeable]
138
+
139
+ let mutableChar = CBMutableCharacteristic(
140
+ type: charUUID,
141
+ properties: properties,
142
+ value: value,
143
+ permissions: permissions
144
+ )
145
+
146
+ characteristics.append(mutableChar)
147
147
  }
148
148
 
149
- service.characteristics = characteristics
150
- peripheralServices.append(service)
149
+ mutableService.characteristics = characteristics
150
+ peripheralServices.append(mutableService)
151
151
  }
152
152
 
153
- peripheralManager?.removeAllServices()
154
153
  for service in peripheralServices {
155
154
  peripheralManager?.add(service)
156
155
  }
157
156
  }
158
157
 
159
- // MARK: - Central Features
158
+ // MARK: - Central/Manager Features
160
159
 
161
- func isBluetoothEnabled() throws -> Bool {
162
- guard let centralManager = centralManager else { return false }
163
- return centralManager.state == .poweredOn
160
+ override func isBluetoothEnabled() throws -> Promise<Bool> {
161
+ return Promise { resolver in
162
+ let isEnabled = self.centralManager?.state == .poweredOn
163
+ resolver.resolve(isEnabled ?? false)
164
+ }
164
165
  }
165
166
 
166
- func requestBluetoothPermission() throws -> Bool {
167
- guard let centralManager = centralManager else { return false }
168
- // On iOS, permission is requested automatically when creating CBCentralManager
169
- // The state will be .unauthorized if permission is denied
170
- return centralManager.state != .unauthorized
167
+ override func requestBluetoothPermission() throws -> Promise<Bool> {
168
+ return Promise { resolver in
169
+ // In iOS, permissions are handled by CBPeripheralManager/CBCentralManager
170
+ resolver.resolve(true)
171
+ }
171
172
  }
172
173
 
173
- func startScan(options: [String: Any]?) throws {
174
+ override func startScan(options: ScanOptions?) throws {
174
175
  guard let centralManager = centralManager,
175
176
  centralManager.state == .poweredOn else {
176
177
  throw NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Bluetooth is not powered on"])
177
178
  }
178
179
 
179
- guard !isScanning else { return }
180
-
181
180
  scanOptions = options
182
181
  isScanning = true
183
- discoveredPeripherals.removeAll()
184
182
 
185
- var serviceUUIDs: [CBUUID]?
186
- if let serviceUUIDsArray = options?["serviceUUIDs"] as? [String] {
187
- serviceUUIDs = serviceUUIDsArray.compactMap { CBUUID(string: $0) }
183
+ var scanOptions: [String: Any] = [:]
184
+ if let options = options {
185
+ scanOptions[CBCentralManagerScanOptionAllowDuplicatesKey] = options.allowDuplicates ?? false
188
186
  }
189
187
 
190
- centralManager.scanForPeripherals(withServices: serviceUUIDs, options: [
191
- CBCentralManagerScanOptionAllowDuplicatesKey: options?["allowDuplicates"] as? Bool ?? false
192
- ])
188
+ centralManager.scanForPeripherals(withServices: nil, options: scanOptions as [String : Any])
193
189
  }
194
190
 
195
- func stopScan() throws {
196
- guard isScanning else { return }
191
+ override func stopScan() throws {
197
192
  centralManager?.stopScan()
198
193
  isScanning = false
199
- scanOptions = nil
200
194
  }
201
195
 
202
- func connect(deviceId: String) throws {
203
- guard let peripheral = discoveredPeripherals[deviceId] else {
204
- throw NSError(domain: "MunimBluetooth", code: 2, userInfo: [NSLocalizedDescriptionKey: "Device not found"])
196
+ override func connect(deviceId: String) throws -> Promise<Void> {
197
+ return Promise { resolver in
198
+ guard let peripheral = self.discoveredPeripherals[deviceId] else {
199
+ resolver.reject(NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Device not found"]))
200
+ return
201
+ }
202
+
203
+ self.centralManager?.connect(peripheral, options: nil)
204
+ self.connectedPeripherals[deviceId] = peripheral
205
+ resolver.resolve(())
205
206
  }
206
-
207
- centralManager?.connect(peripheral, options: nil)
208
207
  }
209
208
 
210
- func disconnect(deviceId: String) throws {
211
- guard let peripheral = connectedPeripherals[deviceId] ?? discoveredPeripherals[deviceId] else {
212
- return
213
- }
214
-
209
+ override func disconnect(deviceId: String) throws {
210
+ guard let peripheral = connectedPeripherals[deviceId] else { return }
215
211
  centralManager?.cancelPeripheralConnection(peripheral)
212
+ connectedPeripherals.removeValue(forKey: deviceId)
216
213
  }
217
214
 
218
- func discoverServices(deviceId: String) throws -> [[String: Any]] {
219
- guard let peripheral = connectedPeripherals[deviceId] else {
220
- throw NSError(domain: "MunimBluetooth", code: 2, userInfo: [NSLocalizedDescriptionKey: "Device not connected"])
215
+ override func discoverServices(deviceId: String) throws -> Promise<[GATTService]> {
216
+ return Promise { resolver in
217
+ guard let peripheral = self.connectedPeripherals[deviceId] else {
218
+ resolver.reject(NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Device not connected"]))
219
+ return
220
+ }
221
+
222
+ peripheral.discoverServices(nil)
223
+ resolver.resolve([])
221
224
  }
222
-
223
- peripheral.discoverServices(nil)
224
-
225
- // Wait for services to be discovered (in real implementation, this would be async)
226
- // For now, return empty array - services will be discovered via delegate callbacks
227
- return []
228
225
  }
229
226
 
230
- func readCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws -> [String: Any] {
231
- guard let peripheral = connectedPeripherals[deviceId] else {
232
- throw NSError(domain: "MunimBluetooth", code: 2, userInfo: [NSLocalizedDescriptionKey: "Device not connected"])
233
- }
234
-
235
- guard let characteristics = peripheralCharacteristics[deviceId],
236
- let characteristic = characteristics.first(where: {
237
- $0.service?.uuid == CBUUID(string: serviceUUID) &&
238
- $0.uuid == CBUUID(string: characteristicUUID)
239
- }) else {
240
- throw NSError(domain: "MunimBluetooth", code: 3, userInfo: [NSLocalizedDescriptionKey: "Characteristic not found"])
227
+ override func readCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws -> Promise<CharacteristicValue> {
228
+ return Promise { resolver in
229
+ resolver.reject(NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Not implemented"]))
241
230
  }
242
-
243
- peripheral.readValue(for: characteristic)
244
-
245
- // Return empty for now - value will come via delegate callback
246
- return [
247
- "value": "",
248
- "serviceUUID": serviceUUID,
249
- "characteristicUUID": characteristicUUID
250
- ]
251
231
  }
252
232
 
253
- func writeCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String, value: String, writeType: String?) throws {
254
- guard let peripheral = connectedPeripherals[deviceId] else {
255
- throw NSError(domain: "MunimBluetooth", code: 2, userInfo: [NSLocalizedDescriptionKey: "Device not connected"])
256
- }
257
-
258
- guard let characteristics = peripheralCharacteristics[deviceId],
259
- let characteristic = characteristics.first(where: {
260
- $0.service?.uuid == CBUUID(string: serviceUUID) &&
261
- $0.uuid == CBUUID(string: characteristicUUID)
262
- }) else {
263
- throw NSError(domain: "MunimBluetooth", code: 3, userInfo: [NSLocalizedDescriptionKey: "Characteristic not found"])
233
+ override func writeCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String, value: String, writeType: WriteType?) throws -> Promise<Void> {
234
+ return Promise { resolver in
235
+ resolver.reject(NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Not implemented"]))
264
236
  }
265
-
266
- guard let data = hexStringToData(value) else {
267
- throw NSError(domain: "MunimBluetooth", code: 4, userInfo: [NSLocalizedDescriptionKey: "Invalid hex string"])
268
- }
269
-
270
- let type: CBCharacteristicWriteType = (writeType == "writeWithoutResponse") ? .withoutResponse : .withResponse
271
- peripheral.writeValue(data, for: characteristic, type: type)
272
237
  }
273
238
 
274
- func subscribeToCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws {
275
- guard let peripheral = connectedPeripherals[deviceId] else {
276
- throw NSError(domain: "MunimBluetooth", code: 2, userInfo: [NSLocalizedDescriptionKey: "Device not connected"])
277
- }
278
-
279
- guard let characteristics = peripheralCharacteristics[deviceId],
280
- let characteristic = characteristics.first(where: {
281
- $0.service?.uuid == CBUUID(string: serviceUUID) &&
282
- $0.uuid == CBUUID(string: characteristicUUID)
283
- }) else {
284
- throw NSError(domain: "MunimBluetooth", code: 3, userInfo: [NSLocalizedDescriptionKey: "Characteristic not found"])
285
- }
286
-
287
- peripheral.setNotifyValue(true, for: characteristic)
239
+ override func subscribeToCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws {
240
+ // Not implemented
288
241
  }
289
242
 
290
- func unsubscribeFromCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws {
291
- guard let peripheral = connectedPeripherals[deviceId] else {
292
- throw NSError(domain: "MunimBluetooth", code: 2, userInfo: [NSLocalizedDescriptionKey: "Device not connected"])
293
- }
294
-
295
- guard let characteristics = peripheralCharacteristics[deviceId],
296
- let characteristic = characteristics.first(where: {
297
- $0.service?.uuid == CBUUID(string: serviceUUID) &&
298
- $0.uuid == CBUUID(string: characteristicUUID)
299
- }) else {
300
- throw NSError(domain: "MunimBluetooth", code: 3, userInfo: [NSLocalizedDescriptionKey: "Characteristic not found"])
301
- }
302
-
303
- peripheral.setNotifyValue(false, for: characteristic)
243
+ override func unsubscribeFromCharacteristic(deviceId: String, serviceUUID: String, characteristicUUID: String) throws {
244
+ // Not implemented
304
245
  }
305
246
 
306
- func getConnectedDevices() throws -> [String] {
307
- return Array(connectedPeripherals.keys)
247
+ override func getConnectedDevices() throws -> Promise<[String]> {
248
+ return Promise { resolver in
249
+ resolver.resolve(Array(self.connectedPeripherals.keys))
250
+ }
308
251
  }
309
252
 
310
- func readRSSI(deviceId: String) throws -> Double {
311
- guard let peripheral = connectedPeripherals[deviceId] else {
312
- throw NSError(domain: "MunimBluetooth", code: 2, userInfo: [NSLocalizedDescriptionKey: "Device not connected"])
253
+ override func readRSSI(deviceId: String) throws -> Promise<Double> {
254
+ return Promise { resolver in
255
+ guard let peripheral = self.connectedPeripherals[deviceId] else {
256
+ resolver.reject(NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Device not connected"]))
257
+ return
258
+ }
259
+
260
+ peripheral.readRSSI()
261
+ resolver.resolve(0)
313
262
  }
314
-
315
- peripheral.readRSSI()
316
- // RSSI will come via delegate callback
317
- return 0.0
318
263
  }
319
264
 
320
- // MARK: - Event Management
321
-
322
- func addListener(eventName: String) throws {
323
- // Event listeners are handled by the event emitter
324
- // This is a no-op as Nitro modules handle events differently
265
+ override func addListener(eventName: String) throws {
266
+ // Event management
325
267
  }
326
268
 
327
- func removeListeners(count: Double) throws {
328
- // Event listeners are handled by the event emitter
329
- // This is a no-op as Nitro modules handle events differently
269
+ override func removeListeners(count: Double) throws {
270
+ // Event management
330
271
  }
331
272
 
332
273
  // MARK: - Helper Methods
333
274
 
334
- private func processAdvertisingData(_ dataDict: [String: Any], into advertisingData: inout [String: Any]) {
335
- // Flags
336
- if let flags = dataDict["flags"] as? Int {
337
- let isConnectable = (flags & 0x02) != 0
338
- advertisingData[CBAdvertisementDataIsConnectable] = isConnectable
275
+ private func hexStringToData(_ hex: String) -> Data? {
276
+ var data = Data()
277
+ var hex = hex
278
+
279
+ if hex.count % 2 != 0 {
280
+ hex = "0" + hex
339
281
  }
340
282
 
341
- // Service UUIDs
342
- addServiceUUIDs(dataDict["incompleteServiceUUIDs16"], to: &advertisingData, key: CBAdvertisementDataServiceUUIDsKey)
343
- addServiceUUIDs(dataDict["completeServiceUUIDs16"], to: &advertisingData, key: CBAdvertisementDataServiceUUIDsKey)
344
- addServiceUUIDs(dataDict["incompleteServiceUUIDs32"], to: &advertisingData, key: CBAdvertisementDataServiceUUIDsKey)
345
- addServiceUUIDs(dataDict["completeServiceUUIDs32"], to: &advertisingData, key: CBAdvertisementDataServiceUUIDsKey)
346
- addServiceUUIDs(dataDict["incompleteServiceUUIDs128"], to: &advertisingData, key: CBAdvertisementDataServiceUUIDsKey)
347
- addServiceUUIDs(dataDict["completeServiceUUIDs128"], to: &advertisingData, key: CBAdvertisementDataServiceUUIDsKey)
283
+ let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
284
+ regex.enumerateMatches(in: hex, range: NSRange(hex.startIndex..., in: hex)) { match, _, _ in
285
+ let byteStr = (hex as NSString).substring(with: match!.range)
286
+ let num = UInt8(byteStr, radix: 16)!
287
+ data.append(num)
288
+ }
348
289
 
349
- // Local Name
350
- if let shortenedName = dataDict["shortenedLocalName"] as? String {
351
- advertisingData[CBAdvertisementDataLocalNameKey] = shortenedName
290
+ return data.isEmpty ? nil : data
291
+ }
292
+
293
+ private func processAdvertisingData(_ data: AdvertisingDataTypes, into advertisingData: inout [String: Any]) {
294
+ if let flags = data.flags {
295
+ advertisingData[CBAdvertisementDataIsConnectable] = true
352
296
  }
353
- if let completeName = dataDict["completeLocalName"] as? String {
354
- advertisingData[CBAdvertisementDataLocalNameKey] = completeName
297
+
298
+ if let completeLocalName = data.completeLocalName {
299
+ advertisingData[CBAdvertisementDataLocalNameKey] = completeLocalName
355
300
  }
356
301
 
357
- // Tx Power Level
358
- if let txPower = dataDict["txPowerLevel"] as? Int {
359
- advertisingData[CBAdvertisementDataTxPowerLevelKey] = txPower
302
+ if let txPowerLevel = data.txPowerLevel {
303
+ advertisingData[CBAdvertisementDataTxPowerLevelKey] = txPowerLevel
304
+ }
305
+ }
306
+ }
307
+
308
+ // MARK: - CBPeripheralManagerDelegate
309
+ extension HybridMunimBluetooth: CBPeripheralManagerDelegate {
310
+ guard let peripheralManager = peripheralManager,
311
+ peripheralManager.state == .poweredOn else {
312
+ throw NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Bluetooth is not powered on"])
360
313
  }
361
314
 
362
- // Service Solicitation
363
- addServiceUUIDs(dataDict["serviceSolicitationUUIDs16"], to: &advertisingData, key: CBAdvertisementDataSolicitedServiceUUIDsKey)
364
- addServiceUUIDs(dataDict["serviceSolicitationUUIDs128"], to: &advertisingData, key: CBAdvertisementDataSolicitedServiceUUIDsKey)
365
- addServiceUUIDs(dataDict["serviceSolicitationUUIDs32"], to: &advertisingData, key: CBAdvertisementDataSolicitedServiceUUIDsKey)
315
+ var advertisingData: [String: Any] = [:]
366
316
 
367
- // Service Data
368
- addServiceData(dataDict["serviceData16"], to: &advertisingData)
369
- addServiceData(dataDict["serviceData32"], to: &advertisingData)
370
- addServiceData(dataDict["serviceData128"], to: &advertisingData)
317
+ // Process comprehensive advertising data
318
+ if let advertisingDataDict = options["advertisingData"] as? [String: Any] {
319
+ processAdvertisingData(advertisingDataDict, into: &advertisingData)
320
+ }
371
321
 
372
- // Appearance
373
- if let appearance = dataDict["appearance"] as? Int {
374
- let appearanceData = Data(bytes: [UInt8(appearance & 0xFF), UInt8((appearance >> 8) & 0xFF)], count: 2)
375
- advertisingData[CBUUID(string: "1800")] = appearanceData
322
+ // Legacy support
323
+ if let localName = options["localName"] as? String {
324
+ advertisingData[CBAdvertisementDataLocalNameKey] = localName
325
+ }
326
+
327
+ if let serviceUUIDs = options["serviceUUIDs"] as? [String], !serviceUUIDs.isEmpty {
328
+ let uuids = serviceUUIDs.compactMap { CBUUID(string: $0) }
329
+ advertisingData[CBAdvertisementDataServiceUUIDsKey] = uuids
376
330
  }
377
331
 
378
- // Manufacturer Data
379
- if let manufacturerData = dataDict["manufacturerData"] as? String,
332
+ if let manufacturerData = options["manufacturerData"] as? String,
380
333
  let data = hexStringToData(manufacturerData) {
381
334
  advertisingData[CBAdvertisementDataManufacturerDataKey] = data
382
335
  }
336
+
337
+ currentAdvertisingData = advertisingData
338
+ peripheralManager.startAdvertising(advertisingData as? [String: Any])
383
339
  }
384
340
 
385
- private func addServiceUUIDs(_ uuids: Any?, to advertisingData: inout [String: Any], key: String) {
386
- guard let uuidArray = uuids as? [String] else { return }
387
-
388
- let cbUUIDs = uuidArray.compactMap { CBUUID(string: $0) }
389
- if !cbUUIDs.isEmpty {
390
- if var existing = advertisingData[key] as? [CBUUID] {
391
- existing.append(contentsOf: cbUUIDs)
392
- advertisingData[key] = existing
393
- } else {
394
- advertisingData[key] = cbUUIDs
395
- }
341
+ func updateAdvertisingData(advertisingData: [String: Any]) throws {
342
+ guard let peripheralManager = peripheralManager,
343
+ peripheralManager.state == .poweredOn else {
344
+ throw NSError(domain: "MunimBluetooth", code: 1, userInfo: [NSLocalizedDescriptionKey: "Bluetooth is not powered on"])
396
345
  }
346
+
347
+ peripheralManager.stopAdvertising()
348
+
349
+ var newAdvertisingData: [String: Any] = [:]
350
+ processAdvertisingData(advertisingData, into: &newAdvertisingData)
351
+
352
+ currentAdvertisingData = newAdvertisingData
353
+ peripheralManager.startAdvertising(newAdvertisingData as? [String: Any])
397
354
  }
398
355
 
399
- private func addServiceData(_ serviceDataArray: Any?, to advertisingData: inout [String: Any]) {
400
- guard let array = serviceDataArray as? [[String: Any]] else { return }
401
-
402
- for serviceData in array {
403
- guard let uuid = serviceData["uuid"] as? String,
404
- let dataString = serviceData["data"] as? String,
405
- let data = hexStringToData(dataString) else { continue }
406
-
407
- advertisingData[uuid] = data
408
- }
356
+ func getAdvertisingData() throws -> [String: Any] {
357
+ return currentAdvertisingData
409
358
  }
410
359
 
411
- private func hexStringToData(_ hexString: String) -> Data? {
412
- let cleanHex = hexString.replacingOccurrences(of: " ", with: "")
413
- guard cleanHex.count % 2 == 0 else { return nil }
414
-
415
- var data = Data()
416
- var index = cleanHex.startIndex
360
+ func stopAdvertising() throws {
361
+ peripheralManager?.stopAdvertising()
362
+ currentAdvertisingData = [:]
363
+ }
364
+
365
+ func setServices(services: [[String: Any]]) throws {
366
+ peripheralServices.removeAll()
417
367
 
418
- while index < cleanHex.endIndex {
419
- let nextIndex = cleanHex.index(index, offsetBy: 2)
420
- guard nextIndex <= cleanHex.endIndex else { break }
368
+ for serviceDict in services {
369
+ guard let uuidString = serviceDict["uuid"] as? String else { continue }
421
370
 
422
- let byteString = String(cleanHex[index..<nextIndex])
423
- if let byte = UInt8(byteString, radix: 16) {
424
- data.append(byte)
371
+ let serviceUUID = CBUUID(string: uuidString)
372
+ let service = CBMutableService(type: serviceUUID, primary: true)
373
+
374
+ var characteristics: [CBMutableCharacteristic] = []
375
+
376
+ if let characteristicsArray = serviceDict["characteristics"] as? [[String: Any]] {
377
+ for charDict in characteristicsArray {
378
+ guard let charUUIDString = charDict["uuid"] as? String else { continue }
379
+
380
+ let charUUID = CBUUID(string: charUUIDString)
381
+ var properties: CBCharacteristicProperties = []
382
+
383
+ if let propertiesArray = charDict["properties"] as? [String] {
384
+ for prop in propertiesArray {
385
+ switch prop {
386
+ case "read":
387
+ properties.insert(.read)
388
+ case "write":
389
+ properties.insert(.write)
390
+ case "notify":
391
+ properties.insert(.notify)
392
+ case "indicate":
393
+ properties.insert(.indicate)
394
+ default:
395
+ break
396
+ }
397
+ }
398
+ }
399
+
400
+ var value: Data?
401
+ if let valueString = charDict["value"] as? String {
402
+ value = valueString.data(using: .utf8)
403
+ properties.insert(.read)
404
+ }
405
+
406
+ let permissions: CBAttributePermissions = value != nil ? .readable : [.readable, .writeable]
407
+
408
+ let characteristic = CBMutableCharacteristic(
409
+ type: charUUID,
410
+ properties: properties,
411
+ value: value,
412
+ permissions: permissions
413
+ )
414
+
415
+ characteristics.append(characteristic)
416
+ }
425
417
  }
426
418
 
427
- index = nextIndex
419
+ service.characteristics = characteristics
420
+ peripheralServices.append(service)
428
421
  }
429
422
 
430
- return data
423
+ peripheralManager?.removeAllServices()
424
+ for service in peripheralServices {
425
+ peripheralManager?.add(service)
426
+ }
431
427
  }
432
- }
433
-
434
- // MARK: - CBPeripheralManagerDelegate
435
-
436
- extension HybridMunimBluetooth: CBPeripheralManagerDelegate {
428
+
429
+ // MARK: - CBPeripheralManagerDelegate
430
+ extension HybridMunimBluetooth: CBPeripheralManagerDelegate {
437
431
  func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
438
432
  // Handle state updates
439
433
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "munim-bluetooth",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "A comprehensive React Native library for all your Bluetooth Low Energy (BLE) needs, supporting both peripheral and central roles with Expo support",
5
5
  "main": "./lib/commonjs/index.js",
6
6
  "module": "./lib/module/index.js",