RxSwift 与 BLE 实战——VisualBlue

最近发生了挺多事,也没什么时间更新博客,心中颇有点负疚感。今天就介绍一下前段时间写的一个 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 先会取消选中的高亮效果,然后连接相应的设备,如果连接成功就直接跳转到下一个页面,并用segueperipheral传递过去;否则就弹出一个错误提示:

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 ^ ^

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏木子昭的博客

往"某度文库"上传资源之前,请先做好这些...想到一句好玩的话:知道是你干的,只是懒得抓你!如果你喜欢python,喜欢故事,请点赞或关注我!您的支持是对作者最大的鼓励!

先讲个相关的故事:匿名黑客的"复仇行动" 2010年12月10日,黑客组织匿名者发布了一条消息,解释了他们发起最近一次代号为”复仇行动”的攻击的大致动机(Pr...

38213
来自专栏偏前端工程师的驿站

Thinking in React Implemented by Reagent

1083
来自专栏進无尽的文章

扒虫篇- Bug日志 Ⅷ

不执行的原因是 在VC中使用这个ImageUploaderManager时,需要设置为全局变量,如果是局部变量的话,很快会被销毁掉,其中的代理自然不会执行了。

1973
来自专栏哲学驱动设计

产品前端重构(TypeScript、MVC框架设计)

最近两周完成了对公司某一产品的前端重构,本文记录重构的主要思路及相关的设计内容。 公司期望把某一管理类信息系统从项目代码中抽取、重构为一个可复用的产品。该系统的...

2698
来自专栏张善友的专栏

使用OData协议查询Windows日志

OData开放数据协议是微软针对Google的GData推出的,旨在推广Web程序数据库格式标准化的开放数据协议,微软将 OData 定义为基于 HTTP、A...

2439
来自专栏androidBlog

360面试总结(Android)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

911
来自专栏小巫技术博客

应用被强杀了怎么办

972
来自专栏测试开发架构之路

Android软件测试Monkey测试工具

前言: 最近开始研究Android自动化测试方法,对其中的一些工具、方法和框架做了一些简单的整理,其中包括android测试框架、CTS、Monkey、Monk...

1K11
来自专栏自然语言处理

微博话题爬取与存储分析(上)

本文基于python以新浪微博为数据平台,从数据采集、关键字提取、数据存储三个角度,用最简单的策略来挖掘我们的“黄金”。

3882
来自专栏程序猿DD

极大提高工作效率的几十种神兵利器

来源:https://www.liutf.com/posts/3720794851.html

2323

扫码关注云+社区

领取腾讯云代金券