The Nearby Messages API is a publish-subscribe API which lets nearby devices
exchange small payloads of data. Once a device publishes a message, nearby
devices can receive the message. Message size should be kept fairly small to
maintain good performance. This service is not meant for exchanging larger
objects such as photos and videos.

The set of nearby devices is determined by the exchange of small tokens over
Bluetooth and near-ultrasonic (inaudible) audio. When a device detects a token
from a nearby device, it sends the token to the Nearby Messages server to
validate it and check if there are any messages to deliver for the application's
current set of subscriptions.

An application can control the set of mediums used for device discovery, and
whether the mediums are used to broadcast tokens and/or scan for tokens.
By default, broadcasting and scanning is done on all the mediums. To do
discovery on a subset or mediums, and to control whether to broadcast or scan,
you must pass additional parameters when you create publications and
subscriptions.

> [!NOTE]
> **Note:** All of the callbacks in this library use blocks rather than delegates. Be careful to avoid retain cycles in your block implementations. See the [Apple
> documentation](https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html#//apple_ref/doc/uid/TP40011210-CH8-SW16) describing how to avoid retain cycles.

This library runs on **iOS 7** and above, and builds with the **iOS 8 SDK**.

## Creating a message manager

This code creates a message manager object, which lets you publish and
subscribe. Message exchange is unauthenticated, so you must supply a
public API key for iOS. You can create one using the [Google Developers Console](https://console.developers.google.com/) entry for
your project.

<br />

### Objective-C

<br />

```objective-c
#import <GNSMessages.h>

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

<br />

### Swift

<br />

```swift
let messageManager = GNSMessageManager(APIKey: "API_KEY")
```

<br />

<br />

## Publishing a message

This code snippet demonstrates publishing a message containing a name.
The publication is active as long as the publication object exists. To stop
publishing, release the publication object.

<br />

### Objective-C

<br />

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

<br />

### Swift

<br />

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

<br />

<br />

## Subscribing to messages

This code snippet demonstrates subscribing to all names shared by the
previous publication snippet. The subscription is active as long as the
subscription objects exists. To stop subscribing, release the subscription
object.

The message found handler is called when nearby devices that are publishing
messages are discovered. The message lost handler is called when a message is no
longer observed (the device has gone out of range or is no longer publishing the
message).

<br />

### Objective-C

<br />

```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
    }];
```

<br />

### Swift

<br />

```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
    })
```

<br />

<br />

## Discovery mediums

By default, both mediums (audio and Bluetooth) will be used to discover nearby
devices, and both mediums will broadcast and scan. For certain cases, you are
required to add the following entries to your app's `Info.plist`:

- If your app scans using audio, add `NSMicrophoneUsageDescription`, which is a
  string describing why you will be using the microphone. For example, "The
  microphone listens for anonymous tokens from nearby devices."

- If your app broadcasts using BLE, add
  `NSBluetoothPeripheralUsageDescription`, which is a string describing why you
  will be advertising on BLE. For example, "An anonymous token is advertised
  via Bluetooth to discover nearby devices."

In some cases, your app may need to use only one of the mediums, and it
may not need to do both broadcasting and scanning on that medium.

For instance, an app that is designed to connect to a set-top box that's
broadcasting on audio only needs to scan on audio to discover it. The following
snippet shows how to publish a message to that set-top box using only audio
scanning for discovery:

<br />

### Objective-C

<br />

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

<br />

### Swift

<br />

```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
      })
    })
