Swift5でMacBookからiBeaconを発信するアプリを作る

## はじめに
研究室の入退室管理のためMacbookからiBeaconの発信をしようとしたところ，いくら調べてもSwift3,Objective-C,Node.jsでの発信方法しか見つからずとても苦労したので記事を書くことにしました．
本記事では，タイトルの通りSwift5を使ってMacBookからiBeaconを発信する方法を解説していきます．
なおXcodeのmacOSのAppプロジェクトで実行する前提なので他のプロジェクトで動くかはわかりません．

## 実行環境
MacBook Pro (13-inch, M1, 2020)
Xcode 13.4.1
Swift 5.6.1

## iBeacon管理クラス
以下がiBeacon発信を管理するコードです．

```swift:iBeacon.swift
import Foundation
import CoreBluetooth

class IBeacon: NSObject, CBPeripheralManagerDelegate {

    private var beaconPeripheralData: BeaconData!
    private var peripheralManager: CBPeripheralManager!

    private let BEACON_ID: String = "iBeacon"
    private let uuid: UUID = UUID(uuidString: "AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA")!
    private let MAJOR: UInt16 = 1
    private let MINOR: UInt16 = 1
        
    // Beaconを設定
    func initBeacon() {
        if beaconPeripheralData != nil {
            stopLocalBeacon()
        }
        beaconPeripheralData = BeaconData(proximityUUID: self.uuid, major: self.MAJOR, minor: self.MINOR, measuredPower: -60)
        peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
        beaconStatus = true
    }
    
    // Beaconを停止
    func stopLocalBeacon() {
        peripheralManager.stopAdvertising()
        peripheralManager = nil
        beaconPeripheralData = nil
        beaconStatus = false
        print("end beacon")
    }
 
    // Bluetoothの電源が切り替わった際に実行される処理
    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        if peripheral.state == .poweredOn {            
            print("can use bluetooth")
            if(peripheralManager != nil){
                peripheralManager.startAdvertising(beaconPeripheralData.advertisement)
                print("start advertisement")
            }
        } else if peripheral.state == .poweredOff {
            peripheralManager.stopAdvertising()
            print("bluetooth power off")
        } else if peripheral.state == .unsupported{
            print("bluetooth no support")
        }
    }
    
    // iBeaconの発信が始まると走る関数
    func peripheralManagerDidStartAdvertising(_ peripheral: CBPeripheralManager,error: Error?) {
        if (error != nil) {
            print("Failed to start advertisement" + error!.localizedDescription)
        }
    } 
}

// iBeacon用に自作したクラス
final class BeaconData: NSObject {

    var advertisement: [String: Any]
    
    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 = Data(bytes: buffer, count: buffer.count)
        advertisement = ["kCBAdvDataAppleBeaconKey": data]
    }
}
```

iBeaconクラスのinitBeacon関数を呼び出すことで発信の準備が整い，iBeaconの発信ができる状態になっていたらiBeaconの発信が開始されます．
当然ですがBluetoothがオフになっていた場合は「bluetooth power off」と表示されるだけなので，ちゃんとBluetoothはオンにしておきましょう．

## Xcode側の設定
上記コードを用意してもXcodeでいくつかの設定をしないとコードが正常に動きません．

### Bluetoothの使用の許可
デフォルトの設定ではBluetoothの利用がXcode側で制限されているため，プロジェクトファイルからその制限を解除しなければいけません．

Targets　> Signing　& Capabilities > App Sandbox - Bluetooth　にチェックをつけます．
このチェックをつけないと「bluetooth no support」と出力されてiBeaconが発信されません．
![スクリーンショット 2022-10-25 11.03.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2741818/c7109e03-0ae8-8790-0143-1de78b48ac0d.png)

### info.plistへの追記
Bluetoothの機能を利用する場合，plistに使用用途を記載しないとXcode側でエラーが吐かれてしまいます．
そのためinfo.plistに「Privacy - Bluetooth Always Usage Description」を追加しましょう.

![スクリーンショット 2022-10-25 11.09.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2741818/4c5309be-2b7b-6c87-088f-455cf4eab5a8.png)

## おわりに
初めて記事を書いたので，至らない部分が多々あると思います．
わかりづらい部分や改善点があればコメントで教えていただけると幸いです．


## 参考
- [[iBeacon] Mac を iBeacon 端末にする(Swift3編)
](https://dev.classmethod.jp/articles/ibeacon-mac-beacon-swift/)
- [iBeacon - AppleDeveloper](https://developer.apple.com/ibeacon/)