如何使用 iPad 5 in Swift 4 来宣传 iBeacon

How to advertise an iBeacon using an iPad 5 in Swift 4

所以我找到了一个关于使用 iBeacons 的好教程,我创建了一个接收器和一个广播器,但是当我 运行 代码时,我看不到有任何信标正在广播。有没有人有好的解决方案可以解决我的问题?

import UIKit
import CoreLocation
import CoreBluetooth
import Foundation

/// ibeacon class
class iBeaconConfiguration
{
    // You can use uuidgen in terminal to generate new one.
    static let uuid = UUID(uuidString: "7FA08BC7-A55F-45FC-85C0-0BF26F899530")!

    private init() {}
}

如果您想知道,上面的 class 仅用于 UUID

class BroadcastViewController: UIViewController {
    fileprivate var broadcasting: Bool = false
    fileprivate var beacon: CLBeaconRegion?
    fileprivate var peripheralManager: CBPeripheralManager?
    @IBOutlet var statusLabel: UILabel!
    @IBOutlet var triggerButton: UIButton!

    override func viewDidAppear(_ animated: Bool) {
      super.viewDidAppear(animated)
      self.setNeedsStatusBarAppearanceUpdate()
    }

override func viewDidLoad() {
    super.viewDidLoad()

    statusLabel = UILabel()
    triggerButton = UIButton(frame: CGRect(x: 130, y: 10, width: 100, height: 50))
    triggerButton.backgroundColor = UIColor.blue
    triggerButton.addTarget(self, action: #selector(broadcastBeacon(sender:)), for: .touchUpInside)
    self.view.addSubview(triggerButton)

    let button = UIButton(frame: CGRect(x: 10, y: 10, width: 50, height: 50))
    button.backgroundColor = UIColor.red
    button.addTarget(self, action: #selector(dismiss1), for: .touchUpInside)
    self.view.addSubview(button)

    self.view.backgroundColor = UIColor.white
    let UUID: UUID = iBeaconConfiguration.uuid

    let major: CLBeaconMajorValue = CLBeaconMajorValue(arc4random() % 100 + 1)
    let minor: CLBeaconMinorValue = CLBeaconMinorValue(arc4random() % 2 + 1)

    self.beacon = CLBeaconRegion(proximityUUID: UUID, major: major, minor: minor, identifier: "tw.darktt.beaconDemo")

    self.peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
}

deinit
{
    self.beacon = nil
    self.peripheralManager = nil
}

override func didReceiveMemoryWarning()
{
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

    @IBAction func dismiss1() {
    //self.dismiss(animated: true, completion: nil)
    present(ReceiverViewController(), animated: true, completion: nil)
    }
}

// MARK: - Status Bar -
extension BroadcastViewController {
override var preferredStatusBarStyle: UIStatusBarStyle
{
    if self.broadcasting {
        return .lightContent
    }

    return .default
}

override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation
{
    return .fade
}

override var prefersStatusBarHidden: Bool
{
    return false
}
}

//MARK: - Actions -
extension BroadcastViewController {
@IBAction fileprivate func broadcastBeacon(sender: UIButton) -> Void
{
    let state: CBManagerState = self.peripheralManager!.state

    if (state == .poweredOff && !self.broadcasting) {
        let OKAction: UIAlertAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)

        let alert: UIAlertController = UIAlertController(title: "Bluetooth OFF", message: "Please power on your Bluetooth!", preferredStyle: .alert)
        alert.addAction(OKAction)

        self.present(alert, animated: true, completion: nil)

        return
    }

    let titleFromStatus: (Void) -> String = {
        let title: String = (self.broadcasting) ? "Start" : "Stop"

        return title + " Broadcast"
    }

    let buttonTitleColor: UIColor = (self.broadcasting) ? UIColor.blue : UIColor.white

    sender.setTitle("nil", for: .normal)
    sender.setTitleColor(buttonTitleColor, for: .normal)

    let labelTextFromStatus: (Void) -> String = {
        let text: String = (self.broadcasting) ? "Not Broadcast" : "Broadcasting..."

        return text
    }

    self.statusLabel.text = "broadcast started"

    let animations: () -> Void = {
        let backgroundColor: UIColor = (self.broadcasting) ? UIColor.white : UIColor.blue

        self.view.backgroundColor = backgroundColor

        self.broadcasting = !self.broadcasting
        self.setNeedsStatusBarAppearanceUpdate()
    }

    let completion: (Bool) -> Void = {
        finish in
        self.advertising(start: self.broadcasting)
    }

    UIView.animate(withDuration: 0.25, animations: animations, completion: completion)
}

// MARK: - Broadcast Beacon

func advertising(start: Bool) -> Void
{
    if self.peripheralManager == nil {
        return
    }

    if (!start) {
        self.peripheralManager!.stopAdvertising()

        return
    }

    let state: CBManagerState = self.peripheralManager!.state

    if (state == .poweredOn) {
        let UUID:UUID = (self.beacon?.proximityUUID)!
        let serviceUUIDs: Array<CBUUID> = [CBUUID(nsuuid: UUID)]

        // Why NSMutableDictionary can not convert to Dictionary<String, Any> 
        var peripheralData: Dictionary<String, Any> = self.beacon!.peripheralData(withMeasuredPower: 1)  as NSDictionary as! Dictionary<String, Any>
        peripheralData[CBAdvertisementDataLocalNameKey] = "iBeacon Demo" 
        peripheralData[CBAdvertisementDataServiceUUIDsKey] = serviceUUIDs

        self.peripheralManager!.startAdvertising(peripheralData)
    }
}
}

// MARK: - CBPeripheralManager Delegate -
extension BroadcastViewController: CBPeripheralManagerDelegate {
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager)
{
    let state: CBManagerState = peripheralManager!.state

    if state == .poweredOff {
        self.statusLabel.text = "Bluetooth Off"

        if self.broadcasting {
            self.broadcastBeacon(sender: self.triggerButton)
        }
    }

    if state == .unsupported {
        self.statusLabel.text = "Unsupported Beacon"
    }

    if state == .poweredOn {
        self.statusLabel.text = "Not Broadcast"
    }
}
}

除此之外,我还阅读了苹果网站上的文档,但我并没有深入了解。我也去了可能喜欢的 10 个不同的 Github 项目并尝试了它们,但它们都是以 swift.

的早期版本的方式编写的

假设您有一个正在运行的 iBeacon 扫描应用程序(否则,很难知道您的设备是否正确广播)...

我建议从简单开始...让 "start broadcasting" 部分正常工作,然后添加其他内容。

创建一个新项目(或向当前项目添加一个新的 VC)。添加 "Start" 和 "Stop" 按钮,您将连接到下面的 @IBAction 函数。

将此设为您的新 VC 的 class:

//
//  BroadcasterViewController.swift
//
//  Created by Don Mag on 3/16/18.
//

import UIKit
import CoreLocation
import CoreBluetooth

class BroadcasterViewController: UIViewController, CBPeripheralManagerDelegate {

    var myBeacon: CLBeaconRegion!
    var beaconData: NSDictionary!
    var peripheralMgr: CBPeripheralManager!

    let myRegionID = "myRegionID"

    @IBAction func startBeacon(_ sender: Any) {

        // don't do anything if myBeacon already exists
        guard myBeacon == nil else { return }

        let beaconUUID = "7FA08BC7-A55F-45FC-85C0-0BF26F899530"
        let beaconMajor: CLBeaconMajorValue = 123
        let beaconMinor: CLBeaconMinorValue = 456

        let uuid = UUID(uuidString: beaconUUID)!
        myBeacon = CLBeaconRegion(proximityUUID: uuid, major: beaconMajor, minor: beaconMinor, identifier: "MyIdentifier")

        beaconData = myBeacon.peripheralData(withMeasuredPower: nil)
        peripheralMgr = CBPeripheralManager(delegate: self, queue: nil, options: nil)

    }

    @IBAction func stopBeacon(_ sender: Any) {

        // don't do anything if there's no peripheral manager
        guard peripheralMgr != nil else { return }

        peripheralMgr.stopAdvertising()
        peripheralMgr = nil
        beaconData = nil
        myBeacon = nil

    }

    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        if peripheral.state == .poweredOn {
            peripheralMgr.startAdvertising(beaconData as! [String: AnyObject]!)
        } else if peripheral.state == .poweredOff {
            peripheralMgr.stopAdvertising()
        }
    }

}

运行 您 "broadcaster" 设备上的应用程序,然后点击“开始”按钮。然后 运行 你的 "scanner" 应用程序在另一台设备上,看看你是否找到你的新 "Device as iBeacon"

注意:我相信您已经知道,您的扫描器必须正在扫描与您在广播设备上使用的相同的beaconUUID。在此示例代码中,我使用了您在问题中发布的相同 UUID 字符串。

这对我来说效果很好 - 希望它对您也能起到同样的作用。如果 这部分 正常工作,那么您可以实现应用程序的其余部分——UI、状态标签、任何其他选项——并添加一些错误处理(蓝牙未启用或未授予权限等)。

您不能向 iBeacon 广告添加任何额外内容并使其被识别。这两行是个问题,必须删除:

 peripheralData[CBAdvertisementDataLocalNameKey] = "iBeacon Demo"
 peripheralData[CBAdvertisementDataServiceUUIDsKey] = serviceUUIDs

第一行试图在广告中添加广播名称。您不能这样做,因为数据包中没有额外的空间用于此类信息。

第二行试图在广告中添加 GATT 服务 UUID。这根本不符合逻辑。 iBeacon 广告是蓝牙 LE 制造商广告,它不广告 GATT 服务。添加这将使广告无效或导致广告 API 失败。

显示的代码可能存在其他问题。我建议简化代码以在应用程序启动时启动传输,这样您就可以消除任何可能的 UI 错误。我会使用像 Locate 这样的 Android 检测器,这样即使您的 ProximityUIID 错误,您也可以看到广告。 Android,不像iOS可以看到任何信标广告。