みなさまご無沙汰です。かれこれ前回のブログから2ヶ月近く経ってました...。
その間に、シリコンバレーに起業するための研修などいってたのですが、その記事はいつか書きます...。
今回はあるプロジェクトでiOSのBluetooth周りを担当することになったので、その知見をまとめておきます。
通信関係は、バージョンが上がるたびに変わったりするので、過去の遺産になるのも早そうだけど....😇
環境情報
- Xcode 10.1 (10B61)
- Swift4.2
実装
とりあえず Bluetooth を使うには CoreBluetooth
が必要になるので、importします
import CoreBluetooth
そのほかに継承するべきクラスとして、CBCentralManagerDelegate
, CBPeripheralDelegate
があるので、継承します。
ここで、すべてのファイルに全ての implementメソッドを書いてもいいのですが、チームでソース管理をしたりすると、何かと面倒なので、extension
として分割することにしました。
ディレクトリ構成としては、以下の通りにしておきました。
ProjectDir/ ┠ ViewController.swift ┠ BluetoothController.swift ┗ BluetoothExt/ ┠ CBCentralManagerDelegate.swift ┗ CBPeripheralDelegate.swift
また、Bluetooth 関連のクラスは複数のクラスから呼び出されることが想定されます。その時に毎回インスタンスが生成されると、ちょっとややこしいのでBluetooth関連のクラスはシングルトンにして、生成は一回のみ、あとは初回生成されたインスタンスを使うことにします。
・ViewController.swift
import UIKit class ViewController: UIViewController { /** * Bluetooth制御クラスインスタンス **/ var bluetoothController = BluetoothController.shared() }
・BluetoothController.swift
import UIKit import CoreBluetooth class BluetoothController: UIViewController { // シングルトンクラスを生成 private static let sharedInstance : BluetoothController = { return BluetoothController() }() // Sharedインスタンスを返す class func shared() -> BluetoothController { return self.sharedInstance } /** * Bluetooth **/ var centralManager : CBCentralManager! // Bluetooth_CentralManager var targetPeripheral : CBPeripheral! // 接続先のBluetooth情報を保持 var targetService : CBService! // 対象BLE端末の接続サービスを保持 var targetCharacteristic : CBCharacteristic! // 対象のBLE端末のCharacteristicを保持 }
・ CBCentralManagerDelegate.swift
import UIKit import CoreBluetooth /** * Bluetooth制御に関するデリゲートをViewControllerに拡張 */ extension BluetoothController : CBCentralManagerDelegate { /** * CentralManagerのステートが更新された時に呼び出されるコールバック関数 **/ public func centralManagerDidUpdateState(_ central: CBCentralManager) { // セントラルの状態を取得 switch central.state { case .poweredOff: // Bluetooth is OFF. break case .poweredOn: // Bluetooth is ON. // Peripheralが使用できるサービスを指定 // Peripheral端末の検索を開始 break case .resetting: // Bluetooth is Resetting. Connection was momentarily lost. break case .unauthorized: // Not Authorized. break case .unsupported: // Bluetooth is not Support. break case .unknown: // Somethings Error. break default: break } } /** * PheripheralのScanが成功した時 **/ public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { // BLE端末の検索を止める self.centralManager.stopScan() // 接続先のBLE端末としてデータ保持 self.targetPeripheral = peripheral // 接続開始 self.centralManager.connect(self.targetPeripheral, options: nil) } /** * Pheripheralに接続した時 **/ public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { // 検索に関するメソッドデリゲートを自分自身にバインド self.targetPeripheral.delegate = self // 対象のBLE端末の提供しているサービスを検索 self.targetPeripheral.discoverServices(nil) } /** * Pheripheralと接続が切れた時 **/ public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { print(#function) if let _ = error { return } } /** * Pheripheralへの接続が失敗した時 **/ public func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { print(#function) if let _ = error { return } } }
・ CBPeripheralDelegate.swift
import UIKit import CoreBluetooth /** * 接続先のBLE端末間通信制御デリゲートをViewControllerに拡張 **/ extension BluetoothController : CBPeripheralDelegate { /** * BLE端末が提供してるサービスの検索が終わった **/ func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { print(#function) if let _ = error { return } // 取得したサービスを一覧で表示 for service in peripheral.services! { self.targetService = service // BLE端末検索に関するメソッドを自分自身にバインド self.targetPeripheral.delegate = self // 対象BLE端末のCharacteristicの検索 self.targetPeripheral.discoverCharacteristics(nil, for: self.targetService) } } /** * 対象BLE端末のCharacteristicの検索 **/ func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { print(#function) if let _ = error { return } for characteristic in service.characteristics! { // poproのCharacteristicCBUUIDと対象のCBUUIDが同じか } /*** peripheralの情報(GATT情報)はiOS端末がキャッシュする 新規情報を得るためにはSwiftから操作することはできない iOS端末のBluetoothスイッチをOn/Offするしか今の所方法はない ***/ } /** * Characteristic読み込み時のコールバック関数 **/ func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { print(#function) if let _ = error { return } } /** * BLE端末へのCharacteristic書き込みが完了した時に呼び出されるコールバック関数 **/ func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { print(#function) if let _ = error { return } } }
基本的にはこれだけでBluetooth通信を使えるようになりますが、自分で接続しようとしているBLE端末のUUIDなどを指定して検索しないと、特定の端末に接続することはできないので要注意です。