Follow a trip in iOS

  • Journey Sharing allows consumer apps to display the real-time location of a vehicle during a trip on a map.

  • Developers need to initialize map components, manage a Journey Sharing session, and register for trip updates to integrate the feature.

  • Trip progress updates, including location, status, and ETA, are received through the GMTCTripModelSubscriber protocol methods.

  • Error handling is crucial, with both server-side and client-side errors needing to be addressed using appropriate callbacks and error codes.

  • Starting and stopping a trip involve managing the session and the flow of trip updates, ensuring a smooth user experience.

Select platform: Android iOS JavaScript

When you follow a trip, your consumer app displays the location of the appropriate vehicle to the consumer. To do this, your app needs to start following a trip, update trip progress, and stop following a trip when it completes.

This document covers how that process works.

Start following a trip

Here's how you start following a trip:

  • Gather all user inputs such as drop off and pickup locations from a ViewController .

  • Create a new ViewController to start following a trip directly.

The following example shows how to start following a trip immediately after the view loads.

Swift

  /* 
 * MapViewController.swift 
 */ 
 override 
  
 func 
  
 viewDidLoad 
 () 
  
 { 
  
 super 
 . 
 viewDidLoad 
 () 
  
 ... 
  
 self 
 . 
 mapView 
  
 = 
  
 GMTCMapView 
 ( 
 frame 
 : 
  
 UIScreen 
 . 
 main 
 . 
 bounds 
 ) 
  
 self 
 . 
 mapView 
 . 
 delegate 
  
 = 
  
 self 
  
 self 
 . 
 view 
 . 
 addSubview 
 ( 
 self 
 . 
 mapView 
 ) 
 } 
 func 
  
 mapViewDidInitializeCustomerState 
 ( 
 _ 
 : 
  
 GMTCMapView 
 ) 
  
 { 
  
 self 
 . 
 mapView 
 . 
 pickupLocation 
  
 = 
  
 self 
 . 
 selectedPickupLocation 
  
 self 
 . 
 mapView 
 . 
 dropoffLocation 
  
 = 
  
 self 
 . 
 selectedDropoffLocation 
  
 self 
 . 
 startConsumerMatchWithLocations 
 ( 
  
 pickupLocation 
 : 
  
 self 
 . 
 mapView 
 . 
 pickupLocation 
 !, 
  
 dropoffLocation 
 : 
  
 self 
 . 
 mapView 
 . 
 dropoffLocation 
 ! 
  
 ) 
  
 { 
  
 [ 
 weak 
  
 self 
 ] 
  
 ( 
 tripName 
 , 
  
 error 
 ) 
  
 in 
  
 guard 
  
 let 
  
 strongSelf 
  
 = 
  
 self 
  
 else 
  
 { 
  
 return 
  
 } 
  
 if 
  
 error 
  
 != 
  
 nil 
  
 { 
  
 // print error message. 
  
 return 
  
 } 
  
 let 
  
 tripService 
  
 = 
  
 GMTCServices 
 . 
 shared 
 (). 
 tripService 
  
 // Create a tripModel instance for listening the update of the trip 
  
 // specified by this trip name. 
  
 let 
  
 tripModel 
  
 = 
  
 tripService 
 . 
 tripModel 
 ( 
 forTripName 
 : 
  
 tripName 
 ) 
  
 // Create a journeySharingSession instance based on the tripModel 
  
 let 
  
 journeySharingSession 
  
 = 
  
 GMTCJourneySharingSession 
 ( 
 tripModel 
 : 
  
 tripModel 
 ) 
  
 // Add the journeySharingSession instance on the mapView for UI updating. 
  
 strongSelf 
 . 
 mapView 
 . 
 show 
 ( 
 journeySharingSession 
 ) 
  
 // Register for the trip update events. 
  
 tripModel 
 . 
 register 
 ( 
 strongSelf 
 ) 
  
 strongSelf 
 . 
 currentTripModel 
  
 = 
  
 tripModel 
  
 strongSelf 
 . 
 currentJourneySharingSession 
  
 = 
  
 journeySharingSession 
  
 strongSelf 
 . 
 hideLoadingView 
 () 
  
 } 
  
 self 
 . 
 showLoadingView 
 () 
 } 
 

