admin管理员组

文章数量:1131174

import SwiftUI
import CoreBluetooth
import os

class BluetoothViewModel: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    @Published var peripherals: [CBPeripheral] = []  // List of discovered peripherals
    @Published var connectedPeripheral: CBPeripheral?  // The selected peripheral that is connected
    @Published var discoveredServices: [CBService] = []  // List of discovered services
    @Published var discoveredServiceNames: [String] = []  // List of names of discovered services
    @Published var peripheralNames: [String] = []  // List of peripheral names found during scanning
    @Published var filteredPeripheralNames: [String] = []  // List of filtered peripheral names
    
    var cbManager: CBCentralManager?
    private var peripheralNameReceived = false  // Flag to track when the name is received
    
    override init() {
        super.init()
        self.cbManager = CBCentralManager(delegate: self, queue: .main)
    }

    // Start scanning for peripherals (without connecting to them)
    func startScanning() {
        os_log("Start scanning for all peripherals.")
        let options: [String: Any] = [CBCentralManagerScanOptionAllowDuplicatesKey: NSNumber(value: false)]
        cbManager?.scanForPeripherals(withServices: nil, options: options)  // Scan for all peripherals
    }
    
    // Stop scanning for peripherals
    func stopScanning() {
        os_log("Stop scanning.")
        cbManager?.stopScan()
    }
    
    // MARK: - CBCentralManagerDelegate Methods
    
    // Called when the central manager's state changes
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .poweredOn:
            os_log("Bluetooth is powered on.")
        case .poweredOff:
            os_log("Bluetooth is powered off.")
        case .unauthorized:
            os_log("Bluetooth unauthorized.")
        case .unknown:
            os_log("Bluetooth state is unknown.")
        case .resetting:
            os_log("Bluetooth is resetting.")
        case .unsupported:
            os_log("Bluetooth is unsupported.")
        @unknown default:
            os_log("Unknown Bluetooth state.")
        }
    }
    
    // Called when a peripheral is discovered (but no connection is made)
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi: NSNumber) {
        os_log("Discovered peripheral: %@", peripheral.name ?? "Unknown")
        
        // Filter out peripherals by name if found in the filtered list
        if let name = peripheral.name {
            if !isPeripheralNameFiltered(name) {
                // Add name to the list if it is not filtered out
                peripheralNames.append(name)
                os_log("Peripheral name added: %@", name)
            } else {
                os_log("Peripheral name %@ is filtered out", name)
            }
        }
        
        // Add peripheral to the list if it's not already there
        if !peripherals.contains(peripheral) {
            peripherals.append(peripheral)
        }
    }
    
    // Function to check if a peripheral name is in the filtered list
    func isPeripheralNameFiltered(_ name: String) -> Bool {
        return filteredPeripheralNames.contains(name)
    }
    
    // Function to filter out a specific peripheral by name
    func filterOutPeripheralName(_ name: String) {
        if !filteredPeripheralNames.contains(name) {
            filteredPeripheralNames.append(name)
            os_log("Peripheral name %@ added to filtered list", name)
        }
    }

    // Called when a peripheral is connected
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        os_log("Connected to peripheral: %@", peripheral.name ?? "Unknown")
        
        // Set the peripheral delegate to discover its services
        peripheral.delegate = self
        connectedPeripheral = peripheral
        
        // Discover all services on the connected peripheral
        peripheral.discoverServices(nil)  // Pass nil to discover all services
    }
    
    // Called when a peripheral connection fails
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        os_log("Failed to connect to peripheral: %@", peripheral.name ?? "Unknown")
    }
    
    // Called when a peripheral is disconnected
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        os_log("Disconnected from peripheral: %@", peripheral.name ?? "Unknown")
        connectedPeripheral = nil
        discoveredServices.removeAll()
        discoveredServiceNames.removeAll()
        peripheralNames.removeAll()
        filteredPeripheralNames.removeAll()
    }

    // MARK: - CBPeripheralDelegate Methods
    
    // Called when the peripheral discovers its services
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if let error = error {
            os_log("Failed to discover services: %@", error.localizedDescription)
            return
        }
        
        os_log("Discovered services for peripheral: %@", peripheral.name ?? "Unknown")
        
        // Define the UUIDs for Device Information and Battery Service
        let deviceInformationServiceUUID = CBUUID(string: "180A")  // Device Information
        let batteryServiceUUID = CBUUID(string: "180F")  // Battery Service
        
        // Loop through discovered services and fetch their names
        for service in peripheral.services ?? [] {
            discoveredServices.append(service)
            discoveredServiceNames.append(service.uuid.uuidString)
            os_log("Service found: %@", service.uuid.uuidString)
            
            // Check if the service is Device Information or Battery Service
            if service.uuid == deviceInformationServiceUUID {
                os_log("Device Information Service found")
            } else if service.uuid == batteryServiceUUID {
                os_log("Battery Service found")
            } else {
                os_log("Other service found: %@", service.uuid.uuidString)
            }
            
            // If device information service is found, discover characteristics
            if service.uuid == deviceInformationServiceUUID {
                peripheral.discoverCharacteristics([CBUUID(string: "2A00")], for: service)  // Device Name characteristic
            }
        }
    }
    
    // Called when a peripheral discovers characteristics for a given service
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if let error = error {
            os_log("Failed to discover characteristics for service: %@", error.localizedDescription)
            return
        }
        
        os_log("Discovered characteristics for service: %@", service.uuid.uuidString)
        
        // If the Device Information Service is found, read the device name characteristic (2A00)
        for characteristic in service.characteristics ?? [] {
            if characteristic.uuid == CBUUID(string: "2A00") {  // Device Name characteristic
                peripheral.readValue(for: characteristic)
            }
        }
    }
    
    // Called when a characteristic's value is updated
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        if let error = error {
            os_log("Failed to read characteristic value: %@", error.localizedDescription)
            return
        }
        
        if characteristic.uuid == CBUUID(string: "2A00") {  // Device Name characteristic
            if let nameData = characteristic.value, let deviceName = String(data: nameData, encoding: .utf8) {
                os_log("Device Name from Device Info Service: %@", deviceName)
                peripheralNames.append(deviceName)  // Add device name to list
                peripheralNameReceived = true  // Mark the name as received
            }
        }
    }
    
    // Handle Manufacturer Data (custom name or metadata) if present
    func peripheral(_ peripheral: CBPeripheral, didReadValueFor characteristic: CBCharacteristic, error: Error?) {
        if let manufacturerData = characteristic.value, characteristic.uuid == CBUUID(string: "2A29") {
            os_log("Manufacturer Data: %@", convertDataToHexString(manufacturerData))
        }
    }
    
    // Helper function to convert Data to a hexadecimal string for logging
    private func convertDataToHexString(_ data: Data) -> String {
        return data.map { String(format: "%02hhx", $0) }.joined()
    }

    // A method to check if the name was received before moving on
    func waitForName() {
        while !peripheralNameReceived {
            // Implement a small delay or check, or you can return here and handle other logic asynchronously
            usleep(100000) // Sleep for 0.1 seconds for example
        }
    }
}

results, The unknown is being displayed in ios settings with its device name.

Discovered peripheral: [TV] Cia tv
Peripheral name added: [TV] Cia tv
Discovered peripheral: Unknown
Discovered peripheral: Unknown
Discovered peripheral: [TV] Cia tv
Peripheral name added: [TV] Cia tv
Discovered peripheral: Unknown

import SwiftUI
import CoreBluetooth
import os

class BluetoothViewModel: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    @Published var peripherals: [CBPeripheral] = []  // List of discovered peripherals
    @Published var connectedPeripheral: CBPeripheral?  // The selected peripheral that is connected
    @Published var discoveredServices: [CBService] = []  // List of discovered services
    @Published var discoveredServiceNames: [String] = []  // List of names of discovered services
    @Published var peripheralNames: [String] = []  // List of peripheral names found during scanning
    @Published var filteredPeripheralNames: [String] = []  // List of filtered peripheral names
    
    var cbManager: CBCentralManager?
    private var peripheralNameReceived = false  // Flag to track when the name is received
    
    override init() {
        super.init()
        self.cbManager = CBCentralManager(delegate: self, queue: .main)
    }

    // Start scanning for peripherals (without connecting to them)
    func startScanning() {
        os_log("Start scanning for all peripherals.")
        let options: [String: Any] = [CBCentralManagerScanOptionAllowDuplicatesKey: NSNumber(value: false)]
        cbManager?.scanForPeripherals(withServices: nil, options: options)  // Scan for all peripherals
    }
    
    // Stop scanning for peripherals
    func stopScanning() {
        os_log("Stop scanning.")
        cbManager?.stopScan()
    }
    
    // MARK: - CBCentralManagerDelegate Methods
    
    // Called when the central manager's state changes
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .poweredOn:
            os_log("Bluetooth is powered on.")
        case .poweredOff:
            os_log("Bluetooth is powered off.")
        case .unauthorized:
            os_log("Bluetooth unauthorized.")
        case .unknown:
            os_log("Bluetooth state is unknown.")
        case .resetting:
            os_log("Bluetooth is resetting.")
        case .unsupported:
            os_log("Bluetooth is unsupported.")
        @unknown default:
            os_log("Unknown Bluetooth state.")
        }
    }
    
    // Called when a peripheral is discovered (but no connection is made)
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi: NSNumber) {
        os_log("Discovered peripheral: %@", peripheral.name ?? "Unknown")
        
        // Filter out peripherals by name if found in the filtered list
        if let name = peripheral.name {
            if !isPeripheralNameFiltered(name) {
                // Add name to the list if it is not filtered out
                peripheralNames.append(name)
                os_log("Peripheral name added: %@", name)
            } else {
                os_log("Peripheral name %@ is filtered out", name)
            }
        }
        
        // Add peripheral to the list if it's not already there
        if !peripherals.contains(peripheral) {
            peripherals.append(peripheral)
        }
    }
    
    // Function to check if a peripheral name is in the filtered list
    func isPeripheralNameFiltered(_ name: String) -> Bool {
        return filteredPeripheralNames.contains(name)
    }
    
    // Function to filter out a specific peripheral by name
    func filterOutPeripheralName(_ name: String) {
        if !filteredPeripheralNames.contains(name) {
            filteredPeripheralNames.append(name)
            os_log("Peripheral name %@ added to filtered list", name)
        }
    }

    // Called when a peripheral is connected
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        os_log("Connected to peripheral: %@", peripheral.name ?? "Unknown")
        
        // Set the peripheral delegate to discover its services
        peripheral.delegate = self
        connectedPeripheral = peripheral
        
        // Discover all services on the connected peripheral
        peripheral.discoverServices(nil)  // Pass nil to discover all services
    }
    
    // Called when a peripheral connection fails
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        os_log("Failed to connect to peripheral: %@", peripheral.name ?? "Unknown")
    }
    
    // Called when a peripheral is disconnected
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        os_log("Disconnected from peripheral: %@", peripheral.name ?? "Unknown")
        connectedPeripheral = nil
        discoveredServices.removeAll()
        discoveredServiceNames.removeAll()
        peripheralNames.removeAll()
        filteredPeripheralNames.removeAll()
    }

    // MARK: - CBPeripheralDelegate Methods
    
    // Called when the peripheral discovers its services
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if let error = error {
            os_log("Failed to discover services: %@", error.localizedDescription)
            return
        }
        
        os_log("Discovered services for peripheral: %@", peripheral.name ?? "Unknown")
        
        // Define the UUIDs for Device Information and Battery Service
        let deviceInformationServiceUUID = CBUUID(string: "180A")  // Device Information
        let batteryServiceUUID = CBUUID(string: "180F")  // Battery Service
        
        // Loop through discovered services and fetch their names
        for service in peripheral.services ?? [] {
            discoveredServices.append(service)
            discoveredServiceNames.append(service.uuid.uuidString)
            os_log("Service found: %@", service.uuid.uuidString)
            
            // Check if the service is Device Information or Battery Service
            if service.uuid == deviceInformationServiceUUID {
                os_log("Device Information Service found")
            } else if service.uuid == batteryServiceUUID {
                os_log("Battery Service found")
            } else {
                os_log("Other service found: %@", service.uuid.uuidString)
            }
            
            // If device information service is found, discover characteristics
            if service.uuid == deviceInformationServiceUUID {
                peripheral.discoverCharacteristics([CBUUID(string: "2A00")], for: service)  // Device Name characteristic
            }
        }
    }
    
    // Called when a peripheral discovers characteristics for a given service
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if let error = error {
            os_log("Failed to discover characteristics for service: %@", error.localizedDescription)
            return
        }
        
        os_log("Discovered characteristics for service: %@", service.uuid.uuidString)
        
        // If the Device Information Service is found, read the device name characteristic (2A00)
        for characteristic in service.characteristics ?? [] {
            if characteristic.uuid == CBUUID(string: "2A00") {  // Device Name characteristic
                peripheral.readValue(for: characteristic)
            }
        }
    }
    
    // Called when a characteristic's value is updated
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        if let error = error {
            os_log("Failed to read characteristic value: %@", error.localizedDescription)
            return
        }
        
        if characteristic.uuid == CBUUID(string: "2A00") {  // Device Name characteristic
            if let nameData = characteristic.value, let deviceName = String(data: nameData, encoding: .utf8) {
                os_log("Device Name from Device Info Service: %@", deviceName)
                peripheralNames.append(deviceName)  // Add device name to list
                peripheralNameReceived = true  // Mark the name as received
            }
        }
    }
    
    // Handle Manufacturer Data (custom name or metadata) if present
    func peripheral(_ peripheral: CBPeripheral, didReadValueFor characteristic: CBCharacteristic, error: Error?) {
        if let manufacturerData = characteristic.value, characteristic.uuid == CBUUID(string: "2A29") {
            os_log("Manufacturer Data: %@", convertDataToHexString(manufacturerData))
        }
    }
    
    // Helper function to convert Data to a hexadecimal string for logging
    private func convertDataToHexString(_ data: Data) -> String {
        return data.map { String(format: "%02hhx", $0) }.joined()
    }

    // A method to check if the name was received before moving on
    func waitForName() {
        while !peripheralNameReceived {
            // Implement a small delay or check, or you can return here and handle other logic asynchronously
            usleep(100000) // Sleep for 0.1 seconds for example
        }
    }
}

