發布及訂閱

Nearby Messages API 是一種發布訂閱 API,可讓鄰近裝置交換小型資料酬載。裝置發布訊息後,附近的裝置就能收到訊息。訊息大小應盡可能精簡,以維持良好效能。這項服務不適用於交換大型物件,例如相片和影片。

附近的裝置組合取決於透過藍牙和近超音波 (無聲) 音訊交換小型權杖。當裝置偵測到鄰近裝置提供的權杖時,就會將權杖傳送至 Nearby Messages 伺服器,以進行驗證,並檢查是否有任何訊息要傳送至應用程式目前的訂閱項目組合。

應用程式可以控制用於探索裝置的一組媒介,以及是否使用媒介廣播權杖和/或掃描權杖。根據預設,廣播及掃描會為所有媒體執行。如要探索子集或媒介,以及控制是否要播送或掃描,您必須在建立出版品和訂閱項目時傳送其他參數。

這個程式庫可在 iOS 7 以上版本中執行,並以 iOS 8 SDK 建構。

建立訊息管理員

此程式碼會建立訊息管理員物件,讓您發布及訂閱。訊息交換未經驗證,因此您必須提供 iOS 適用的公用 API 金鑰。您可以使用專案的 Google Developers Console 項目建立一個訂閱項目。

Objective-C

#import <GNSMessages.h>

GNSMessageManager *messageManager =
    [[GNSMessageManager alloc] initWithAPIKey:@"API_KEY"];

Swift

let messageManager = GNSMessageManager(APIKey: "API_KEY")

發布訊息

這個程式碼片段示範如何發布含有名稱的訊息。只要發布物件存在,出版品就會處於有效狀態。如要停止發布,請釋出發布物件。

Objective-C

id<GNSPublication> publication =
    [messageManager publicationWithMessage:[GNSMessage messageWithContent:[name dataUsingEncoding:NSUTF8StringEncoding]]];

Swift

let publication =
    messageManager.publication(with: GNSMessage(content: name.data(using: .utf8)))

訂閱訊息

這段程式碼示範如何訂閱先前發布程式碼片段共用的所有名稱。只要訂閱物件存在,訂閱項目就會保持有效。如要停止訂閱,請放開訂閱物件。

找到附近正在發布訊息的裝置時,系統會呼叫找到的處理常式。如果系統不再觀察到訊息 (裝置超出範圍,或不再發布訊息),系統會呼叫訊息遺失處理常式。

Objective-C

id<GNSSubscription> subscription =
    [messageManager subscriptionWithMessageFoundHandler:^(GNSMessage *message) {
      // Add the name to a list for display
    }
    messageLostHandler:^(GNSMessage *message) {
      // Remove the name from the list
    }];

Swift

let subscription =
    messageManager.subscription(messageFoundHandler: { (message: GNSMessage?) in
      // Add the name to a list for display
    },
    messageLostHandler: { (message: GNSMessage?) in
      // Remove the name from the list
    })

探索媒介

根據預設,系統會使用音訊和藍牙 (音訊和藍牙) 偵測鄰近裝置,而這兩種媒介都會廣播及掃描。在某些情況下,您必須在應用程式的 Info.plist 中加入以下項目:

  • 如果應用程式會使用音訊掃描,請新增 NSMicrophoneUsageDescription 字串,說明您要使用麥克風的原因。例如:「麥克風監聽鄰近裝置中的匿名權杖」。

  • 如果應用程式使用 BLE 播送,請新增 NSBluetoothPeripheralUsageDescription,該字串說明您要在 BLE 上放送廣告的原因。例如:「透過藍牙宣傳匿名權杖,以便探索鄰近裝置」。

在某些情況下,您的應用程式可能只需要使用其中一種媒介,不需要同時對該媒介進行廣播和掃描。

舉例來說,如果應用程式設計為連線到透過音訊播送的機上盒,只需要掃描音訊就能找到。下列程式碼片段說明如何僅使用音訊掃描進行探索,將訊息發布至該機上盒:

Objective-C

id<GNSPublication> publication = [messageManager publicationWithMessage:message
    paramsBlock:^(GNSPublicationParams *params) {
      params.strategy = [GNSStrategy strategyWithParamsBlock:^(GNSStrategyParams *params) {
        params.discoveryMediums = kGNSDiscoveryMediumsAudio;
        params.discoveryMode = kGNSDiscoveryModeScan;
      }];
    }];

Swift

let publication = messageManager.publication(with: message,
    paramsBlock: { (params: GNSPublicationParams?) in
      guard let params = params else { return }
      params.strategy = GNSStrategy(paramsBlock: { (params: GNSStrategyParams?) in
        guard let params = params else { return }
        params.discoveryMediums = .audio
        params.discoveryMode = .scan
      })
    })