Objective-C

  /* 
 * MapViewController.m 
 */ 
 - 
 ( 
 void 
 ) 
 viewDidLoad 
  
 { 
  
 [ 
 super 
  
 viewDidLoad 
 ]; 
  
 ... 
  
 self 
 . 
 mapView 
  
 = 
  
 [[ 
 GMTCMapView 
  
 alloc 
 ] 
  
 initWithFrame 
 : 
 CGRectZero 
 ]; 
  
 self 
 . 
 mapView 
 . 
 delegate 
  
 = 
  
 self 
 ; 
  
 [ 
 self 
 . 
 view 
  
 addSubview 
 : 
 self 
 . 
 mapView 
 ]; 
 } 
 // Handle the callback when the GMTCMapView did initialized. 
 - 
 ( 
 void 
 ) 
 mapViewDidInitializeCustomerState: 
 ( 
 GMTCMapView 
  
 * 
 ) 
 mapview 
  
 { 
  
 self 
 . 
 mapView 
 . 
 pickupLocation 
  
 = 
  
 self 
 . 
 selectedPickupLocation 
 ; 
  
 self 
 . 
 mapView 
 . 
 dropoffLocation 
  
 = 
  
 self 
 . 
 selectedDropoffLocation 
 ; 
  
 __weak 
  
 __typeof 
 ( 
 self 
 ) 
  
 weakSelf 
  
 = 
  
 self 
 ; 
  
 [ 
 self 
  
 startTripBookingWithPickupLocation 
 : 
 self 
 . 
 selectedPickupLocation 
  
 dropoffLocation 
 : 
 self 
 . 
 selectedDropoffLocation 
  
 completion 
 : 
 ^ 
 ( 
 NSString 
  
 * 
 tripName 
 , 
  
 NSError 
  
 * 
 error 
 ) 
  
 { 
  
 __typeof 
 ( 
 self 
 ) 
  
 strongSelf 
  
 = 
  
 weakSelf 
 ; 
  
 GMTCTripService 
  
 * 
 tripService 
  
 = 
  
 [ 
 GMTCServices 
  
 sharedServices 
 ]. 
 tripService 
 ; 
  
 // Create a tripModel instance for listening to updates to the trip specified by this trip name. 
  
 GMTCTripModel 
  
 * 
 tripModel 
  
 = 
  
 [ 
 tripService 
  
 tripModelForTripName 
 : 
 tripName 
 ]; 
  
 // Create a journeySharingSession instance based on the tripModel. 
  
 GMTCJourneySharingSession 
  
 * 
 journeySharingSession 
  
 = 
  
 [[ 
 GMTCJourneySharingSession 
  
 alloc 
 ] 
  
 initWithTripModel 
 : 
 tripModel 
 ]; 
  
 // Add the journeySharingSession instance on the mapView for updating the UI. 
  
 [ 
 strongSelf 
 . 
 mapView 
  
 showMapViewSession 
 : 
 journeySharingSession 
 ]; 
  
 // Register for trip update events. 
  
 [ 
 tripModel 
  
 registerSubscriber 
 : 
 self 
 ]; 
  
 strongSelf 
 . 
 currentTripModel 
  
 = 
  
 tripModel 
 ; 
  
 strongSelf 
 . 
 currentJourneySharingSession 
  
 = 
  
 journeySharingSession 
 ; 
  
 [ 
 strongSelf 
  
 hideLoadingView 
 ]; 
  
 }]; 
  
 [ 
 self 
  
 showLoadingView 
 ]; 
 } 
 

Stop following a trip

You stop following a trip when it is complete or canceled. The following example shows how to stop sharing the active trip.

Swift

  /* 
 * MapViewController.swift 
 */ 
 func 
  
 cancelCurrentActiveTrip 
 () 
  
 { 
  
 // Stop the tripModel 
  
 self 
 . 
 currentTripModel 
 . 
 unregisterSubscriber 
 ( 
 self 
 ) 
  
 // Remove the journey sharing session from the mapView's UI stack. 
  
 self 
 . 
 mapView 
 . 
 hide 
 ( 
 journeySharingSession 
 ) 
 } 
 

