All,
I have an issue where my IOBluetoothDeviceInquiry and IOBluetoothDevicePair objects do not seem to be triggering all of their lifecycle states. Here is some sample code below. What am I missing here. In this example, the deviceInquiryStarted trigger never fires, even though it clearly starts and finds devices. Also devices are not added to the @Published object as they are found.
Also the sender.stop() does not seem to work when coming from the delegate to stop the search.
I feel like I am just missing something here as it relates to delegates.
I am just looking to scan for HID devices like mice, keyboards and headsets. Then allow the user to pair the device, and list paired devices.
I am pretty new to IOBluetooth so any help is appreciated.
import IOBluetooth
import IOBluetoothUI
import SwiftUI
class BluetoothSearchDelegate : NSObject, ObservableObject, IOBluetoothDeviceInquiryDelegate {
@Published var devices: [IOBluetoothDevice] = []
func deviceInquiryDeviceFound(_ sender: IOBluetoothDeviceInquiry, device: IOBluetoothDevice) {
debugPrint("********** DEVICE FOUND ************")
self.devices.append(device)
}
func deviceInquiryStarted(_ sender: IOBluetoothDeviceInquiry) {
debugPrint("inquiryStarted")
}
func deviceInquiryComplete(_ sender: IOBluetoothDeviceInquiry, error: IOReturn, aborted: Bool) {
sender.stop()
print("completed")
}
}
class BluetoothController: ObservableObject {
//
// DEVICE LISTS
//
@Published var devices: [IOBluetoothDevice] = []
//
// SEARCH TYPE
//
let searchType: IOBluetoothDeviceSearchTypes = kIOBluetoothDeviceSearchClassic.rawValue
//
// BLUETOOTH DEVICE
//
let btDevice: IOBluetoothDevice = IOBluetoothDevice()
//
// DELEGATES
//
let searchDelegate = BluetoothSearchDelegate()
//
// AGENTS
//
let searchAgent: IOBluetoothDeviceInquiry
let pairingAgent = IOBluetoothDevicePair()
init() {
self.searchAgent = IOBluetoothDeviceInquiry(delegate: self.searchDelegate)
self.searchAgent.searchType = self.searchType
}
func scan() {
self.searchAgent.inquiryLength = 1
self.searchAgent.start()
self.devices = self.searchDelegate.devices
}
func getPairedDevices() -> [IOBluetoothDevice] {
let devices = IOBluetoothDevice.pairedDevices() as? [IOBluetoothDevice]
debugPrint(devices ?? [])
return devices ?? []
}
func stop() {
self.searchAgent.stop()
self.devices = self.searchAgent.foundDevices() as? [IOBluetoothDevice] ?? []
self.searchAgent.clearFoundDevices()
debugPrint("Device Count: \(self.devices.count)")
}
func pair(device: IOBluetoothDevice) -> IOBluetoothDevicePair {
self.pairingAgent.setDevice(device)
pairingAgent.start()
return pairingAgent
}
func stopPairing(pairingAgent: IOBluetoothDevicePair) {
pairingAgent.stop()
}
}
struct ContentView: View {
@State var isScanning: Bool = false
@State var isPairing: Bool = false
@ObservedObject var bt = BluetoothController()
var body: some View {
VStack {
Text("Scan for bt devices").padding()
Button(action: {
if !self.isScanning {
self.bt.scan()
self.isScanning.toggle()
return
} else {
self.bt.stop()
self.isScanning.toggle()
return
}
}, label: {
Text(self.isScanning ? "Stop" : "Scan")
}).padding()
List(self.bt.searchDelegate.devices, id: \.self) { device in
HStack {
Text("\(device.name)")
Spacer()
Button(action: {
var pairingAgent: IOBluetoothDevicePair = IOBluetoothDevicePair()
if !self.isPairing {
pairingAgent = bt.pair(device: device)
} else {
bt.stopPairing(pairingAgent: pairingAgent)
}
}, label: {
if !self.isPairing {
Text("Pair")
} else {
Text("Stop")
}
})
}
}
}
}
}