Last Mile Fleet Solution is currently available only to select customers. Contact sales to learn more.

Getting started with the Driver SDK for iOS

The Driver SDK keeps the Fleet Engine services and your custom services aware of the vehicle’s location and state. For example, The vehicle can be ONLINE or OFFLINE, and the vehicle location changes as a trip progresses.

Minimum system requirements

The mobile device must be running iOS 12 or later.

Prerequisites

This guide assumes your app already implements the Navigation SDK and that the Fleet Engine backend is set up and available. However, the example code provides a sample of how to set up the Navigation SDK.

You must also enable the Maps SDK for iOS in your Google Cloud Project and Get an API Key.

Get access

If you are a Google Workspace customer, create a Workspace Group such as google-maps-platform-sdk-users@workspacedomain.com during onboarding and provide the name to Google. This is the recommended approach. Your Workspace Group will then be added to an allowlist that grants access to the correct CocoaPods repositories. Confirm that the user emails and service account emails that need access are included in this list.

If your organization cannot create Workspace Groups, send to Google a list of user and service account emails that need access to these artifacts.

Local development

For local development, it is sufficient to log in with the Cloud SDK.

gcloud

gcloud auth login

The email used to log in must be a member of the Workspace Group.

Automation (build systems or continuous integration)

Set up your automation hosts according to best practices:

  • If your process runs inside a Google Cloud environment, use automatic credential detection.

  • Otherwise, store the service account key file in a secure location on the host's file-system and set the GOOGLE_APPLICATION_CREDENTIALS environment variable appropriately.

The service account email associated with the credentials must be a member of the Workspace Goup.

Project configuration

You can configure the Driver SDK for iOS using Cocoapods or manually.

Use Cocoapods

To configure the Driver SDK for iOS you need the following items:

  • The CocoaPods tool: To install this tool, open the Terminal and run the following command.

    sudo gem install cocoapods
    

    Refer to the CocoaPods Getting Started guide for more details.

  • Your development account on the Google Access Control List (ACL): The pod repository of the SDK is not in public source, to access the pod contact the Google Customer Engineer. The engineer adds your development account to the ACL, and then sets a cookie for authentication.

After your project is on the ACL, you can access the pod.

  1. Create a Podfile for the Delivery SDK for iOS and use it to install the API and its dependencies: Create a file named Podfile in your project directory. This file defines your project's dependencies. Edit the Podfile and add your dependencies. Here is an example which includes the dependencies:

    source "https://cpdc-eap.googlesource.com/ridesharing-driver-sdk.git"
    source "https://cpdc-eap.googlesource.com/geo-nav-sdk.git"
    source "https://github.com/CocoaPods/Specs.git"
    
    target 'YOUR_APPLICATION_TARGET_NAME_HERE' do
      pod 'GoogleDeliveryDriver'
    end
    
  2. Save the Podfile. Open a terminal and go to the directory containing the Podfile:

    cd <path-to-project>
    
  3. Run the pod install command. This will install the APIs specified in the Podfile, along with any dependencies they may have.

    pod install
    
  4. Close Xcode, and then open (double-click) your project's .xcworkspace file to launch Xcode. From this time onwards, you must use the .xcworkspace file to open the project.

Configure manually

To configure the Driver SDK for iOS manually:

  1. Install the Maps SDK for iOS

    The Delivery framework requires the Maps SDK for iOS (GoogleMaps framework). Learn more about installing the Maps SDK for iOS.

  2. Add the Delivery framework and resources

    1. Unpack the source ZIP file that you received.

    2. Launch Xcode and either open an existing project, or create a new project.

    3. Select your project from the Project Navigator, and choose your application's target.

    4. Open the Build Phases tab, and within Link Binary with Libraries, and click on the plus sign button (+).

    5. Click Add Other, and select the delivery framework.

    6. From the Delivery framework directory, select the delivery framework GoogleDeliveryDriver.framework

  3. Add resources

    1. In Xcode, open the project's Build Phases, and then within Copy Bundle Resources, and click on the plus sign button (+).

    2. Click Add Other, and select the ride sharing framework.

    3. From the Delivery framework directory, select the following file:

      • gRPCCertificates.bundle (not required if you are already using gRPC in your project).
    4. Under Choose options for adding the files, check the Copy items if needed

Implement authorization and authentication

When your Driver app generates and sends updates to the Fleet Engine backend, the requests must include valid access tokens. In order to authorize and authenticate these requests, the Driver SDK calls your object conforming to the GMTDAuthorization protocol. The object is responsible for providing the required access token.

As the app developer, you choose how tokens are generated. Your implementation should provide the ability to do the following:

  • Fetch an access token, possibly in JSON format, from an HTTPS server.
  • Parse and cache the token.
  • Refresh the token when it expires.

For details of the tokens expected by the Fleet Engine server, see Creating a JSON Web Token (JWT) for authorization.

The provider ID is the same as Google Cloud Project ID. See Fleet Engine Deliveries API User Guide for more information.

The following example implements an access token provider:

#import "SampleAccessTokenProvider.h"
#import <GoogleRidesharingDriver/GoogleRidesharingDriver.h>

// SampleAccessTokenProvider.h
@interface SampleAccessTokenProvider : NSObject<GMTDAuthorization>
@end

static NSString *const PROVIDER_URL = @"INSERT_YOUR_TOKEN_PROVIDER_URL";

// SampleAccessTokenProvider.m
@implementation SampleAccessTokenProvider{
  // The cached vehicle token.
  NSString *_cachedVehicleToken;
  // Keep track of the vehicle ID the cached token is for.
  NSString *_lastKnownVehicleID;
  // Keep track of when tokens expire for caching.
  NSTimeInterval _tokenExpiration;
}

- (void)fetchTokenWithContext:(nullable GMTDAuthorizationContext *)authorizationContext
                   completion:(nonnull GMTDAuthTokenFetchCompletionHandler)completion {
  if (!completion) {
    NSAssert(NO, @"%s encountered an unexpected nil completion.", __PRETTY_FUNCTION__);
    return;
  }

  // Get the vehicle ID from the authorizationContext. This is set by the Driver SDK.
  NSString *vehicleID = authorizationContext.vehicleID;
  if (!vehicleID) {
    NSAssert(NO, @"Vehicle ID is missing from authorizationContext.");
    return;
  }

// Clear cached vehicle token if vehicle ID has changed.
  if (![_lastKnownVehicleID isEqual:vehicleID]) {
    _tokenExpiration = 0.0;
    _cachedVehicleToken = nil;
  }
  _lastKnownVehicleID = vehicleID;

  // Clear cached vehicle token if it has expired.
  if ([[NSDate date] timeIntervalSince1970] > _tokenExpiration) {
    _cachedVehicleToken = nil;
  }

  // If appropriate, use the cached token.
  if (_cachedVehicleToken) {
    completion(_cachedVehicleToken, nil);
    return;
  }
  // Otherwise, try to fetch a new token from your server.
  NSURL *requestURL = [NSURL URLWithString:PROVIDER_URL];
  NSMutableURLRequest *request = 
                          [[NSMutableURLRequest alloc] initWithURL:requestURL];
  request.HTTPMethod = @"GET";
  // Replace the following key values with the appropriate keys based on your
  // server's expected response.
  NSString *vehicleTokenKey = @"VEHICLE_TOKEN_KEY";
  NSString *tokenExpirationKey = @"TOKEN_EXPIRATION";
  __weak typeof(self) weakSelf = self;
  void (^handler)(NSData *_Nullable data, NSURLResponse *_Nullable response,
                  NSError *_Nullable error) =
      ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
        typeof(self) strongSelf = weakSelf;
        if (error) {
          completion(nil, error);
          return;
        }

        NSError *JSONError;
        NSMutableDictionary *JSONResponse =
            [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&JSONError];

        if (JSONError) {
          completion(nil, JSONError);
          return;
        } else {
          // Sample code only. No validation logic.
          id expirationData = JSONResponse[tokenExpirationKey];
          if ([expirationData isKindOfClass:[NSNumber class]]) {
            NSTimeInterval expirationTime = ((NSNumber *)expirationData).doubleValue;
            strongSelf->_tokenExpiration = [[NSDate date] timeIntervalSince1970] + expirationTime;
          }
          strongSelf->_cachedVehicleToken = JSONResponse[vehicleTokenKey];
          completion(JSONResponse[vehicleTokenKey], nil);
        }
    };
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *mainQueueURLSession =  
       [NSURLSession  sessionWithConfiguration:config delegate:nil
delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionDataTask *task = [mainQueueURLSession dataTaskWithRequest:request completionHandler:handler];
[task resume];
}

@end

Create a DeliveryDriverAPI instance

To get a GMTDVehicleReporter instance, you first need to create a GMTDDeliveryDriverAPI instance using the providerID, vehicleID, driverContext and accessTokenProvider. The providerID is the same as Google Cloud Project ID. And you can access the GMTDVehicleReporter instance from the driver API directly.

The following example creates a GMTDDeliveryDriverAPI instance:

#import “SampleViewController.h”
#import “SampleAccessTokenProvider.h”
#import <GoogleDeliveryDriver/GoogleDeliveryDriver.h>

static NSString *const PROVIDER_ID = @"INSERT_YOUR_PROVIDER_ID";

@implementation SampleViewController {
 GMSMapView *_mapView; 
}

- (void)viewDidLoad {
  NSString *vehicleID = @"INSERT_CREATED_VEHICLE_ID";
  SampleAccessTokenProvider *accessTokenProvider = 
                                [[SampleAccessTokenProvider alloc] init];
  GMTDDriverContext *driverContext = 
     [[GMTDDriverContext alloc] initWithAccessTokenProvider:accessTokenProvider
                                                 providerID:PROVIDER_ID 
                                              vehicleID:vehicleID 
      navigator:_mapView.navigator];

  GMTDDeliveryDriverAPI *deliveryDriverAPI = [[GMTDDeliveryDriverAPI alloc] initWithDriverContext:driverContext];
}

Optionally listen to VehicleReporter events

GMTDVehicleReporter periodically updates the vehicle when locationTrackingEnabled is YES. To respond to these periodic updates, any object can subscribe to GMTDVehicleReporter events by conforming to theGMTDVehicleReporterListener protocol.

You can handle the following events:

  • vehicleReporter:didSucceedVehicleUpdate

    Informs the Driver app that the backend services successfully received the vehicle location and state update.

  • vehicleReporter:didFailVehicleUpdate:withError

    Informs the listener that a vehicle update failed. As long as location tracking is enabled, GMTDVehicleReporter continues to send the latest data to Fleet Engine backend.

The following example handles these events:

SampleViewController.h
@interface SampleViewController : UIViewController<GMTDVehicleReporterListener>
@end

SampleViewController.m
#import “SampleViewController.h”
#import “SampleAccessTokenProvider.h”
#import <GoogleDeliveryDriver/GoogleDeliveryDriver.h>

static NSString *const PROVIDER_ID = @"INSERT_YOUR_PROVIDER_ID";

@implementation SampleViewController {
 GMSMapView *_mapView; 
}


- (void)viewDidLoad {
  // ASSUMES YOU IMPLEMENTED HAVE THE SAMPLE CODE UP TO THIS STEP.
  [ridesharingDriverAPI.vehicleReporter addListener:self];
}

- (void)vehicleReporter:(GMTDVehicleReporter *)vehicleReporter didSucceedVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate { 
  // Handle update succeeded.
}

- (void)vehicleReporter:(GMTDVehicleReporter *)vehicleReporter didFailVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate withError:(NSError *)error { 
  // Handle update failed. 
}

@end

Enable location tracking

To enable location tracking, your app can set locationTrackingEnabled to YES on GMTDVehicleReporter. GMTDVehicleReporter automatically sends location updates. After the services match and assign the vehicle to a trip, GMTDVehicleReporter sends route updates automatically when the GMSNavigator is in navigation mode (when a destination is set through setDestinations:).

The route set during the trip update will be the same route the driver is navigating to during the navigation session. Thus, for fleet tracking to work properly, the waypoint set through setDestinations should match the destination set in the Fleet Engine backend.

If locationTrackingEnabled is set to YES, trip and vehicle updates are sent to the Fleet Engine backend at a regular interval based on the value set for locationUpdateInterval. If locationTrackingEnabled is set to NO, updates stop and a final vehicle update request is sent to the Fleet Engine backend to set the vehicle state to GRDVehicleStateOffline. See updateVehicleState for special considerations on handling failures when locationTrackingEnabled is set to NO.

The following example enables location tracking:

SampleViewController.m
#import “SampleViewController.h”
#import “SampleAccessTokenProvider.h”
#import <GoogleDeliveryDriver/GoogleDeliveryDriver.h>

static NSString *const PROVIDER_ID = @"INSERT_YOUR_PROVIDER_ID";

@implementation SampleViewController {
 GMSMapView *_mapView; 
}

- (void)viewDidLoad {
  // ASSUMES YOU IMPLEMENTED HAVE THE SAMPLE CODE UP TO THIS STEP.
  deliveryDriverAPI.vehicleReporter.locationTrackingEnabled = YES;
}

@end

By default, the reporting interval is 10 seconds, but the reporting interval can be changed with locationUpdateInterval. The minimum supported update interval is 1 second. The maximum supported update interval is 60 seconds. More frequent updates may result in slower requests and errors.

Update the vehicle state

The following example shows how to set the vehicle state to ONLINE. See updateVehicleState for details.

#import “SampleViewController.h”
#import “SampleAccessTokenProvider.h”
#import <GoogleDeliveryDriver/GoogleDeliveryDriver.h>

static NSString *const PROVIDER_ID = @"INSERT_YOUR_PROVIDER_ID";

@implementation SampleViewController {
 GMSMapView *_mapView; 
}

- (void)viewDidLoad {
  // ASSUMES YOU IMPLEMENTED HAVE THE SAMPLE CODE UP TO THIS STEP.
  [deliveryDriverAPI.vehicleReporter
                                   updateVehicleState:GMTDVehicleStateOnline];
}

@end

Disable location updates and take the vehicle offline

Your app can disable updates and take the vehicle offline. For example, when a driver’s shift ends, your app can set locationTrackingEnabled to NO. Disabling updates also sets the vehicle’s status to OFFLINE on the Fleet Engine backend.

  _vehicleReporter.locationTrackingEnabled = NO