Objective-C

  /* 
 * MapViewController.m 
 */ 
 - 
 ( 
 void 
 ) 
 cancelCurrentActiveTrip 
  
 { 
  
 // Stop the tripModel 
  
 [ 
 self 
 . 
 currentTripModel 
  
 unregisterSubscriber 
 : 
 self 
 ]; 
  
 // Remove the journey sharing session from the mapView's UI stack. 
  
 [ 
 self 
 . 
 mapView 
  
 hideMapViewSession 
 : 
 journeySharingSession 
 ]; 
 } 
 

Update trip progress

During a trip, you manage trip progress as follows:

When a trip completes or is canceled, stop listening for updates. For an example, see Stop listening for updates example .

Start listening for updates example

The following example shows how to register the tripModel callback.

Swift

  /* 
 * MapViewController.swift 
 */ 
 override 
  
 func 
  
 viewDidLoad 
 () 
  
 { 
  
 super 
 . 
 viewDidLoad 
 () 
  
 // Register for trip update events. 
  
 self 
 . 
 currentTripModel 
 . 
 register 
 ( 
 self 
 ) 
 } 
 

Objective-C

  /* 
 * MapViewController.m 
 */ 
 - 
 ( 
 void 
 ) 
 viewDidLoad 
  
 { 
  
 [ 
 super 
  
 viewDidLoad 
 ]; 
  
 // Register for trip update events. 
  
 [ 
 self 
 . 
 currentTripModel 
  
 registerSubscriber 
 : 
 self 
 ]; 
  
 ... 
 } 
 

Stop listening for updates example

The following example shows how to cancel registration of the tripModel callback.

Swift

  /* 
 * MapViewController.swift 
 */ 
 deinit 
  
 { 
  
 self 
 . 
 currentTripModel 
 . 
 unregisterSubscriber 
 ( 
 self 
 ) 
 } 
 

Objective-C

  /* 
 * MapViewController.m 
 */ 
 - 
 ( 
 void 
 ) 
 dealloc 
  
 { 
  
 [ 
 self 
 . 
 currentTripModel 
  
 unregisterSubscriber 
 : 
 self 
 ]; 
  
 ... 
 } 
 

Handle trip updates example

The following example shows how to implement the GMTCTripModelSubscriber protocol for handling callbacks when the trip state is updated.

Swift

  /* 
 * MapViewController.swift 
 */ 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdate 
  
 trip 
 : 
  
 GMTSTrip 
 ?, 
  
 updatedPropertyFields 
 : 
  
 GMTSTripPropertyFields 
 ) 
  
 { 
  
 // Update the UI with the new `trip` data. 
  
 self 
 . 
 updateUI 
 ( 
 with 
 : 
  
 trip 
 ) 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdate 
  
 tripStatus 
 : 
  
 GMTSTripStatus 
 ) 
  
 { 
  
 // Handle trip status did change. 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdateActiveRouteRemainingDistance 
  
 activeRouteRemainingDistance 
 : 
  
 Int32 
 ) 
  
 { 
  
 // Handle remaining distance of active route did update. 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdateActiveRoute 
  
 activeRoute 
 : 
  
 [ 
 GMTSLatLng 
 ]?) 
  
 { 
  
 // Handle trip active route did update. 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdate 
  
 vehicleLocation 
 : 
  
 GMTSVehicleLocation 
 ?) 
  
 { 
  
 // Handle vehicle location did update. 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdatePickupLocation 
  
 pickupLocation 
 : 
  
 GMTSTerminalLocation 
 ?) 
  
 { 
  
 // Handle pickup location did update. 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdateDropoffLocation 
  
 dropoffLocation 
 : 
  
 GMTSTerminalLocation 
 ?) 
  
 { 
  
 // Handle drop off location did update. 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdatePickupETA 
  
 pickupETA 
 : 
  
 TimeInterval 
 ) 
  
 { 
  
 // Handle the pickup ETA did update. 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdateDropoffETA 
  
 dropoffETA 
 : 
  
 TimeInterval 
 ) 
  
 { 
  
 // Handle the drop off ETA did update. 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdateRemaining 
  
 remainingWaypoints 
 : 
  
 [ 
 GMTSTripWaypoint 
 ]?) 
  
 { 
  
 // Handle updates to the pickup, dropoff or intermediate destinations of the trip. 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didFailUpdateTripWithError 
  
 error 
 : 
  
 Error 
 ?) 
  
 { 
  
 // Handle the error. 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdateIntermediateDestinations 
  
 intermediateDestinations 
 : 
  
 [ 
 GMTSTerminalLocation 
 ]?) 
  
 { 
  
 // Handle the intermediate destinations being updated. 
 } 
 func 
  
 tripModel 
 ( 
 _ 
 : 
  
 GMTCTripModel 
 , 
  
 didUpdateActiveRouteTraffic 
  
 activeRouteTraffic 
 : 
  
 GMTSTrafficData 
 ?) 
  
 { 
  
 // Handle trip active route traffic being updated. 
 } 
 

