最近发生了挺多事,也没什么时间更新博客,心中颇有点负疚感。今天就介绍一下前段时间写的一个 demo,主要功能是扫描附近的蓝牙设备,显示相关信息,点击后可以连接设备,并获取更多信息。对 RxSwift 及 BLE 感兴趣的同学可以看看,或有所得。
做过蓝牙相关开发的 iOS 同学们肯定都用过 CoreBluetooth 这个系统框架,使用它的话,几乎所有操作都是通过 delegate 完成的。公司的项目正从 OC 向 Swift 迁移,之前蓝牙部分还全是用 OC 写的,各种 delegate 和 notification 满天飞,看着都头疼。我曾一度想着用 RxSwift 去包装一下 CoreBluetooth,后来发现已经有人做了这件事了,于是就想先写个 demo 看看靠不靠谱。Demo 在这里。
UI 非常简陋,只是随便拿 Storyboard 弄了一下,就不细说了。扫描设备的整个流程大致是这样的:
- 筛选蓝牙状态,将除
.PoweredOn
之外的状态过滤掉 - 扫描设备
- 筛选设备,将已出现过的设备过滤掉
- 将扫描到的新设备添加到设备列表中
- 用设备列表构建一个
dataSource
,并与tableView
绑定 - 将资源添加到
disposeBag
中,以待统一回收处理
这个过程用 Rx 的风格来写大概是这样的:
func bindDataSource() {
manager.rx_state
.filter { $0 == .PoweredOn }
.take(1)
.flatMap { _ in self.manager.scanForPeripherals(nil) }
.filter(isNewPeripheral)
.map { self.scannedPeripherals.append($0) }
.map { [SectionModel(model: "Peripheral", items: self.scannedPeripherals)] }
.bindTo(tableView.rx_itemsWithDataSource(dataSource))
.addDisposableTo(disposeBag)
}
点击 cell 先会取消选中的高亮效果,然后连接相应的设备,如果连接成功就直接跳转到下一个页面,并用segue
把peripheral
传递过去;否则就弹出一个错误提示:
func configDelegate() {
tableView.rx_itemSelected
.subscribeNext { self.tableView.deselectRowAtIndexPath($0, animated: true) }
.addDisposableTo(disposeBag)
tableView.rx_modelSelected(ScannedPeripheral.self)
.asObservable()
.flatMap { $0.peripheral.connect() }
.subscribe(handleEvent)
.addDisposableTo(disposeBag)
}
func handleEvent(event: Event<Peripheral>) {
switch event {
case .Next(let peripheral):
self.performSegueWithIdentifier(SegueId.ShowCharacteristics, sender: peripheral)
case .Error(let error):
let alertController = UIAlertController(title: "Connect Failed", message: "Error: \(error)", preferredStyle: .Alert)
self.presentViewController(alertController, animated: true, completion: nil)
case .Completed:
print("Completed")
}
}
连接了之后主要就是显示该设备所有的characteristics
,它们分别属于哪个service
,有哪些properties
什么的。
流程:
- 发现服务
- 发现特征(对应了硬件那边所谓的通道……)
- 构建
dataSource
,并与tableView
绑定
核心代码:
func bindDataSource() {
guard let peripheral = peripheral else { return }
peripheral.discoverServices(nil)
.flatMap(discoverCharacteristics)
.map { [SectionModel(model: "Characteristic", items: $0)] }
.bindTo(tableView.rx_itemsWithDataSource(dataSource))
.addDisposableTo(disposeBag)
}
func discoverCharacteristics(services: [Service]) -> Observable<[Characteristic]> {
return services
.map { $0.discoverCharacteristics(nil) }
.toObservable()
.switchLatest()
}
有空的话我可能还会为这个 demo 再加些内容,譬如加上监听蓝牙通道、向设备发送数据等功能,或许还会优化一下 UI 和交互,譬如连接蓝牙的时候加个进度动画什么的。大家要是有兴趣的话也欢迎提交 P-R。Have fun ^ ^