啟用偵錯記錄功能

偵錯記錄功能會將重要的內部事件輸出到主控台,方便您追蹤在應用程式中整合 Nearby Messages 時可能會遇到的問題。如果您向我們尋求技術支援,我們就會詢問這些記錄。

請先啟用這項服務,再建立訊息管理員。下列程式碼片段說明如何啟用偵錯記錄功能:

Objective-C

[GNSMessageManager setDebugLoggingEnabled:YES];

Swift

GNSMessageManager.setDebugLoggingEnabled(true)

追蹤鄰近分享權限狀態

如要啟用裝置探索功能,您必須取得使用者同意。這是以「鄰近分享」權限狀態表示。第一次呼叫建立發布項目或訂閱項目時,系統會顯示同意聲明對話方塊。如果使用者未同意,裝置探索功能就無法運作。在此情況下,應用程式應顯示訊息,提醒使用者裝置探索功能已停用。權限狀態會儲存在 NSUserDefaults 中。

下列程式碼片段示範如何訂閱權限狀態。每當狀態變更,系統就會呼叫權限狀態已變更的處理常式,而且在首次呼叫使用者授予或拒絕權限時,才會呼叫這個處理常式。釋出權限物件以停止訂閱。

Objective-C

GNSPermission *nearbyPermission = [[GNSPermission alloc] initWithChangedHandler:^(BOOL granted) {
  // Update the UI here
}];

Swift

let nearbyPermission = GNSPermission(changedHandler: { (granted: Bool) in
  // Update the UI here
})

應用程式可讓使用者變更權限狀態,例如使用設定頁面中的切換開關。

以下範例說明如何取得及設定權限狀態。

Objective-C

BOOL permissionState = [GNSPermission isGranted];
[GNSPermission setGranted:!permissionState];  // toggle the state

Swift

let permissionState = GNSPermission.isGranted()
GNSPermission.setGranted(!permissionState)  // toggle the state

追蹤影響鄰近分享功能的使用者設定

如果使用者拒絕授予麥克風權限、藍牙權限遭拒,或藍牙關閉,「鄰近分享」功能無法正常運作,甚至完全無法運作。在這種情況下,應用程式應顯示訊息,通知使用者 Nearby 的作業正在阻礙。下列程式碼片段說明如何在建立訊息管理員時傳遞處理常式,以追蹤這些使用者設定的狀態:

Objective-C

GNSMessageManager *messageManager = [[GNSMessageManager alloc]
    initWithAPIKey:API_KEY
       paramsBlock:^(GNSMessageManagerParams *params) {
         params.microphonePermissionErrorHandler = ^(BOOL hasError) {
           // Update the UI for microphone permission
         };
         params.bluetoothPowerErrorHandler = ^(BOOL hasError) {
           // Update the UI for Bluetooth power
         };
         params.bluetoothPermissionErrorHandler = ^(BOOL hasError) {
           // Update the UI for Bluetooth permission
         };
}];

Swift

let messageManager = GNSMessageManager(
         APIKey: API_KEY,
    paramsBlock: { (params: GNSMessageManagerParams?) in
      guard let params = params else { return }
      params.microphonePermissionErrorHandler = { (hasError: Bool) in
        // Update the UI for microphone permission
      }
      params.bluetoothPowerErrorHandler = { (hasError: Bool) in
        // Update the UI for Bluetooth power
      }
      params.bluetoothPermissionErrorHandler = { (hasError: Bool) in
        // Update the UI for Bluetooth permission
      }
    })

覆寫鄰近分享權限對話方塊

視您傳入出版品和訂閱項目的參數而定,iOS 可能會在允許 Nearby 運作之前要求各種權限。舉例來說,預設策略會監聽近似超音波音訊傳輸的資料,因此 iOS 會要求麥克風使用權限。在這類情況下,Nearby 會顯示「預檢」對話方塊,說明系統要求使用者授予權限的原因。

如要提供自訂的「預檢」對話方塊,請在發布參數或訂閱參數中,將 permissionRequestHandler 參數設為自訂區塊。使用者回應後,您的自訂區塊必須呼叫 permissionHandler 區塊。下列程式碼片段說明如何為發布項目執行這項操作:

Objective-C

id<GNSPublication> publication =
    [messageManager publicationWithMessage:[GNSMessage messageWithContent:[name dataUsingEncoding:NSUTF8StringEncoding]]
                               paramsBlock:^(GNSPublicationParams *params) {
                                 params.permissionRequestHandler = ^(GNSPermissionHandler permissionHandler) {
                                   // Show your custom dialog here.
                                   // Don't forget to call permissionHandler() with YES or NO when the user dismisses it.
                                 };
                               }];