Objective-C

  /* 
 * MapViewController.m 
 */ 
 #pragma mark - GMTCTripModelSubscriber implementation 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdateTrip 
 :( 
 nullable 
  
 GMTSTrip 
  
 * 
 ) 
 trip 
  
 updatedPropertyFields 
 :( 
 enum 
  
 GMTSTripPropertyFields 
 ) 
 updatedPropertyFields 
  
 { 
  
 // Update the UI with the new `trip` data. 
  
 [ 
 self 
  
 updateUIWithTrip 
 : 
 trip 
 ]; 
  
 ... 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdateTripStatus: 
 ( 
 enum 
  
 GMTSTripStatus 
 ) 
 tripStatus 
  
 { 
  
 // Handle trip status did change. 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdateActiveRouteRemainingDistance 
 :( 
 int32_t 
 ) 
 activeRouteRemainingDistance 
  
 { 
  
 // Handle remaining distance of active route did update. 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdateActiveRoute 
 :( 
 nullable 
  
 NSArray<GMTSLatLng 
  
 * 
>  
 * 
 ) 
 activeRoute 
  
 { 
  
 // Handle trip active route did update. 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdateVehicleLocation 
 :( 
 nullable 
  
 GMTSVehicleLocation 
  
 * 
 ) 
 vehicleLocation 
  
 { 
  
 // Handle vehicle location did update. 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdatePickupLocation 
 :( 
 nullable 
  
 GMTSTerminalLocation 
  
 * 
 ) 
 pickupLocation 
  
 { 
  
 // Handle pickup location did update. 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdateDropoffLocation 
 :( 
 nullable 
  
 GMTSTerminalLocation 
  
 * 
 ) 
 dropoffLocation 
  
 { 
  
 // Handle drop off location did update. 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdatePickupETA: 
 ( 
 NSTimeInterval 
 ) 
 pickupETA 
  
 { 
  
 // Handle the pickup ETA did update. 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdateRemainingWaypoints 
 :( 
 nullable 
  
 NSArray<GMTSTripWaypoint 
  
 * 
>  
 * 
 ) 
 remainingWaypoints 
  
 { 
  
 // Handle updates to the pickup, dropoff or intermediate destinations of the trip. 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdateDropoffETA: 
 ( 
 NSTimeInterval 
 ) 
 dropoffETA 
  
 { 
  
 // Handle the drop off ETA did update. 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didFailUpdateTripWithError: 
 ( 
 nullable 
  
 NSError 
  
 * 
 ) 
 error 
  
 { 
  
 // Handle the error. 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdateIntermediateDestinations 
 : 
  
 ( 
 nullable 
  
 NSArray<GMTSTerminalLocation 
  
 * 
>  
 * 
 ) 
 intermediateDestinations 
  
 { 
  
 // Handle the intermediate destinations being updated. 
 } 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didUpdateActiveRouteTraffic 
 :( 
 nullable 
  
 GMTSTrafficData 
  
 * 
 ) 
 activeRouteTraffic 
  
 { 
  
 // Handle trip active route traffic being updated. 
 } 
 

Handle trip errors

If you subscribed the tripModel and an error occurs, you can get the callback of tripModel by implementing the delegate method tripModel(_:didFailUpdateTripWithError:) . Error messages follow the Google Cloud Error standard. For detailed error message definitions and all the error codes, refer to Google Cloud Errors documentation .

Here are some common errors that can occur during trip monitoring:

HTTP RPC Description
400
INVALID_ARGUMENT Client specified an invalid trip name. The trip name must follow the format providers/{provider_id}/trips/{trip_id} . The provider_idmust be the ID of the Cloud Project owned by the service provider.
401
UNAUTHENTICATED You receive this error if there are no valid authentication credentials. For example, if the JWT token is signed without a trip ID or the JWT token has expired.
403
PERMISSION_DENIED You receive this error if the client does not have sufficient permission (for example, a user with the consumer role tries to call updateTrip), if the JWT token is invalid, or the API is not enabled for the client project. The JWT token might be missing or the token is signed with a trip ID that does not match requested trip ID.
429
RESOURCE_EXHAUSTED The resource quota is at zero or the rate of traffic exceeds the limit.
503
UNAVAILABLE Service unavailable. Typically the server is down.
504
DEADLINE_EXCEEDED Request deadline exceeded. This error only occurs if the caller sets a deadline that is shorter than the method's default deadline (that is, the requested deadline is not enough for the server to process the request) and the request did not finish within the deadline.

Handle Consumer SDK Errors

The Consumer SDK sends trip update errors to the consumer app using a callback mechanism. The callback parameter is a platform-specific return type ( TripUpdateError on Android, and NSError on iOS).

Extract status codes

The errors passed to the callback are typically gRPC errors, and you can also extract additional information from them in the form of a status code. For the complete list of status codes, see Status codes and their use in gRPC .

Swift

The NSError is called back in tripModel(_:didFailUpdateTripWithError:) .

  // Called when there is a trip update error. 
 func 
  
 tripModel 
 ( 
 _ 
  
 tripModel 
 : 
  
 GMTCTripModel 
 , 
  
 didFailUpdateTripWithError 
  
 error 
 : 
  
 Error 
 ?) 
  
 { 
  
 // Check to see if the error comes from gRPC. 
  
 if 
  
 let 
  
 error 
  
 = 
  
 error 
  
 as 
  
 NSError 
 ?, 
  
 error 
 . 
 domain 
  
 == 
  
 "io.grpc" 
  
 { 
  
 let 
  
 gRPCErrorCode 
  
 = 
  
 error 
 . 
 code 
  
 ... 
  
 } 
 } 
 

Objective-C

The NSError is called back in tripModel:didFailUpdateTripWithError: .

  // Called when there is a trip update error. 
 - 
 ( 
 void 
 ) 
 tripModel: 
 ( 
 GMTCTripModel 
  
 * 
 ) 
 tripModel 
  
 didFailUpdateTripWithError: 
 ( 
 NSError 
  
 * 
 ) 
 error 
  
 { 
  
 // Check to see if the error comes from gRPC. 
  
 if 
  
 ([ 
 error 
 . 
 domain 
  
 isEqualToString 
 : 
 @"io.grpc" 
 ]) 
  
 { 
  
 NSInteger 
  
 gRPCErrorCode 
  
 = 
  
 error 
 . 
 code 
 ; 
  
 ... 
  
 } 
 } 
 

Interpret status codes

Status codes cover two kinds of errors: server and network-related errors, and client-side errors.

Server and network errors

The following status codes are for either network or server errors, and you don't need to any take action to resolve them. The Consumer SDK automatically recovers from them.

Status Code
Description
ABORTED
The server stopped sending the response. This is normally caused by a server problem.
CANCELLED
The server terminated the outgoing response. This normally happens when
the app is sent to the background, or when there is a state change in the
Consumer app.
INTERRUPTED
DEADLINE_EXCEEDED
The server took too long to respond.
UNAVAILABLE
The server was unavailable. This is normally caused by a network problem.

Client errors

The following status codes are for client errors, and you must take action to resolve them. The Consumer SDK continues retrying to refresh the trip until you end journey sharing, but it won't recover until you take action.

Status Code
Description
INVALID_ARGUMENT
The Consumer app specified an invalid trip name; The trip name must follow the format providers/{provider_id}/trips/{trip_id} .
NOT_FOUND
The trip was never created.
PERMISSION_DENIED
The Consumer app has insufficient permissions. This error occurs when:
  • The Consumer app doesn't have permissions
  • The Consumer SDK isn't enabled for the project in the Google Cloud Console.
  • The JWT token is either missing or is invalid.
  • The JWT token is signed with a trip ID that doesn't match the requested trip.
RESOURCE_EXHAUSTED
The resource quota is at zero, or the rate of traffic flow exceeds the speed limit.
UNAUTHENTICATED
The request failed authentication due to an invalid JWT token. This error occurs either when the JWT token is signed without a trip ID, or when the JWT token has expired.
Create a Mobile Website
View Site in Mobile | Classic
Share by: