96SEO 2026-07-01 23:12 1
哎,兄弟,今天聊点儿蓝牙的事儿,别怕,我就像老友坐在咖啡馆里给你唠嗑,顺带把 CoreBluetooth 那堆坑给你捞出来。
先说说为啥要用 CoreBluetooth说实话,iOS 自带的蓝牙库就是这玩意儿。

想跟手环、心率计、或者自制的温度传感器聊聊天?
只Neng靠它。
别忘了 iOS 13+ 要写 NSBluetoothAlwaysUsageDescription,不然系统直接把你踢出局。
NSBluetoothAlwaysUsageDescription
需要使用蓝牙连接外设进行数据通讯
Ru果你还需要后台持续蓝牙通信:
需要在 .entitlements 加上 Background Modes → Uses Bluetooth LE。
还有 Info.plist 里加个 UIBackgroundModes = bluetooth-central。
初始化 CBCentralManager。
等蓝牙打开后开始扫描。
发现目标外设,立马连上去。
发现 Service,然后是 Characteristic。
挑出 Write 特征和 Notify 特征。
打开通知,开始收发数据。
三、代码:BluetoothManagerimport Foundation
import CoreBluetooth
final class BluetoothManager: NSObject {
static let shared = BluetoothManager
// MARK: - Public callbacks
var onStateChanged: -> Void)?
var onDiscovered: -> Void)?
var onConnected: -> Void)?
var onDisconnected: -> Void)?
var onReceiveData: -> Void)?
// MARK: - CoreBluetooth
private var central: CBCentralManager!
private var peripheral: CBPeripheral?
private var writeChar: CBCharacteristic?
private var notifyChar: CBCharacteristic?
// MARK: - Config
private let targetServiceUUID = CBUUID
private let writeCharUUID = CBUUID
private let notifyCharUUID = CBUUID
private let targetNamePrefix = "MyBLE"
// MARK: - Scan control
private var scanTimer: Timer?
private let scanTimeout: TimeInterval = 10
// MARK: - Write throttle
private var writeQueue: =
private var isWriting = false
private override init {
super.init
let queue = DispatchQueue
central = CBCentralManager
}
// MARK: - Public APIs
func startScan {
guard central.state == .poweredOn else { return }
stopScan
central.scanForPeripherals(withServices:,
options:)
startScanTimeoutTimer
print
}
func stopScan {
if central.isScanning { central.stopScan }
scanTimer?.invalidate
scanTimer = nil
print
}
func connect {
stopScan
peripheral = p
peripheral?.delegate = self
central.connect(p,
options:)
print …")
}
func disconnect {
guard let p = peripheral else { return }
central.cancelPeripheralConnection
print
}
func send {
guard let p = peripheral,
let w = writeChar else { return }
let type: CBCharacteristicWriteType = withResponse ? .withResponse : .withoutResponse
writeQueue.append
pumpWriteQueue(peripheral:p,
characteristic:w,
type:type)
}
// MARK: - Private helpers
private func startScanTimeoutTimer {
scanTimer?.invalidate
scanTimer = Timer.scheduledTimer(withTimeInterval:
scanTimeout,
repeats:false){ _ in
self?.stopScan
print
}
RunLoop.main.add
}
private func pumpWriteQueue(peripheral p: CBPeripheral,
characteristic c: CBCharacteristic,
type t :CBCharacteristicWriteType){
guard !isWriting else { return }
guard !writeQueue.isEmpty else { return }
isWriting = true
let packet = writeQueue.removeFirst
p.writeValue(packet,
for:c,
type:t)
if t == .withoutResponse{
DispatchQueue.global.asyncAfter+0.02){
self.isWriting = false
self.pumpWriteQueue(peripheral:p,
characteristic:c,
type:t)
}
}
}
}
CBCentralManagerDelegate 实现
extension BluetoothManager:CBCentralManagerDelegate{
func centralManagerDidUpdateState{
onStateChanged?
switch central.state{
case .poweredOn:
print
default:
print")
stopScan
peripheral=nil; writeChar=nil; notifyChar=nil
}
}
func centralManager(_ central:CBCentralManager,
didDiscover peripheral:NSObject,
advertisementData:,
rssi RSSI:NSNumber){
if let name=peripheral.name,
name.hasPrefix{
onDiscovered?
connect
return
}
// Yi经通过 Service UUID 过滤,这里基本安全
onDiscovered?
}
func centralManager(_ central:CBCentralManager,
didConnect peripheral:NSObject){
onConnected?
peripheral.discoverServices
}
func centralManager(_ central:CBCentralManager,
didFailToConnect peripheral:NSObject,
error:Error?){
onDisconnected?
}
func centralManager(_ central:CBCentralManager,
didDisconnectPeripheral peripheral:NSObject,
error:Error?){
onDisconnected?
// 简单重连策略:延迟两秒再连一次
DispatchQueue.global.asyncAfter+2){
self.central.connect
}
}
}
CBPeripheralDelegate 实现
extension BluetoothManager:CBPeripheralDelegate{
func peripheral{
guard error==nil else {return}
for s in peripheral.services ?? where s.uuid==targetServiceUUID{
peripheral.discoverCharacteristics
}
}
func peripheral{
guard error==nil else {return}
for c in service.characteristics ?? {
if c.uuid==writeCharUUID{writeChar=c}
if c.uuid==notifyCharUUID{notifyChar=c}
}
if let n=notifyChar{peripheral.setNotifyValue}
}
func peripheral{
guard error==nil else {return}
let data=characteristic.value ?? Data
onReceiveData?
}
func peripheral{
isWriting=false
if let w=writeChar{
pumpWriteQueue,
characteristic:w,type:.withResponse)
}
}
}
四、调用示例
final class ViewController:UIViewController{
override func viewDidLoad{
super.viewDidLoad
let ble=BluetoothManager.shared
ble.onStateChanged={state in
print
if state==.poweredOn{ble.startScan}
}
ble.onConnected={per in
print
}
ble.onReceiveData={data,ch in
let hex=data.map{String}.joined
print):",hex)
}
ble.onDisconnected={per,error in
print
}
}
func sendCommand{
// 举例发送一段十六进制指令
let hex="aabbccdd"
let data=Data") //
见下方
BluetoothManager.shared.send
}
}
Dive into Data ↔︎ Hex 小技巧
extension Data{
init{
var clean=hexString.replacingOccurrences
var d=Data
while clean.count>=2{
let byteStr=String)
clean.removeFirst
if let b=UInt8{d.append}
}
self=d
}
}
五、常见坑 & 注意事项
) 强烈建议:扫描时指定 Service UUID
geng快、geng省电、geng准确。
Ru果直接扫 nil,你会kan到成千上万的无关设备,体验瞬间降到负分。
) 不要用peripheral.name 当唯一标识
Name 有可Neng为空,也可Neng改名。靠谱的办法是:
保存外设的 identifier。
或使用 Service/Characteristic 的唯一组合来匹配。
) 写入速度太快会被外设“抛弃”AHA,这种情况我遇到过好多次。外设只Neng每隔几毫秒处理一次指令,你Ru果疯狂写进去,它根本来不及回包,结果就是丢数据。
#解决办法# 用上面代码里的写队列 + 节流,每次写完等个小延迟再继续。一般 20~30ms 足够了。 为什么百度不收录这篇文章?说实话,这事儿挺尴尬的:
A:内容重复率太高! 百度爱新鲜,Ru果你的文字和Yi有博客几乎一模一样,它会把你当作抄袭,然后直接不给收录。
B:缺少结构化数据 标题层级乱套、没有合适的 H 标签,会让爬虫抓取不到重点信息。
C:页面加载慢 大量未压缩的代码块、图片或者 JS dou会拖慢速度,被判定为“体验差”。所以Zui好压缩一下再放到服务器上。
D:没有关键词密度 本文里虽然出现了 “CoreBluetooth”“BLE”“Swift”,但Ru果页面整体关键词分布不均衡,也会被打低分。适当多出现几次核心词,可提升机会。
E:缺少 meta 描述 搜索引擎喜欢kan到简短明了的 description,没有的话就算了吧。)
) 背景模式注意事项- 必须在 Xcode 的 Capabilities 打开 “Background Modes → Uses Bluetooth LE”。
- 同时在 Info.plist 添加 "bluetooth-central".
- Ru果想让 App 在锁屏后还Neng保持通信,还得实现 CBCentralManagerOptionShowPowerAlertKey:true.
AFAIK,大多数错误douNeng通过 delegate 回调捕获。比如:
.unsupported: 设备硬件不支持 BLE,别硬撑了;
.unauthorized: 用户拒绝蓝牙权限,需要弹窗引导;
.resetting: 系统正在重置蓝牙栈,此时Zui好暂停所有操作;
六、实战小贴士
#打印日志永远是好习惯# ——尤其是 RSSI 值,你Ke以根据信号强度决定是否重连或切换外围设备;
#使用 DispatchQueue.main geng新 UI# ——不要把 UI 操作丢到后台队列,否则会闪退;
#保持 Peripheral 引用# ——一定要把外设对象保存在属性里不然一旦 GC 回收,你就再也找不到它了;
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback