[iBeacon] Mac を iBeacon 端末にする(Swift3編)

1 はじめに

iBeaconの発信(ペリフェラル側)は、Macでも作成可能です。この事については、既に、ここDevelopers.IOでも、紹介されておりますので、詳しくは、こちらをご参照下さい。


[iOS 7] [iBeacon] Mac を Beacon 端末にする

既に、語り尽くされた感のあるiBeaconの話ですが、今回これをSwift3で書いてみましたので紹介させて下さい。

なお、CBPeripheralManagerYosemite (10.10) で動作しなかった問題については、改善されており、本記事で紹介するコードは、El Capitan(10.11) 及びmacOS Sierra(10.12) で問題なく動作させる事ができました。


[iOS][OS X] Yosemite で Beacon アプリを動作させる
CBPeripheralManager startAdvertising not working on OS X yosemite

2 検証環境

MacBook Air (13-inch, Mid 2013)
OS X El Capitan(10.11.6) 及び、macOS Sierra (10.12.2)
iPhone 6 (iOS 10.2)
Xcode 8.2.1 (8C1002)

3 基本的な手順

iBeaconの発信は、超簡単に言ってしまうと次の手順だけです。

  • CBPeripheralManagerのインスタンス生成
  • startAdvertising(_:)で発信開始
  • stopAdvertising()で発信停止

そして、それぞれコードは、下記のようになります。

// インスタンスの生成
let manager = CBPeripheralManager(delegate: self, queue: nil)
let beaconData = ・・・アドバタイズメントデータの生成
// 発信開始
manager.startAdvertising(beaconData as! [String : Any]?)
// 発信停止
manager.stopAdvertising()

4 アドバタイズメントデータ

MacでiBeaconの作業をする場合に、少し面倒なのは、発信開始時に使用するstartAdvertising(_:)のパラメータに渡すアドバタイズメントデータの生成です。

iOSでは、CLBeaconRegionが利用可能なため、比較的簡単に生成できるのですが、OS X(macOS)では、これを自前で実装する必要があります。

//参考:iOSの場合の例
let uuid = UUID.init(uuidString: "myuuid")
let beaconRegion = CLBeaconRegion.init(proximityUUID: uuid!, major: 1, minor: 1, identifier: "jp.classmethod.myregion")
let beaconData = NSDictionary(dictionary: beaconRegion.peripheralData(withMeasuredPower: nil))

manager.startAdvertising(beaconData as? [String : Any] )

Mac用に自前でアドバタイズメントデータを生成するクラスは次のとおりです。

import CoreLocation

final class BeaconData: NSObject {

    var advertisement: NSDictionary!

    init(proximityUUID: UUID?, major: UInt16?, minor: UInt16?, measuredPower: Int8?) {
        var buffer = [CUnsignedChar](repeating: 0, count: 21)
        (proximityUUID! as NSUUID).getBytes(&buffer)
        buffer[16] = CUnsignedChar(major! >> 8)
        buffer[17] = CUnsignedChar(major! & 255)
        buffer[18] = CUnsignedChar(minor! >> 8)
        buffer[19] = CUnsignedChar(minor! & 255)
        buffer[20] = CUnsignedChar(bitPattern: measuredPower!)
        let data = NSData(bytes: buffer, length: buffer.count)
        advertisement = NSDictionary(object: data, forKey: "kCBAdvDataAppleBeaconKey" as NSCopying)
    }
}

そして、上記クラスは、下記のように利用可能です。

let uuid = UUID.init(uuidString: "myuuid")
let beaconData = BeaconData(proximityUUID: uuid, major: 1, minor: 1, measuredPower: -60)
manager.startAdvertising(beaconData.advertisement as! [String : Any]?)

5 CBPeripheralManagerDelegate

CBPeripheralManagerのインスタンスを生成する際に、delegateの指定がありますが、ここには、CBPeripheralManagerDelegateプロトコルを実装したクラスを設定します。

CBPeripheralManagerDelegateプロトコルでは、peripheralManagerDidUpdateState(_:)でBluetoothの状態を受け取ることができます。

サンプルでは、ここで、Bluetoothの電源がONで使用可能な事を確認し、操作ボタンを有効にしました。

class ViewController: NSViewController, CBPeripheralManagerDelegate {

    // ・・・省略

    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        if peripheral.state == CBPeripheralManagerState.poweredOn {
            // 使用可能なのでボタンを有効にする
            startButton.isEnabled = true
            stopButton.isEnabled = true
        }
    }

    // ・・・省略
}

6 最後に

iBeaconの発信(ペリフェラル側)の作成アドバタイズメントのデータの生成以外は、iOSの場合と同じです。そして、swiftで書くと非常にシンプルでした。

コードは下記に置いています。気になるところが有りましたら、ぜひ教えてやってください。
github [GitHub] https://github.com/furuya02/iBeaconSender

7 参考資料


API Reference CBPeripheralManager
[iOS 7] [iBeacon] Mac を Beacon 端末にする