I am trying to establish a MVVM pattern to fetch values from a BLEManager to ViewModel and then to my view. But somehow I am not able to emit the values from the BLEManager class to View Model. Following is my data flow:
View(Main View with some contents) <- View(one content with a list) <- ViewModel(viewmodel that feeds data as array to list in view) <- BleManager(ble scanner to fetch available ble devices)
BleManager:
class BLEManager: NSObject, ObservableObject {
var centralManager: CBCentralManager!
@Published var peripherals = [CBPeripheral]()
@Published var isSwitchedOn = false
override init() {
super.init()
centralManager = CBCentralManager(delegate: self, queue: nil)
}
func startScanning() {
centralManager.scanForPeripherals(withServices: nil, options: nil)
}
func stopScanning() {
centralManager.stopScan()
}
}
extension BLEManager: CBCentralManagerDelegate, CBPeripheralDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
isSwitchedOn = true
}
else {
isSwitchedOn = false
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String else {
return
}
if !peripherals.contains(where: { $0.name == peripheralName }), let name = peripheral.name {
peripherals.append(peripheral)
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("didFailToConnect")
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("didDisconnectPeripheral")
}
}
I am not able to figure out how to code my view model following is its current state: ViewModel:
class NearbyBLEListViewModel: ObservableObject {
@Published var authenticationResult: Bool?
@Published var bleDevices = [BLEInfo]()
@Published var blutoothOff: Bool?
var bleManager = BLEManager()
func startScanning() {
bleManager.startScanning()
bleDevices = bleManager.peripherals.map { BLEInfo(bleId: $0.name ?? "NA", hash: "") }
// if bleManager.isSwitchedOn {
// blutoothOff = false
// bleDevices = bleManager.peripherals.map { bleInfo(bleId: $0.name ?? "NA", hash: "") }
// print(bleDevices)
// } else {
// blutoothOff = true
// }
}
func getBLEDevices() {
bleDevices = bleManager.peripherals.map { BLEInfo(bleId: $0.name ?? "NA", hash: "") }
print(bleDevices)
}
}
Using it in the view as following:
struct NearbyBLEListView: View {
@ObservedObject var bleManager = BLEManager()
@StateObject private var viewModel = NearbyBLEListViewModel()
var body: some View {
VStack {
Text("BLEs nearby:")
.font(.system(size: 22, weight: .bold, design: .default))
.padding()
.foregroundColor(.black)
List(viewModel.bleDevices) { ble in
HStack {
Text(ble.bleId)
}
}
.navigationTitle("News")
.onAppear(perform: viewModel.startScanning)
}
}
}
and lastly my top most view:
var body: some View {
NavigationView {
VStack(alignment: .center) {
buttonView
NearbyBLEListView()
}
.navigationTitle("")
.toolbar {
moreButtonView
}
}
}
I have just started using SwiftUI so I may be off the best practices for SwiftUI. Can someone please help me here?