環境
- Xcode 12.3
- iPhone 6 Plus
- iOS 12.6
目的
- iBeacon を用いた勤怠管理システムを構築するための実験として現在利用していない試験機 (iOS 12.6)の iPhone を iBeacon の Region として動作させたい
- いずれは raspberry pi に置き換える予定なので最低限動けばOK
ポイント
Info.plist の設定
- iOS 13 以降では
Privacy - Bluetooth Always Usage Description
の設定が必要 - 今回は iOS 12.6 の端末で動けば良いので実は不要だが一応設定
Region の作成
- Region: Central 側が出入りを確認可能な「地域」
- UUID(地域を一意に定めるもの)、major・minor(ビーコン識別用の値)、identifier(内部管理用のIDで信号には含まれない)が存在
- iOS 13前後で仕様が異なるため今回は条件分けを行った
PeripheralManager.swift
// Region を作成する
private func createBeaconRegion() -> CLBeaconRegion {
guard let uuid = UUID(uuidString: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") else { fatalError() }
let identifier = "xxx.xxxx.xxxxx.xxxxx"
if #available(iOS 13.0, *) {
return CLBeaconRegion(uuid: uuid, identifier: identifier)
}
else {
return CLBeaconRegion(proximityUUID: uuid, identifier: identifier)
}
}
CBPeripheralManagerDelegate
-
CBPeripheralManager
インスタンスを通じて通信を行うが、その際 Delegate を実装しておく必要がある -
func peripheralManagerDidUpdateState(_:)
はCBPeripheralManager
インスタンス の状態が変化した際に呼ばれる-
CBPeripheralManager
インスタンスが生成されたタイミングでも呼ばれるため初期化処理には注意が必要 - 今回は単純に BLE ON になったらアドバタイズ開始、それ以外ではアドバタイズ停止するように
-
-
CBPeripheralManagerDelegate
を適用するクラスはNSObjectProtocol
に準拠しておく必要があるため注意
PeripheralManager.swift
extension PeripheralManager: CBPeripheralManagerDelegate {
// Bluetooth の電源ON/OFF または 接続状態が切り替わった場合に呼び出されるメソッド
// CBPeripheralManager のインスタンスが生成された時点で一度呼び出される
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
switch peripheral.state {
case .poweredOn: self.startAdvertising()
default: self.stopAdvertising()
}
}
}
その他
-
PeripheralManager
クラスをシングルトンとして保持- 単一の Region としての役割のみを期待
- (ないとは思うが)拡張する際にはどの画面からでも同一のマネージャクラスにアクセスできるように
-
startAdvertising()
stopAdvertising()
を外部から参照可能に- (ないとは思うが)拡張する際に利便のよいように
-
setupManager()
を公開-
CBPeripheralManager
インスタンスが生成されるとその時点で状態監視が始まる -
PeripheralManager
インスタンスと同時にCBPeripheralManager
インスタンスを生成すると処理の順序が交錯し分かりづらい
-
実装全体
PeripheralManager.swift
import CoreLocation
import CoreBluetooth
import Foundation
// MARK: - PeripheralManager
final class PeripheralManager: NSObject {
static let shared = PeripheralManager()
func setupManager() {
if self.manager == nil {
let region = self.createBeaconRegion()
self.beaconPeripheralData = region.peripheralData(withMeasuredPower: nil)
self.manager = CBPeripheralManager(delegate: self, queue: nil)
}
}
// アドバタイズを開始する
func startAdvertising() {
if let manager = self.manager,
let data = self.beaconPeripheralData as? [String : Any] {
manager.startAdvertising(data)
}
}
// アドバタイズを停止する
func stopAdvertising() {
if let manager = self.manager {
manager.stopAdvertising()
}
}
// MARK: - Private
private var beaconPeripheralData: NSDictionary?
private var manager: CBPeripheralManager?
// Region を作成する
private func createBeaconRegion() -> CLBeaconRegion {
guard let uuid = UUID(uuidString: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX") else { fatalError() }
let identifier = "xxx.xxxx.xxxxx.xxxxx"
if #available(iOS 13.0, *) {
return CLBeaconRegion(uuid: uuid, identifier: identifier)
}
else {
return CLBeaconRegion(proximityUUID: uuid, identifier: identifier)
}
}
}
// MARK: - CBPeripheralManagerDelegate
extension PeripheralManager: CBPeripheralManagerDelegate {
// Bluetooth の電源ON/OFF または 接続状態が切り替わった場合に呼び出されるメソッド
// CBPeripheralManager のインスタンスが生成された時点で一度呼び出される
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
switch peripheral.state {
case .poweredOn: self.startAdvertising()
default: self.stopAdvertising()
}
}
}
AppDelegate.swift
import UIKit
// MARK: - AppDelegate
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// MARK: - UIApplicationDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// PeripheralManager を設定する
PeripheralManager.shared.setupManager()
return true
}
}