BleExtensions.swift
1 // 2 // BleExtensions.swift 3 // ble_peripheral 4 // 5 // Created by Rohit Sangwan on 29/07/23. 6 // 7 8 #if os(iOS) 9 import Flutter 10 #elseif os(macOS) 11 import FlutterMacOS 12 #else 13 #error("Unsupported platform.") 14 #endif 15 import CoreBluetooth 16 import Foundation 17 18 enum CustomError: Error { 19 case notFound(String) 20 } 21 22 /// local list of characteristic 23 var characteristicsList = [CBMutableCharacteristic]() 24 var servicesList = [CBMutableService]() 25 26 extension BleService { 27 func toCBService() -> CBMutableService { 28 let service = CBMutableService( 29 type: CBUUID(string: uuid), 30 primary: primary 31 ) 32 let chars = characteristics.compactMap { bleChar in 33 bleChar.toCBChar() 34 } 35 if !chars.isEmpty { 36 service.characteristics = chars 37 } 38 // Add local reference of this service 39 servicesList.removeAll { $0.uuid.uuidString.lowercased() == service.uuid.uuidString.lowercased() } 40 servicesList.append(service) 41 return service 42 } 43 } 44 45 extension BleDescriptor { 46 func toCBMutableDescriptor() -> CBMutableDescriptor { 47 return CBMutableDescriptor( 48 type: CBUUID(string: uuid), 49 value: value?.toData() 50 ) 51 } 52 } 53 54 extension BleCharacteristic { 55 func toCBChar() -> CBMutableCharacteristic { 56 let properties: [CBCharacteristicProperties] = self.properties.compactMap { int64 in 57 int64.toCBCharacteristicProperties() 58 } 59 let permissions: [CBAttributePermissions] = self.permissions.compactMap { int64 in 60 int64.toCBAttributePermissions() 61 } 62 63 let combinedProperties = properties.reduce(CBCharacteristicProperties()) { $0.union($1) } 64 let combinedPermissions = permissions.reduce(CBAttributePermissions()) { $0.union($1) } 65 66 let char = CBMutableCharacteristic( 67 type: CBUUID(string: uuid), 68 properties: combinedProperties, 69 value: value?.toData(), 70 permissions: combinedPermissions 71 ) 72 char.descriptors = descriptors?.compactMap { desc in 73 desc.toCBMutableDescriptor() 74 } 75 // Add local reference of this characteristic 76 characteristicsList.removeAll { $0.uuid.uuidString.lowercased() == char.uuid.uuidString.lowercased() } 77 characteristicsList.append(char) 78 return char 79 } 80 } 81 82 extension String { 83 func findCharacteristic() -> CBMutableCharacteristic? { 84 return characteristicsList.first { ch in 85 ch.uuid.uuidString == self 86 } 87 } 88 89 func findService() -> CBMutableService? { 90 return servicesList.first { ch in 91 ch.uuid.uuidString == self 92 } 93 } 94 } 95 96 extension Int64 { 97 func toCBCharacteristicProperties() -> CBCharacteristicProperties? { 98 switch self { 99 case 0: 100 return CBCharacteristicProperties.broadcast 101 case 1: 102 return CBCharacteristicProperties.read 103 case 2: 104 return CBCharacteristicProperties.writeWithoutResponse 105 case 3: 106 return CBCharacteristicProperties.write 107 case 4: 108 return CBCharacteristicProperties.notify 109 case 5: 110 return CBCharacteristicProperties.indicate 111 case 6: 112 return CBCharacteristicProperties.authenticatedSignedWrites 113 case 7: 114 return CBCharacteristicProperties.extendedProperties 115 case 8: 116 return CBCharacteristicProperties.notifyEncryptionRequired 117 case 9: 118 return CBCharacteristicProperties.indicateEncryptionRequired 119 default: 120 return nil 121 } 122 } 123 124 func toCBAttributePermissions() -> CBAttributePermissions? { 125 switch self { 126 case 0: 127 return CBAttributePermissions.readable 128 case 1: 129 return CBAttributePermissions.writeable 130 case 2: 131 return CBAttributePermissions.readEncryptionRequired 132 case 3: 133 return CBAttributePermissions.writeEncryptionRequired 134 default: 135 return nil 136 } 137 } 138 139 func toCBATTErrorCode() -> CBATTError.Code { 140 switch self { 141 case 0: 142 return CBATTError.success 143 case 1: 144 return CBATTError.invalidHandle 145 case 2: 146 return CBATTError.readNotPermitted 147 case 3: 148 return CBATTError.writeNotPermitted 149 case 4: 150 return CBATTError.invalidPdu 151 case 5: 152 return CBATTError.insufficientAuthentication 153 case 6: 154 return CBATTError.requestNotSupported 155 case 7: 156 return CBATTError.invalidOffset 157 case 8: 158 return CBATTError.insufficientAuthorization 159 case 9: 160 return CBATTError.prepareQueueFull 161 case 10: 162 return CBATTError.attributeNotFound 163 case 11: 164 return CBATTError.attributeNotLong 165 case 12: 166 return CBATTError.insufficientEncryptionKeySize 167 case 13: 168 return CBATTError.invalidAttributeValueLength 169 case 14: 170 return CBATTError.unlikelyError 171 case 15: 172 return CBATTError.insufficientEncryption 173 case 16: 174 return CBATTError.unsupportedGroupType 175 case 17: 176 return CBATTError.insufficientResources 177 default: 178 return CBATTError.success 179 } 180 } 181 } 182 183 extension FlutterStandardTypedData { 184 func toData() -> Data { 185 return Data(data) 186 } 187 } 188 189 extension Data { 190 func toInt64() -> [Int64] { 191 return map { Int64(Int($0)) } 192 } 193 194 func toFlutterBytes() -> FlutterStandardTypedData { 195 return FlutterStandardTypedData(bytes: self) 196 } 197 } 198 199 extension [Int64?] { 200 func toData() -> Data { 201 let finalArray = self.compactMap { data in 202 data 203 } 204 return Data(bytes: finalArray, count: finalArray.count) 205 } 206 }