pebble/devsite/source/_guides/migration/pebblekit-ios-3.md
2025-02-24 18:58:29 -08:00

12 KiB

title description permalink generate_toc guide_group order
PebbleKit iOS 3.0 Migration Guide How to migrate apps that use PebbleKit iOS to the 3.0 version. /guides/migration/pebblekit-ios-3/ true migration 1

With previous Pebble firmware versions, iOS users had to manage two different Bluetooth pairings to Pebble. A future goal is removing the Bluetooth Classic pairing and keeping only the LE (Low Energy) one. This has a couple of advantages. By using only one Bluetooth connection Pebble saves energy, improving the battery life of both Pebble and the phone. It also has the potential to simplify the onboarding experience. In general, fewer moving parts means less opportunity for failures and bugs.

The plan is to remove the Bluetooth Classic connection and switch to LE in gradual steps. The first step is to make the Pebble app communicate over LE if it is available. The Bluetooth Classic pairing and connection will be kept since as of today most iOS companion apps rely on the Classic connection in order to work properly.

Building a companion app against PebbleKit iOS 3.0 will make it compatible with the new LE connection, while still remaining compatible with older Pebble watches which don't support the LE connection. Once it is decided to cut the Bluetooth Classic cord developers won't have to do anything, existing apps will continue to work.

Note: Pebble Time Round (the chalk platform) uses only Bluetooth LE, and so companion apps must use PebbleKit iOS 3.0 to connect with it.

What's New

Sharing No More: Dedicated Channels per App

A big problem with the Bluetooth Classic connection is that all iOS companion apps have to share a single communication channel which gets assigned on a "last one wins" basis. Another problem is that a "session" on this channel has to be opened and closed by the companion app.

PebbleKit iOS 3.0 solved both these problems with LE based connections. When connected over LE each companion app has a dedicated and persistent communication channel to Pebble.

This means that an app can stay connected as long as there is a physical Bluetooth LE connection, and it does not have to be closed before that other apps can use it!

Starting an App from Pebble

Since each companion app using the LE connection will have a dedicated and persistent channel, the user can now start using an app from the watch without having to pull out the phone to open the companion app. The companion app will already be connected and listening. However there are a few caveats to this:

  • The user must have launched the companion app at least once after rebooting the iOS device.

  • If the user force-quits the companion app (by swiping it out of the app manager) the channel to the companion app will be disconnected.

Otherwise the channel is pretty robust. iOS will revive the companion app in the background when the watchapp sends a message if the companion app is suspended, has crashed, or was stopped/killed by iOS because it used too much memory.

How to Upgrade

  1. Download the new PebbleKit.framework from the pebble-ios-sdk repository.

  2. Replace the existing PebbleKit.framework directory in the iOS project.

  3. The PebbleVendor.framework isn't needed anymore. If it is not used, remove it from the project to reduce its size.

  4. See the Breaking API Changes section below.

  5. When submitting the iOS companion to the Pebble appstore, make sure to check the checkbox shown below.

Important: Make sure to invoke [[PBPebbleCentral defaultCentral] run] after the iOS app is launched, or watches won't connect!

Breaking API Changes

App UUIDs Are Now NSUUIDs

Older example code showed how to use the appUUID property of the PBPebbleCentral object, which was passed as an NSData object. Now it is possible to directly use an NSUUID object. This also applies to the PBWatch APIs requiring appUUID: parameters. An example of each case is shown below.

Previous Versions of PebbleKit iOS

uuid_t myAppUUIDbytes;
NSUUID *myAppUUID = [[NSUUID alloc] initWithUUIDString:@"226834ae-786e-4302-a52f-6e7efc9f990b"];
[myAppUUID getUUIDBytes:myAppUUIDbytes];
[PBPebbleCentral defaultCentral].appUUID = [NSData dataWithBytes:myAppUUIDbytes length:16];

With PebbleKit iOS 3.0

NSUUID *myAppUUID = [[NSUUID alloc] initWithUUIDString:@"226834ae-786e-4302-a52f-6e7efc9f990b"];
[PBPebbleCentral defaultCentral].appUUID = myAppUUID;

Cold PBPebbleCentral

As soon as PebbleKit uses a CoreBluetooth API a pop-up asking for Bluetooth permissions will appear. Since it is undesirable for this pop-up to jump right into users' faces when they launch the iOS app, PBPebbleCentral will start in a "cold" state.

This gives developers the option to explain to app users that this pop-up will appear, in order to provide a smoother onboarding experience. As soon as a pop-up would be appropriate to show (e.g.: during the app's onboarding flow), call [central run], and the pop-up will be shown to the user.

To help personalize the experience, add some custom text to the pop-up by adding a NSBluetoothPeripheralUsageDescription ("Privacy - Bluetooth Peripheral Usage Description") value to the project's Info.plist file.

// MyAppDelegate.m - Set up PBPebbleCentral and run if the user has already
// performed onboarding
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [PBPebbleCentral defaultCentral].delegate = self;
  [PBPebbleCentral defaultCentral].appUUID = myAppUUID;
  if ([MySettings sharedSettings].userDidPerformOnboarding) {
    [[PBPebbleCentral defaultCentral] run];
  }
}
// MyOnboarding.m - Once the pop-up has been accepted, begin PBPebbleCentral
- (IBAction)didTapGrantBluetoothPermissionButton:(id)sender {
  [MySettings sharedSettings].userDidPerformOnboarding = YES;
  [[PBPebbleCentral defaultCentral] run]; // will trigger pop-up
}

