最近發(fā)生了挺多事,也沒什么時間更新博客秀又,心中頗有點負(fù)疚感单寂。今天就介紹一下前段時間寫的一個 demo,主要功能是掃描附近的藍(lán)牙設(shè)備吐辙,顯示相關(guān)信息宣决,點擊后可以連接設(shè)備,并獲取更多信息昏苏。對 RxSwift 及 BLE 感興趣的同學(xué)可以看看尊沸,或有所得。
做過藍(lán)牙相關(guān)開發(fā)的 iOS 同學(xué)們肯定都用過 CoreBluetooth 這個系統(tǒng)框架贤惯,使用它的話洼专,幾乎所有操作都是通過 delegate 完成的。公司的項目正從 OC 向 Swift 遷移孵构,之前藍(lán)牙部分還全是用 OC 寫的壶熏,各種 delegate 和 notification 滿天飛,看著都頭疼浦译。我曾一度想著用 RxSwift 去包裝一下 CoreBluetooth,后來發(fā)現(xiàn)已經(jīng)有人做了這件事了溯职,于是就想先寫個 demo 看看靠不靠譜精盅。Demo 在這里。
UI 非常簡陋谜酒,只是隨便拿 Storyboard 弄了一下叹俏,就不細(xì)說了。掃描設(shè)備的整個流程大致是這樣的:
- 篩選藍(lán)牙狀態(tài)僻族,將除
.PoweredOn
之外的狀態(tài)過濾掉 - 掃描設(shè)備
- 篩選設(shè)備粘驰,將已出現(xiàn)過的設(shè)備過濾掉
- 將掃描到的新設(shè)備添加到設(shè)備列表中
- 用設(shè)備列表構(gòu)建一個
dataSource
,并與tableView
綁定 - 將資源添加到
disposeBag
中述么,以待統(tǒng)一回收處理
這個過程用 Rx 的風(fēng)格來寫大概是這樣的:
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 先會取消選中的高亮效果蝌数,然后連接相應(yīng)的設(shè)備,如果連接成功就直接跳轉(zhuǎn)到下一個頁面度秘,并用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")
}
}
連接了之后主要就是顯示該設(shè)備所有的characteristics
,它們分別屬于哪個service
剑梳,有哪些properties
什么的唆貌。
流程:
- 發(fā)現(xiàn)服務(wù)
- 發(fā)現(xiàn)特征(對應(yīng)了硬件那邊所謂的通道……)
- 構(gòu)建
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 再加些內(nèi)容垢乙,譬如加上監(jiān)聽藍(lán)牙通道锨咙、向設(shè)備發(fā)送數(shù)據(jù)等功能,或許還會優(yōu)化一下 UI 和交互追逮,譬如連接藍(lán)牙的時候加個進(jìn)度動畫什么的酪刀。大家要是有興趣的話也歡迎提交 P-R粹舵。Have fun ^ ^