```

<br />

<br />

## Enabling debug logging

Debug logging prints significant internal events to the console that can be
useful for tracking down problems that you may encounter when integrating Nearby
Messages into your app. We will ask for these logs if you contact us for
technical support.

You should enable it before creating a message manager. This code snippet shows
how to enable debug logging:

<br />

### Objective-C

<br />

```objective-c
[GNSMessageManager setDebugLoggingEnabled:YES];
```

<br />

### Swift

<br />

```swift
GNSMessageManager.setDebugLoggingEnabled(true)
```

<br />

<br />

## Tracking the Nearby permission state

User consent is required to enable device discovery. This is indicated by the
Nearby permission state. On the first call to create a publication or
subscription, the user is presented with a consent dialog. If the user does not
consent, device discovery will not work. In this case, your app should show a
message to remind the user that device discovery is disabled. The permission
state is stored in `NSUserDefaults`.

The following snippet demonstrates subscribing to the permission state. The
permission state changed handler is called whenever the state changes, and it is
not called the first time until the user has given or denied permission.
Release the permission object to stop subscribing.

<br />

### Objective-C

<br />

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

<br />

### Swift

<br />

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

<br />

<br />

Your app can provide a way for the user to change the permission state; for
example, by using a toggle switch on a settings page.

Here's an example of how to get and set the permission state.

<br />

### Objective-C

<br />

```objective-c
BOOL permissionState = [GNSPermission isGranted];
[GNSPermission setGranted:!permissionState];  // toggle the state
```

<br />

### Swift

<br />

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

<br />

<br />

> [!NOTE]
> **Note:** The app should only set the permission state in response to user input. Never change the permission state without user consent.

## Tracking user settings that affect Nearby

If the user has denied microphone permission, denied Bluetooth permission, or
has turned Bluetooth off, Nearby will not work as well, or may not work at all.
Your app should show a message in these cases, alerting the user that Nearby's
operations are being hindered. The following snippet shows how to track the
status of these user settings by passing handlers when creating the message
manager:

<br />

### Objective-C

<br />

```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
         };
}];
```

<br />

### Swift

<br />

```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
      }
    })
```

<br />

<br />

## Overriding the Nearby permission dialog

Depending on the parameters you pass into your publications and subscriptions,
iOS may ask for various permissions before allowing Nearby to function. For
instance, the default strategy listens for data transmitted on near-ultrasonic
audio, so iOS will ask for permission to use the microphone. In these cases,
Nearby will show a "preflight" dialog that explains why the user is being asked
to give permission.

If you want to provide a custom "preflight" dialog, set the
`permissionRequestHandler` parameter to a custom block in the publication or
subscription parameters. Your custom block must call the `permissionHandler`
block after the user has responded. The following snippet shows how to do this
for a publication:

<br />

### Objective-C

<br />

```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.
                                 };
                               }];
```

<br />

### Swift

<br />

```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.
          }
        })
```

<br />

<br />

## Background operation

Publications and subscriptions that use BLE for device discovery can work in the
background. Here are some things you should be aware of when deciding to use
background mode:

- Background operations must use the BLE medium only; audio is not supported.
- There is an added battery cost for background BLE. The cost is low, but you should measure it before deciding to use background mode.
- iOS will ask the user for permission to advertise via BLE in the background.

To add background mode to a publication or subscription, follow these additional
steps:

- Enable background mode and BLE-only in your publication or subscription by
  passing in a properly configured `GNSStrategy` object. The following snippet
  shows how to do this for a subscription:

  <br />

  ### Objective-C

  <br />

  ```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;
        }];
      }];
  ```

  <br />

  ### Swift

  <br />

  ```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
        })
      })
  ```

  <br />

  <br />

- Add these entries to your app's `Info.plist`:

  - `UIBackgroundModes` entries:

    - `bluetooth-central` for BLE scanning in the background. Needed only when the discovery mode includes scanning; it does by default.
    - `bluetooth-peripheral` for BLE advertising in the background. Needed only when the discovery mode includes broadcasting; it does by default.
  - `NSBluetoothPeripheralUsageDescription` string describing why
    you will be advertising on BLE. For example, "An anonymous token is
    advertised via Bluetooth to discover nearby devices." See
    [Apple's documentation](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW20)
    for details.

- Your app can be killed at any time by the system while in the background. If
  background mode is a setting that can be enabled or disabled by the user, your
  app should do the following:

  - Save the background mode value to `NSUserDefaults` whenever the user changes it.
  - On startup, read it from `NSUserDefaults` and restore the Nearby publications and/or subscriptions if background mode is enabled.

## Background notifications

If you want your app to notify the user when a subscription receives a message
while the background, you can use
[local notifications](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/Introduction.html).

Follow these steps to add them to your app:

- Register for local notifications on startup:

  <br />

  ### Objective-C

  <br />

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

  <br />

  ### Swift

  <br />

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

  <br />

  <br />

- Send a local notification in the message-found handler of your subscription:

  <br />

  ### Objective-C

  <br />

  ```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...
    };
  ```

  <br />

  ### Swift

  <br />

  ```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...
  }
  ```

  <br />

  <br />