It is very unlikely that the Pebble watch represented by the PBWatch object returned by lastConnectedWatch is connected instantly after invoking [central run]. Instead, it is guaranteed that the delegate will receive pebbleCentral:watchDidConnect: as soon as the watch connects (which might take a few seconds). Once this has occurred, the app may then perform operations on the PBWatch object.

New Features

8K AppMessage Buffers

In previous versions of PebbleKit iOS, if an app wanted to transmit large amounts of data it had to split it up into packets of 126 bytes. As of firmware version 3.5, this is no longer the case - the maximum message size is now such that a dictionary with one byte array (NSData) of 8192 bytes fits in a single app message. The maximum available buffer sizes are increased for messages in both directions (i.e.: inbox and outbox buffer sizes). Note that the watchapp should be compiled with SDK 3.5 or later in order to use this capability.

To check whether the connected watch supports the increased buffer sizes, use getVersionInfo: as shown below.

[watch getVersionInfo:^(PBWatch *watch, PBVersionInfo *versionInfo) {
  // If 8k buffers are supported...
  if ((versionInfo.remoteProtocolCapabilitiesFlags & PBRemoteProtocolCapabilitiesFlagsAppMessage8kSupported) != 0) {
    // Send a larger message!
    NSDictionary *update = @{ @(0): someHugePayload };
    [watch appMessagesPushUpdate:update onSent:^(PBWatch *watch, NSDictionary *update, NSError *error) {
      // ...
    }];
  } else {
    // Fall back to sending smaller 126 byte messages...
  }
}];

Swift Support

The library now exports a module which makes using PebbleKit iOS in Swift projects much easier. PebbleKit iOS 3.0 also adds nullability and generic annotations so that developers get the best Swift experience possible.

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
  let pebbleCentral = PBPebbleCentral.defaultCentral()
  pebbleCentral.appUUID = PBGolfUUID
  pebbleCentral.delegate = self
  pebbleCentral.run()

  return true
}

Minor Changes and Deprecations

  • Removed the PebbleVendor framework.

    • Also removed CocoaLumberjack from the framework. This should reduce conflicts if the app is using CocoaLumberjack itself.

    • If the project need these classes, it can keep the PebbleVendor dependency, therwise just remove it.

  • Added [watch releaseSharedSession] which will close Classic sessions that are shared between iOS apps (but not LE sessions as they are not shared).

    • If the app doesn't need to talk to Pebble in the background, it doesn't have to use it.

    • If the app does talk to Pebble while in the background, call this method as soon as it is done talking.

  • Deprecated [watch closeSession:] - please use [watch releaseSharedSession] if required (see note above). The app can't close LE sessions actively.

  • Deprecated [defaultCentral hasValidAppUUID] - please use [defaultCentral appUUID] and check that it is not nil.

  • Added [defaultCentral addAppUUID:] if the app talks to multiple app UUIDs from the iOS application, allowing PebbleCentral to eagerly create LE sessions.

  • Added logging - PebbleKit iOS 3.0 now logs internal warnings and errors via NSLog. To change the verbosity, use [PBPebbleCentral setLogLevel:] or even override the PBLog function (to forward it to CocoaLumberjack for example).

  • Changed [watch appMessagesAddReceiveUpdateHandler:] - the handler must not be nil.

Other Recommendations

Faster Connection

Set central.appUUID before calling [central run]. If using multiple app UUIDs please use the new addAppUUID: API before calling [central run] for every app UUID that the app will talk to.

Background Apps

If the app wants to run in the background (please remember that Apple might reject it unless it provides reasonable cause) add the following entries to the UIBackgroundModes item in the project's Info.plist file:

  • bluetooth-peripheral ("App shares data using CoreBluetooth") which is used for communication.

  • bluetooth-central ("App communicates using CoreBluetooth") which is used for discovering and reconnecting Pebbles.

Compatibility with Older Pebbles

Most of the Pebble users today will be using a firmware that is not capable of connecting to an iOS application using LE. LE support will gradually roll out to all Pebble watches. However, this will not happen overnight. Therefore, both LE and Classic PebbleKit connections have to be supported for some period of time. This has several implications for apps:

  • Apps still need to be whitelisted. Read {% guide_link appstore-publishing/whitelisting %} for more information and to whitelist a new app.

  • Because the Classic communication channel is shared on older Pebble firmware versions, iOS apps still need to provide a UI to let the user connect to/disconnect from the Pebble app. For example, a "Disconnect" button would cause [watch releaseSharedSession] to be called.

  • In the project's Info.plist file:

    • The UISupportedExternalAccessoryProtocols key still needs to be added with the value com.getpebble.public.

    • The external-accessory value needs to be added to the UIBackgroundModes array, if you want to support using the app while backgrounded.