Skip to content

Instantly share code, notes, and snippets.

@michael-martinez
Created March 26, 2021 18:05
Show Gist options
  • Save michael-martinez/9f129a8bd45193c647b4692f555d13ca to your computer and use it in GitHub Desktop.
Save michael-martinez/9f129a8bd45193c647b4692f555d13ca to your computer and use it in GitHub Desktop.
// MARK: Reading Values
private var receiveDetectionEventsTimer: Timer? = nil
func receiveDetectionEvents() {
let charUuid = wifiUuid
print("STEP 1: READING CHARACTERISTIC \(charUuid)...")
if manager == nil {
manager = CentralManager(options: [CBCentralManagerOptionRestoreIdentifierKey : "CentralMangerKey" as NSString])
}
guard let manager = manager else {
delegate?.readFailure(error: "Erreur Bluetooth")
return
}
let stateChangeFuture = manager.whenStateChanges()
/// Handles state changes and returns a scan future if the bluetooth is powered on
let scanFuture = stateChangeFuture.flatMap { state -> FutureStream<Peripheral> in
switch state {
case .poweredOn:
/// Scans for peripherals which have our service
print("STEP 2: SCANNING FOR SERVICE \(self.serviceUUID)...")
return manager.startScanning(forServiceUUIDs: [self.serviceUUID])
case .poweredOff:
throw AppError.poweredOff
case .unauthorized, .unsupported:
throw AppError.invalidState
case .resetting:
throw AppError.resetting
case .unknown:
throw AppError.unknown
}
}
scanFuture.onFailure { error in
guard let appError = error as? AppError else {
return
}
switch appError {
case .invalidState:
break
case .resetting:
manager.reset()
case .poweredOff:
break
case .unknown:
break
default:
break;
}
}
/// Connects to the first scanned peripheral
let connectionFuture = scanFuture.flatMap { p -> FutureStream<Void> in
/// Stops the scan when we find the first peripheral
manager.stopScanning()
self.peripheral = p
guard let peripheral = self.peripheral else {
throw AppError.unknown
}
/// Connects to the peripheral to trigger connected mode
print("STEP 3: CONNECTING TO PERIPHERAL...")
return peripheral.connect(connectionTimeout: 10, capacity: 5)
}
/// Discovers our service so we can read its characteristics
let discoveryFuture = connectionFuture.flatMap { _ -> Future<Void> in
guard let peripheral = self.peripheral else {
throw AppError.unknown
}
print("STEP 4: DISCOVERING SERVICES...")
return peripheral.discoverServices([self.serviceUUID])
}.flatMap { _ -> Future<Void> in
guard let discoveredPeripheral = self.peripheral else {
throw AppError.unknown
}
guard let service = discoveredPeripheral.services(withUUID: self.serviceUUID)?.first else {
throw AppError.serviceNotFound
}
self.peripheral = discoveredPeripheral
/// Discovers the specific characteristic
print("STEP 5: DISCOVERING CHARACTERISTICS...")
return service.discoverCharacteristics([charUuid])
}
/// Checks if characteristic is correctly discovered; Registers for notifications with dataFuture variable
let dataFuture = discoveryFuture.flatMap { _ -> Future<Void> in
print("STEP 5a")
guard let discoveredPeripheral = self.peripheral else {
throw AppError.unknown
}
print("STEP 5b")
guard let dataCharacteristic1 = discoveredPeripheral.services(withUUID: self.serviceUUID)?.first?.characteristics(withUUID: charUuid)?.first else {
throw AppError.dataCharactertisticNotFound
}
print("STEP 5c")
self.dataCharWifi = dataCharacteristic1
self.receiveDetectionEventsTimer?.invalidate()
self.receiveDetectionEventsTimer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { timer in
print("Step POLLING")
self.readData()
}
/// Asks the characteristic to start notifying when the value is changed
print("STEP 5d")
return dataCharacteristic1.startNotifying()
}.flatMap { _ -> FutureStream<Data?> in
print("STEP 6")
guard let discoveredPeripheral = self.peripheral else {
throw AppError.unknown
}
guard let characteristic = discoveredPeripheral.services(withUUID: self.serviceUUID)?.first?.characteristics(withUUID: charUuid)?.first else {
throw AppError.dataCharactertisticNotFound
}
/// Registers to receive notification when characteristic value changes; returns future to handle notification
return characteristic.receiveNotificationUpdates(capacity: 10)
}
/// onSuccess called every time the characteristic value changes
dataFuture.onSuccess { data in
print("STEP 6: called every time the characteristic value changes")
DispatchQueue.main.async {
self.delegate?.readSuccess(characteristicType: .wifi, data: data)
}
}
dataFuture.onFailure { error in
print("Step RCK: FAILURE \(error)")
switch error {
case PeripheralError.disconnected, PeripheralError.connectionTimeout:
self.receiveDetectionEventsTimer?.invalidate()
self.receiveDetectionEventsTimer = nil
self.peripheral?.terminate()
self.delegate?.readFailure(error: "Erreur : \(error)")
/*DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 10) {
self.peripheral?.reconnect()
}*/
case AppError.serviceNotFound:
break
case AppError.dataCharactertisticNotFound:
break
default:
break
}
}
}
private func readData() {
var readFuture: Future<Void>?
readFuture = self.dataCharWifi?.read(timeout: TimeInterval.infinity)
readFuture?.onSuccess { (_) in
DispatchQueue.main.async {
if let data = self.dataCharWifi?.dataValue {
do {
let wifiData = try Com_Org_Protocol_WifiSettings.init(serializedData: data)
self.delegate?.readSuccess(characteristicType: characteristicType, data: wifiData)
} catch {
self.delegate?.readFailure(error: "Erreur de désérialisation des données pour le wifi")
}
} else {
self.delegate?.readFailure(error: "Erreur de désérialisation des données pour le wifi")
}
}
}
readFuture?.onFailure { (e) in
print("STEP: onFailure TWO")
self.delegate?.readFailure(error: "Erreur : \(e.localizedDescription)")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment