Mobile SDKs

The following section describes how to integrate the mobile SDKs and the main methods that are required to operate with Harbor Lockers.

Integrate iOS SDK

The iOS SDK is available through Cocoapods. Add the following line to your Podfile:

pod "HarborLockersSDK"

You can also specify the version of the SDK, for example:

pod "HarborLockersSDK", '1.0.10'

Then run pod install to install the pods. To start using the HarborSDK, you must import it into your files

@import HarborLockersSDK

The iOS SDK requires Bluetooth access, in your info.plist add the following entries:

  • NSBluetoothAlwaysUsageDescription -> A description of why your app needs access to Bluetooth.

  • NSBluetoothPeripheralUsageDescription -> Same description as before. This is needed only if your app targets iOS 12 or earlier.

Integrate Android SDK

The Android SDK is distributed through Jitpack. It can be integrated through gradle, maven, sbt, and leiningen as following:

In your root build.gradle, at the end of repositories, add the following

allprojects {
  repositories {
    ...
    maven { url 'https://jitpack.io' }
  }
}

Then add the dependency

dependencies {
  implementation 'com.github.Harbor-Lockers:harbor-android-sdk:<version>'
}

Add the following entries to your AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

The android:required value in the last line depends on wether you want your app to be listed to users that doesn’t have BLE support on their devices. This might be the case if all your app does is control lockers with Harbor SDK and can’t do anything useful without BLE support.

Using the SDK

Once you have the SDK integrated there are a series of steps you need to follow to connect to a tower and open a locker.

Most of the data required by the SDK are provided by your server. Your Backend is responsible for the following:

  1. Define the location ID, tower ID, and locker ID you’ll interact with.

  2. Provide you with an SDK authentication token.

  3. Provide you with the session token information so you can connect to the tower.

  4. Provide you with the payload and payload auth to control your assigned locker.

In addition to that, your backend will be responsible for managing the lockers assigned to your account.

Note

You might need to integrate other API calls to release lockers, change assigned locker, or mark a locker as used. Check the API integration sections for details.

Initialize the SDK

The SDK is a singleton class, so you can access it as follows:

[HarborSDK shared]

Before connecting to a tower, you’ll need to initialize the SDK, set the Environment you want to use, set the Harbor Access Token, and start looking for nearby towers.

The first thing you need to do is to set the HarborSDKDelegate and, if needed, configure a HarborSDKConsole to get the logs (you might want to do this only in debug mode)

The HarborSDKDelegate will notify you about nearby towers found through BLE, while the HarborSDKConsole delegate might be helpful for debugging.

@objc
public protocol HarborSDKConsole {
  func printToConsole(_ string: String)
}

@objc
public protocol HarborSDKDelegate {
  func harborDidDiscoverTowers(_ towers: [Tower])
}

Now set the environment you want to use with the setEnvironment function. This will allow you to use either DEVELOPMENT or PRODUCTION envs (defined in an enum) or a custom URL if you get a dedicated environment set for you.

The last step to initialize the SDK is to set the SDK Access Token. For this, you’ll need the SDK Access Token provided by your server. A boilerplate initialization code looks like this:

[[HarborSDK shared] setDelegate:self];
[[HarborSDK shared] setOutputConsole:self];
[[HarborSDK shared] setEnvironment:EnvironmentDevelopment];
[[HarborSDK shared] setAccessToken:token];

Connect to a Tower and Establish a Session

Now that the SDK is configured, you can search for nearby towers and try to connect to them.

When you get notified about discovered towers, you might either show a list of the towers to connect to or if you already know which tower to connect, you can automatically connect to it.

To search for nearby towers, call the startTowersDiscovery function. Note that you’ll need to set up the HarborSDKDelegate before getting notified about the discovered towers.

To connect to a tower, use the connectToTower function. This function receives a Tower object, which you receive in the HarborSDKDelegate methods

The connectToTower function has a completion callback that notifies you if you got connected to the tower correctly.

Here’s an example that automatically connects to a tower assuming you are looking for tower with ID 0000000000000010. This example is written in Objective-C, but it should be easy to translate to other languages.

#define TIMER_LIMIT_IN_SECONDS 15
int count = 0;
NSTimer *timer;

- (void)waitForSyncCompletion
{
  count++;
  BOOL isSyncing = [[HarborSDK shared] isSyncing];
  if (!isSyncing) {
    // tower is in sync with Harbor's backend, can start interaction
  } else if (count == TIMER_LIMIT_IN_SECONDS)
  {
    // sync not completed within time limit, return error
    [timer invalidate];
    timer = nil;
    return;
  } else {
    // wait for sync to finish
  }
}

- (void)harborDidDiscoverTowers:(NSArray<Tower *> *)towers {
  for(Tower * tower in towers) {
    if ([tower.towerId.hexString isEqualToString:@"0000000000000010"]) {
      [[HarborSDK shared] connectToTower:tower completion:^(NSString * _Nullable name, NSError * _Nullable error) {
        if([name length] > 0) {
          // Tower connected successfully
          [[HarborSDK shared] establishSessionWithTowerId:nil
                                    duration:600
                          sessionPermissions:role.integerValue
                           completionHandler:^(BOOL success,
                                               NSError * _Nullable error) {
            if(success) {
              timer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                  target:self
                                                selector:@selector(waitForSyncCompletion)
                                                userInfo:nil
                                                  repeats:YES];
            } else {
              // An error occurred
            }
          }];
        } else if(error != nil) {
          // An error occurred
        }
      }];
    }
  }
}

Once you’re connected to a tower, you need to establish a session to start interacting with it. To establish a session, use the establishSession function to create a pickup or drop off session in the tower.

The establishSession function receives the following parameters:

  • towerId: 8 bytes containing the towerID.

  • duration: How long you expect the session to last (in seconds). Note that if you don’t interact with the tower in more than a minute, your session will be closed by the tower.

  • sessionPermissions: Indicates the permission level for your session. Most of the third party developers will only be able to establish pickup and drop off sessions.

  • completionHandler: Callback to handle the results.

Wait for the Tower to Sync

Once you’re connected to a tower and have a session established, the tower will start syncing with Harbor’s backend. Here, you need to wait for the tower to sync with Harbor’s backend and then you can start interacting with the tower. The isSyncing method will return the status flag of the syncing operation which value will be false when completed. Given the different possible scenarios regarding network speed and connectivity we are using a timer in the example above to keep it simple.

Interacting with the Tower

Once you have a session established and the tower is in sync, you can start interacting with it to open the lockers.

Open a locker

Your server must be integrated with the Harbor API to do this. Your server will request a payload and a payload auth to Harbor that will contain the encrypted data required to open your locker. The details on how to do this are covered in Make a delivery, so we assume you already have the payload and payload auth to be sent to the tower.

Use the sendOpenLockerWithToken function to open a locker. This function receives the following parameters:

  • payload: encrypted payload containing the data about the locker to be opened.

  • payloadAuth: authentication packet to validate the authenticity of the payload.

  • completionHandler: callback indicating if a locker was opened (providing locker ID), or if some error happened (for example the locker wasn’t found).

After a locker was opened, the tower remembers the last opened locker, which is the one considered for other commands such as reopenLocker, checkLockerDoor, or revertLockerState.

Check Locker Door

After a locker was opened, you can check the state of the door using the sendCheckLockerDoor function.

The completion handler will notify you if the door you just tried to open is open or close. If no door was opened during your session, the completion handler will return an error.

You can use this method for 2 things:

  1. To confirm the door was opened (as it might fail to pop open).

  2. To get notified when your user closed the locker door, and move the app to the next screen/state.

Reopen Locker Door

Another thing you might want to do is to reopen the lastly opened locker. This might be useful if your user forgot to add something inside, or if the door was accidentally closed before the user completed the drop off.

Call the sendReopenLocker function to reopen the same locker that was lastly opened in your session. If no door was opened during your session, the completion handler will return an error.

Terminate a session

After you’re done interacting with the tower, you must terminate your session. This will trigger some processes both in the SDK and the tower, that are important to keep consistency in the system.

Since the tower doesn’t have an internet connection, your application (through the SDK) is a key piece to keep it updated and synchronized with Harbor’s backend.

Call the sendTerminateSession function to terminate a session. This function allows you to specify an error code and message, in case the session is being terminated prematurely for some reason. This code and message will be helpful for us in case we need to debug your issue.

The sendTerminateSession function receives the following parameters:

  • errorCode: A code to log to the terminate session event on the device side. Maybe 0 if no error occurred.

  • errorMessage: An optional message to store in the error event log, in case there was an error.

  • completionHandler: A callback containing a boolean indicating if the session was terminated successfully, and an optional error in case something unexpected happened.

Terminating the session triggers a sync process on the SDK, which is important to notify Harbor that the locker was used and to update the state of the tower.

This covers the basic use of a Harbor Tower through the SDK.

Debug Logging

When the debug logging feature is enabled, each step of the process in Harbor SDK is recorded in a log callback. This log callback provides a detailed record that can be utilized for analyzing and addressing any failures that may occur during the connection and drop-off/pick-up process. By reviewing the log, you can gain insights into the specific actions and events that took place, allowing you to troubleshoot and resolve any issues effectively.

After you initialize the SDK, set the Logging capabilities up using the log level you need:

- HarborLockersSDK.setLogLevel('debug'); // can be: debug, verbose, info, warning, error

Then, using an Event Emitter add a listener for HarborLogged:

- subscriptionLog = eventEmitter.addListener('HarborLogged', result => {
 console.log("Harbor Log: ", result);

Finally, remember to remove the subscription when you are no longer going to use it, subscriptionLog.remove();

Important to note, the logging capability for our React Native SDK works as of version 1.0.33