iOS中从BLE设备获取数据的方法(含双服务多特征Swift示例)
Alright, let's walk through exactly how to fetch data from your BLE device (with two services — one having 3 characteristics, the other 2) using Swift. I’ll break this down step by step, fixing and building on the code snippet you shared.
Prerequisite: Connect to the Peripheral & Set Delegate
First, make sure you’ve successfully scanned and connected to your BLE peripheral via CBCentralManager. Critical step: Set the peripheral’s delegate to your view controller so you can receive all the BLE callback events:
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { peripheral.delegate = self // Don’t skip this! peripheral.discoverServices(nil) // Start discovering all services on the peripheral }
Step 1: Discover Target Services
When the peripheral’s services are found, the didDiscoverServices callback fires. Instead of blindly processing all services, you can filter for only the ones you care about (using their UUIDs provided by your device’s manufacturer) to optimize performance:
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { if let error = error { print("Failed to discover services: \(error.localizedDescription)") return } // Replace these with your actual service UUIDs let targetServiceUUIDs = [ CBUUID(string: "YOUR_FIRST_SERVICE_UUID"), CBUUID(string: "YOUR_SECOND_SERVICE_UUID") ] peripheral.services?.forEach { service in if targetServiceUUIDs.contains(service.uuid) { // Discover all characteristics for this target service peripheral.discoverCharacteristics(nil, for: service) } } }
Step 2: Discover Characteristics for Each Service
Once a service’s characteristics are discovered, didDiscoverCharacteristicsFor triggers. Here, you should check each characteristic’s properties before attempting to read or subscribe to it — not all characteristics support every operation:
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { if let error = error { print("Failed to discover characteristics: \(error.localizedDescription)") return } service.characteristics?.forEach { characteristic in print("Found characteristic: \(characteristic.uuid), properties: \(characteristic.properties)") // Only read if the characteristic supports reading if characteristic.properties.contains(.read) { peripheral.readValue(for: characteristic) } // Only enable notifications if the characteristic supports them if characteristic.properties.contains(.notify) { peripheral.setNotifyValue(true, for: characteristic) } } }
Note on your original code: You were calling readValue and setNotifyValue for every characteristic regardless of its properties — this can lead to unnecessary errors. Always check the properties first!
Step 3: Handle Data Updates (Read & Notifications)
When a characteristic’s value is read or updated via notifications, didUpdateValueFor fires. This is where you’ll parse the raw data into usable values. Your original code had a random write operation here — replace that with logic tailored to your device’s data format:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { if let error = error { print("Failed to get characteristic value: \(error.localizedDescription)") return } guard let data = characteristic.value else { print("Characteristic has no data") return } // Parse data based on the characteristic's UUID switch characteristic.uuid.uuidString { case "CHARACTERISTIC_1_UUID": // Example: Convert data to a UTF-8 string if let text = String(data: data, encoding: .utf8) { print("Characteristic 1 data: \(text)") } case "CHARACTERISTIC_2_UUID": // Example: Convert data to a UInt8 number let value = data.withUnsafeBytes { $0.load(as: UInt8.self) } print("Characteristic 2 data: \(value)") case "CHARACTERISTIC_3_UUID": // Handle your 3rd characteristic here case "CHARACTERISTIC_4_UUID": // Handle your 4th characteristic here case "CHARACTERISTIC_5_UUID": // Handle your 5th characteristic here default: print("Unknown characteristic: \(characteristic.uuid)") } }
If your device requires a command to send data: If you need to write a specific value to a characteristic to trigger data (like your original code’s ? character), only do this for the specific characteristic that expects it — not every one.
Step 4: Verify Notification State
When you enable/disable notifications for a characteristic, didUpdateNotificationStateFor triggers. Use this to confirm the state change:
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) { if let error = error { print("Failed to update notification state: \(error.localizedDescription)") return } let status = characteristic.isNotifying ? "enabled" : "disabled" print("Notification status for \(characteristic.uuid): \(status)") }
Key Tips for Success
- Use correct UUIDs: Always use the exact service/characteristic UUIDs provided by your BLE device’s manufacturer — generic UUIDs won’t work.
- Bluetooth permissions: Add
NSBluetoothAlwaysUsageDescriptionto yourInfo.plist(iOS 13+) to grant your app access to Bluetooth. - Background support: If you need to receive BLE data in the background, enable the
Uses Bluetooth LE accessoriesbackground mode in your app’s capabilities. - Error handling: Never skip error checks in callbacks — they’ll help you debug connection or data retrieval issues quickly.
内容的提问来源于stack exchange,提问作者Diwakar Reddy