results, The unknown is being displayed in ios settings with its device name.

Discovered peripheral: [TV] Cia tv
Peripheral name added: [TV] Cia tv
Discovered peripheral: Unknown
Discovered peripheral: Unknown
Discovered peripheral: [TV] Cia tv
Peripheral name added: [TV] Cia tv
Discovered peripheral: Unknown

Share Improve this question edited Jan 8 at 0:25 HangarRash 14.9k5 gold badges17 silver badges55 bronze badges asked Jan 7 at 23:34 Sana NaghashzadehSana Naghashzadeh 111 bronze badge New contributor Sana Naghashzadeh is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Add a comment  | 

1 Answer 1

Reset to default 0

Generally speaking, the peripheral.name property in your code is derived from the CBAdvertisementDataLocalNameKey. This means that if a device advertises its name as part of its advertisement data, it will appear in your scan result - this is what is happening with devices like "Cia tv."

However, not all devices include their name in the advertisement data. In such cases, I believe iOS Settings may display the name using two additional methods:

  • Cached Names: iOS may retrieve the name from previously cached information if the device has been paired or discovered before.
  • GATT Queries: iOS can actively query the remote device's GATT table. Some devices provide their name in the Device Information service (UUID: 0x180A), specifically in the Device Name characteristic (UUID: 0x2A00), even if it is not broadcast in advertisement data.

You can verify if the local name is present in the advertisement data by modifying the didDiscoverPeripheral method:

// Check if the advertisement data contains the local name
if let name = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
    os_log("Advertisement data name: %@", name)
    if !peripheralNames.contains(name) {
        peripheralNames.append(name)
    }
} else {
    os_log("Peripheral name is not in advertisement data.")
}

Here are some relevant discussions and documentation that provide further context:

  • CoreBluetooth fetch device names
  • Advertisement data missing local name
  • Apple Developer forums discussion on peripheral names
  • Handling incorrect peripheral names

本文标签: