SwiftUI: How to process BLE response and display new values

Viewed 607

I have a swift application that communicates with a small BLE device. I'm able to send requests and get answers from the device but I struggle updating values displayed on my swiftui view.

Here's what I tried:

  1. With a callback:

In the BleConnection.swift file, implementing all BLE stuff, I declared a callback var onResponse: (([UInt8]) -> Void)? = nil When a response is received from the device, the data is pushed to the view with the callback:

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
...
            if self.onResponse != nil {
                    self.onResponse!(characteristic.value!.bytes)
                }
            }
        }
    }

In the ReaderInformations.swift file, where there is the swiftui view, I implemented the callback and tried to update a component displayed value with a @State var but it didn't work out. The print() in the callback is well printed in the console but the component not updated. I then read that only internal method to the view could update a state var.

  1. With Combine: I updated the peripheral(didUpdateValueFor) and sent the BLE device response like this:
let passThroughSubjectPublisher = PassthroughSubject<[UInt8], Never>()

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
...
            passThroughSubjectPublisher.send(characteristic.value!.bytes)  
        }
    }

and in the view:

struct ReaderInformations: View {
    var ble: BleConnection
    @State private var status: String = "status" 
    private var cancelSet: Set<AnyCancellable> = []


    init(bleInstance: BleConnection) {
        passThroughSubjectPublisher.sink(receiveValue: { response in. // Escaping closure captures mutating 'self' parameter
             switch response[0] {
                 self.status = "TEST".   // This error because of the self
             ...
             }
        }).store(in: &cancelSet)
    }

I doesn't work either, because I tried in the init to access a member not even instantiated yet.

So here I am with no idea how to do. How would you guys manage this?

1 Answers

Attach observer of publisher in body instead, like in below pseudo-code

struct ReaderInformations: View {
    var ble: BleConnection
    @State private var status: String = "status" 

    var body: some View {

       VStack { // .. any your view


       }
       .onReceive(ble.passThroughSubjectPublisher) { response in // << here !!
              switch response[0] {
                 self.status = "TEST"
              ///...
             }
        }
    }
}
Related