SwiftUI ObservableObject и Опубликовано isue - PullRequest
0 голосов
/ 21 апреля 2020

вот что заставляет меня бодрствовать уже три дня: я пишу небольшое приложение, которое соединяет BT с Arduino. Чтобы получить визуальную обратную связь о состоянии соединения и передаваемых данных, я использую представление, которое позволяет мне подключаться / отключаться, а также показывает мне состояние и данные:

        VStack {
            Text("Glove Training App")
                .font(.title)
            HStack {
                Button(action: { MyBluetoothManager.shared.scan() }) {
                    Text("Connect")
                        .padding(30)
                }
                Text(" | ")
                Button(action: { MyBluetoothManager.shared.disconnect()}) {
                    Text("Disconnect")
                    .padding(30)
                }
            }

            Text(manager.stateChange)
                .font(.subheadline)
                .padding(.bottom, 30)

            Text(peripheral.transmittedString)
            .font(.subheadline)
            .padding(.bottom, 30)
        }
    }

В отдельном файле у меня есть все Управление BT:

class MyBluetoothManager: NSObject, ObservableObject {
    @Published var stateChange: String = "Initializing..." {
        willSet { objectWillChange.send() }
    }
    static let shared = MyBluetoothManager()

    let central = CBCentralManager(delegate: MyCentralManagerDelegate.shared,
        queue: nil, options: [
        CBCentralManagerOptionRestoreIdentifierKey: restoreIdKey,
        ])
(...)
    func setConnected(peripheral: CBPeripheral) {
        (...)

        state = .connected(peripheral)
        self.stateChange = "Connected"
        print("Connected")
    }
}

class MyPeripheralDelegate: NSObject, ObservableObject, CBPeripheralDelegate {
    let objectWillChange = ObservableObjectPublisher()
    var transmittedString: String = "No data" {
        willSet { objectWillChange.send()
        }
    }
    func peripheral(_ peripheral: CBPeripheral,
            didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        (...)
        let rxData = characteristic.value
        if let str = NSString(data: rxData!, encoding: String.Encoding.utf8.rawValue) as String? {
            print(str)
            self.transmittedString = str
            let measurement = str.components(separatedBy: "|")
            (...)
        } else {
            print("not a valid UTF-8 sequence")
        }
    }
}

Значения изначально установлены правильно, но затем никогда не обновляются. В терминале я вижу напечатанные значения, и приложение работает иначе, чем ожидалось. Я на последней версии XCode. Я посмотрел несколько уроков, и это кажется сложным. Любая помощь будет принята с благодарностью.

Приветствия, Кристиан

РЕДАКТИРОВАТЬ: Вот полный класс BluetoothManager (не мой код в основном, но работает отлично):

class MyBluetoothManager: NSObject, ObservableObject {
    @Published var stateChange: String = "Initializing..." {
        willSet { objectWillChange.send() }
    }
    static let shared = MyBluetoothManager()

    let central = CBCentralManager(delegate: MyCentralManagerDelegate.shared,
        queue: nil, options: [
        CBCentralManagerOptionRestoreIdentifierKey: restoreIdKey,
        ])

    var state = State.poweredOff
    enum State {
        case poweredOff
        case restoringConnectingPeripheral(CBPeripheral)
        case restoringConnectedPeripheral(CBPeripheral)
        case disconnected
        case scanning(Countdown)
        case connecting(CBPeripheral, Countdown)
        case discoveringServices(CBPeripheral, Countdown)
        case discoveringCharacteristics(CBPeripheral, Countdown)
        case connected(CBPeripheral)
        case outOfRange(CBPeripheral)

        var peripheral: CBPeripheral? {
            switch self {
            case .poweredOff: return nil
            case .restoringConnectingPeripheral(let p): return p
            case .restoringConnectedPeripheral(let p): return p
            case .disconnected: return nil
            case .scanning: return nil
            case .connecting(let p, _): return p
            case .discoveringServices(let p, _): return p
            case .discoveringCharacteristics(let p, _): return p
            case .connected(let p): return p
            case .outOfRange(let p): return p
            }
        }
    }

    func scan() {
        guard central.state == .poweredOn else {
            self.stateChange = "Cannot scan, BT is not powered on"
            print("Cannot scan, BT is not powered on")
            return
        }

        central.scanForPeripherals(withServices: [myDesiredServiceId], options: nil)
        state = .scanning(Countdown(seconds: 10, closure: {
            self.central.stopScan()
            self.state = .disconnected
            self.stateChange = "Scan timed out"
            print("Scan timed out")

        }))
    }

    func disconnect(forget: Bool = false) {
        if let peripheral = state.peripheral {
            central.cancelPeripheralConnection(peripheral)
        }
        if forget {
            UserDefaults.standard.removeObject(forKey: peripheralIdDefaultsKey)
            UserDefaults.standard.synchronize()
        }
        self.stateChange = "Disconnected"
        state = .disconnected
    }

    func connect(peripheral: CBPeripheral) {
        central.connect(peripheral, options: nil)
        state = .connecting(peripheral, Countdown(seconds: 10, closure: {
            self.central.cancelPeripheralConnection(peripheral)
            self.state = .disconnected
            self.stateChange = "Connect timed out"
            print("Connect timed out")
        }))
    }

    func discoverServices(peripheral: CBPeripheral) {
        peripheral.delegate = MyPeripheralDelegate.shared
        peripheral.discoverServices([myDesiredServiceId])
        state = .discoveringServices(peripheral, Countdown(seconds: 10, closure: {
            self.disconnect()
            self.stateChange = "Could not discover services"
            print("Could not discover services")
        }))
    }

    func discoverCharacteristics(peripheral: CBPeripheral) {
        guard let myDesiredService = peripheral.myDesiredService else {
            self.disconnect()
            return
        }
        peripheral.delegate = MyPeripheralDelegate.shared
        peripheral.discoverCharacteristics([myDesiredCharacteristicId],
            for: myDesiredService)
        state = .discoveringCharacteristics(peripheral, Countdown(seconds: 10,
            closure: {
            self.disconnect()
                self.stateChange = "Could not discover characteristics"
            print("Could not discover characteristics")
        }))
    }

    func setConnected(peripheral: CBPeripheral) {
        guard let myDesiredCharacteristic = peripheral.myDesiredCharacteristic
            else {
                self.stateChange = "Missing characteristic"
                print("Missing characteristic")
                disconnect()
            return
        }

        UserDefaults.standard.set(peripheral.identifier.uuidString,
            forKey: peripheralIdDefaultsKey)
        UserDefaults.standard.synchronize()

        peripheral.delegate = MyPeripheralDelegate.shared
        peripheral.setNotifyValue(true, for: myDesiredCharacteristic)

        state = .connected(peripheral)
        self.stateChange = "Connected"
        print("Connected")
    }
}

1 Ответ

0 голосов
/ 21 апреля 2020
           Button(action: { MyBluetoothManager.shared.scan() }) {
                Text("Connect")
                    .padding(30)
            }
            Text(" | ")
            Button(action: { MyBluetoothManager.shared.disconnect()}) {
                Text("Disconnect")
                .padding(30)
            }
        }

        Text(manager.stateChange) << why don't you use MyBluetoothManager.shared here ? is there a second instance? this might be the error...but unfortunately you just showed us a small piece of code...
...