Swift

let publication =
    messageManager.publication(with: GNSMessage(content: name.data(using: .utf8)),
        paramsBlock: { (params: GNSPublicationParams?) in
          guard let params = params else { return }
          params.permissionRequestHandler = { (permissionHandler: GNSPermissionHandler?) in
            // Show your custom dialog here.
            // Don't forget to call permissionHandler() with true or false when the user dismisses it.
          }
        })

背景作業

使用 BLE 進行裝置探索的發布項目和訂閱項目可以在背景運作。決定使用背景模式時,請注意下列事項:

  • 背景作業只能使用 BLE 中;不支援音訊。
  • 背景 BLE 會增加電池成本。雖然成本很低,但建議您在決定使用背景模式前就先行衡量。
  • iOS 會要求使用者授予在背景透過 BLE 放送廣告的權限。

如要為出版品或訂閱項目新增背景模式,請按照下列步驟操作:

  • 透過傳遞正確設定的 GNSStrategy 物件,在出版品或訂閱中啟用背景模式和僅限 BLE。下列程式碼片段說明如何為訂閱執行這項作業:

    Objective-C

    id<GNSSubscription> subscription =
        [messageManager subscriptionWithMessageFoundHandler:^(GNSMessage *message) {
          // Add the name to a list for display
        }
        messageLostHandler:^(GNSMessage *message) {
          // Remove the name from the list
        }
        paramsBlock:^(GNSSubscriptionParams *params) {
          params.strategy = [GNSStrategy strategyWithParamsBlock:^(GNSStrategyParams *params) {
            params.allowInBackground = YES;
            params.discoveryMediums = kGNSDiscoveryMediumsBLE;
          }];
        }];
    

    Swift

    let subscription =
        messageManager.subscription(messageFoundHandler: { (message: GNSMessage?) in
          // Add the name to a list for display
        },
        messageLostHandler: { (message: GNSMessage?) in
          // Remove the name from the list
        },
        paramsBlock:{ (params: GNSSubscriptionParams?) in
          guard let params = params else { return }
          params.strategy = GNSStrategy(paramsBlock: { (params: GNSStrategyParams?) in
            guard let params = params else { return }
            params.allowInBackground = true
            params.discoveryMediums = .BLE
          })
        })
    

  • 請在應用程式的 Info.plist 中加入以下項目:

    • UIBackgroundModes 個項目:

      • bluetooth-central 代表在背景掃描 BLE。只有在探索模式包含掃描時才需要使用,預設為啟用。
      • bluetooth-peripheral 代表 BLE 在背景放送廣告。只有在探索模式包含廣播功能時才需要使用,預設為啟用。
    • NSBluetoothPeripheralUsageDescription 字串,說明您會在 BLE 上放送廣告的原因。例如:「已透過藍牙宣傳匿名權杖,以便探索鄰近裝置」。詳情請參閱 Apple 說明文件

  • 當系統在背景執行時,系統可能會隨時終止您的應用程式。如果背景模式是使用者可以啟用或停用的設定,應用程式應執行下列步驟:

    • 每當使用者進行變更時,將背景模式值儲存至 NSUserDefaults
    • 在啟動時從 NSUserDefaults 讀取,並還原 Nearby 出版品和/或訂閱 (如果已啟用背景模式)。

背景通知

如果您希望應用程式在背景收到訊息時通知使用者,可以使用本機通知

請按照下列步驟將應用程式加入應用程式:

  • 註冊啟動時即可接收本機通知:

    Objective-C

    if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]) {
      [[UIApplication sharedApplication] registerUserNotificationSettings:
          [UIUserNotificationSettings settingsForTypes:
              UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound
                                            categories:nil]];
    }
    

    Swift

    UIApplication.shared.registerUserNotificationSettings(
        UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil))
    

  • 在訂閱的訊息找到處理常式中,傳送本機通知:

    Objective-C

    GNSMessageHandler myMessageFoundHandler = ^(GNSMessage *message) {
        // Send a local notification if not in the foreground.
        if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive) {
          UILocalNotification *localNotification = [[UILocalNotification alloc] init];
          localNotification.alertBody = @"Message received";
          [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
        }
        // Process the new message...
      };
    

    Swift

    let myMessageFoundHandler: GNSMessageHandler = { (message: GNSMessage?) in
      // Send a local notification if not in the foreground.
      if UIApplication.shared.applicationState != .active {
        let localNotification = UILocalNotification()
        localNotification.alertBody = "Message received"
        UIApplication.shared.presentLocalNotificationNow(localNotification)
      }
      // Process the new message...
    }