iOS 中 iBeacon 開發

LaiYoung_發表於2018-02-07

什麼是iBeacon?

iBeacon 是蘋果公司2013年9月釋出的移動裝置用OS(iOS7)上配備的新功能。其工作方式是,配備有低功耗藍芽(BLE)通訊功能的裝置使用BLE技術向周圍傳送自己特有的 ID,接收到該 ID 的應用軟體會根據該 ID 採取一些行動。

從個人的角度看: iBeacon向四面八方不停地廣播訊號,就像是往平靜的水面上扔了一塊石子,泛起層層漣漪(俗稱水波),波峰相當於 iBeacon 的RSSI(接受訊號強度指示),越靠近中心點的地方波峰越高(RSSI 越大),這個波峰的大小(RSSI 的值)受到扔石子時用力大小(發射功率)和水質(周圍環境因子)的影響,離中心點越遠水波越趨向於平靜,超過了一定值,水波會消失於無形,也就是說 iBeacon 向外廣播的距離是有範圍的,超過了這個範圍,將接受不到 iBeacon 的訊號。

從iOS開發者的角度看: iBeacon 在 CoreLocation 框架中抽象為CLBeacon類, 該類有6個屬性,分別是:

  • proximityUUID,是一個 NSUUID,用來標識公司。每個公司、組織使用的 iBeacon 應該擁有同樣的 proximityUUID

  • major,主要值,用來識別一組相關聯的 beacon,例如在連鎖超市的場景中,每個分店的 beacon 應該擁有同樣的 major

  • minor,次要值,則用來區分某個特定的 beacon。

  • proximity,遠近範圍的,一個列舉值。

    typedef NS_ENUM(NSInteger, CLProximity) {
    	CLProximityUnknown,// 無效
    	CLProximityImmediate,//在幾釐米內
    	CLProximityNear,//在幾米內
    	CLProximityFar//超過 10 米以外,不過在測試中超不過10米就是far
    }
    複製程式碼
  • accuracy,與iBeacon的距離。

  • rssi,訊號輕度為負值,越接近0訊號越強,等於0時無法獲取訊號強度。

Tip:proximityUUIDmajorminor 這三個屬性組成 iBeacon 的唯一識別符號。

只要進入iBeacon的範圍,就能喚醒 App(大約10秒鐘),即使在程式被殺掉的情況下。必要時,可以使用UIApplication類的- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void (^)(void))handler;方法,請求更多的後臺執行時間。

iBeacon的用途:我們可以用iBeacon可以進行室內定位(車庫,商場),智慧打卡,提醒(離開某物體的時候,比如離開家)。

iBeacon 與 BLE 的區別

iOS 中 iBeacon 是基於地理位置的微定位技術,雖然藉助手機藍芽進行接收MajroMinor,但是他們在開發工程中沒有任何關係。

iBeacon使用蘋果提供CoreLocation庫,然而在 BLE 在開發過程中使用CoreBluetooth庫。從上面提供的庫來看就很清楚了,特別是在 iOS8.0 之後的時候如果想使用iBeacon,必須讓使用者點選是否允許XXapp使用地理位置。如果在第一次使用 iOS App 掃描iBeacon的時候沒有提示這句話,是不可能接收到iBeacon的訊號(除非iOS 8.0之下)。如果是 BLE 則的開發過程中之需要提示使用者開啟藍芽,並不要求其他的地理位置任何資訊。

iBeacon 在 iOS 中的運用

許可權請求

info.plist中新增NSLocationAlwaysAndWhenInUseUsageDescription,NSLocationWhenInUseUsageDescriptionNSLocationAlwaysUsageDescription,請求地理位置許可權。

開啟Background Modes

Background modes

相關程式碼

import <CoreLocation/CoreLocation.h>

初始化locationManagerbeaconRegion

- (CLLocationManager *)locationManager {
    if (!_locationManager) {
        _locationManager = [[CLLocationManager alloc] init];
        _locationManager.delegate = self;
    }
    return _locationManager;
}

- (CLBeaconRegion *)beaconRegion {
    if (!_beaconRegion) {
        _beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:Beacon_Device_UUID] identifier:@"test"];
        _beaconRegion.notifyEntryStateOnDisplay = YES;
    }
    return _beaconRegion;
}
複製程式碼

CLBeaconRegion類,提供了3個初始化方法:

//監聽該UUID下的所有Beacon裝置
- (instancetype)initWithProximityUUID:(NSUUID *)proximityUUID identifier:(NSString *)identifier;

//監聽該UUID,major下的所有Beacon裝置
- (instancetype)initWithProximityUUID:(NSUUID *)proximityUUID major:(CLBeaconMajorValue)major identifier:(NSString *)identifier;

//監聽唯一的Beacon裝置
- (instancetype)initWithProximityUUID:(NSUUID *)proximityUUID major:(CLBeaconMajorValue)major minor:(CLBeaconMinorValue)minor identifier:(NSString *)identifier;
複製程式碼

在開始監控之前,我們需要使用isMonitoringAvailableForClass判斷裝置是否支援,是否允許訪問地理位置。

BOOL availableMonitor = [CLLocationManager isMonitoringAvailableForClass:[CLBeaconRegion class]];
    
if (availableMonitor) {
    CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus];
    switch (authorizationStatus) {
        case kCLAuthorizationStatusNotDetermined:
            [self.locationManager requestAlwaysAuthorization];
        break;
        case kCLAuthorizationStatusRestricted:
        case kCLAuthorizationStatusDenied:
            NSLog(@"受限制或者拒絕");
        break;
        case kCLAuthorizationStatusAuthorizedAlways:
        case kCLAuthorizationStatusAuthorizedWhenInUse:{
            [self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
            [self.locationManager startMonitoringForRegion:self.beaconRegion];
        }
        break;
    }
} else {
    NSLog(@"該裝置不支援 CLBeaconRegion 區域檢測");
}
複製程式碼

監聽方式

可用兩種方式檢測區域MonitoringRanging方式

Monitoring:可以用來在裝置進入/退出某個地理區域時獲得通知, 使用這種方法可以在應用程式的後臺執行時檢測 iBeacon,但是隻能同時檢測 20 個 region 區域,並且不能夠推測裝置與 iBeacon 的距離。

// 開始檢測區域
[self.locationManager startMonitoringForRegion:beaconRegion]; 

// 停止檢測區域
[self.locationManager stopMonitoringForRegion:beaconRegion]; 

// Monitoring成功對應回撥函式
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region;

// 裝置進入該區域時的回撥
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region;

// 裝置退出該區域時的回撥
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region;

// Monitoring有錯誤產生時的回撥
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(nullable CLRegion *)region withError:(NSError *)error;
複製程式碼

Ranging:可以用來檢測某區域內的所有 iBeacons。

// 開始檢測區域
[self.locationManager startRangingBeaconsInRegion:beaconRegion];

// 停止檢測區域
[self.locationManager stopRangingBeaconsInRegion:beaconRegion];

// Ranging成功對應回撥函式
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray<CLBeacon *> *)beacons inRegion:(CLBeaconRegion *)region 

// Ranging有錯誤產生時的回撥
- (void)locationManager:(CLLocationManager *)manager rangingBeaconsDidFailForRegion:(CLBeaconRegion *)region withError:(NSError *)error
複製程式碼

程式 kill 之後,進入 iBeacon 區域的回撥

// 當程式被殺掉之後,進入ibeacon區域,或者在程式執行時鎖屏/解鎖 會回撥此函式
- (void)locationManager:(CLLocationManager *)manager
      didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
複製程式碼

爭取更多的後臺時間

必要時,可以使用UIApplication類的- (UIBackgroundTaskIdentifier)beginBackgroundTaskWithExpirationHandler:(void (^)(void))handler;方法,請求更多的後臺執行時間。

用 iPhone 手機模擬 iBeacon

任何支援使用藍芽低功耗共享資料的 iOS 裝置都可以用作 iBeacon

import <CoreBluetooth/CoreBluetooth.h><CoreLocation/CoreLocation.h>

terminal中使用uuidgen命令,生成一個 UUID 063FA845-F091-4129-937D-2A189A86D844

其實利用BLE來模擬 beacon 裝置傳送訊號,很簡單。

相關程式碼

初始化peripheralManager

self.peripheralManager= [[CBPeripheralManager alloc] initWithDelegate:self queue:nil options:nil];
複製程式碼

傳送訊號

NSUUID *proximityUUID = [[NSUUID alloc] initWithUUIDString:self.UUIDTextField.text];
//建立beacon區域
CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:proximityUUID major:self.majorTextField.text.integerValue minor:self.minorTextField.text.integerValue identifier:@"test"];
NSDictionary *beaconPeripheraData = [beaconRegion peripheralDataWithMeasuredPower:nil];
    
if(beaconPeripheraData) {
    [self.peripheralManager startAdvertising:beaconPeripheraData];;//開始廣播
}
複製程式碼

停止廣播

[self.peripheralManager stopAdvertising];
複製程式碼

注意點

  • 需要訪問地理位置許可權。
  • 裝置需要開啟藍芽。
  • 利用 iOS 裝置模擬 beacon訊號,Home 出去之後是不能傳送訊號的。

Demo

官方 Demo

Blog Demo

參考文章

相關文章