Sigue un viaje en iOS

Selecciona la plataforma: Android iOS JavaScript

Cuando sigues un viaje, tu app para el consumidor muestra la ubicación del vehículo adecuado al consumidor. Para ello, tu app debe comenzar a seguir un viaje, actualizar el progreso del viaje y dejar de seguirlo cuando se complete.

En este documento, se explica cómo funciona ese proceso.

Cómo comenzar a seguir un viaje

Sigue estos pasos para comenzar a seguir un viaje:

  • Recopila todas las entradas del usuario, como las ubicaciones de entrega y retiro, desde un ViewController.

  • Crea un nuevo ViewController para comenzar a seguir un viaje directamente.

En el siguiente ejemplo, se muestra cómo comenzar a seguir un viaje inmediatamente después de que se carga la vista.

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

Cómo dejar de seguir un viaje

Dejas de seguir un viaje cuando se completa o se cancela. En el siguiente ejemplo, se muestra cómo dejar de compartir el viaje activo.

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

Actualiza el progreso del viaje

Durante un viaje, puedes administrar el progreso de la siguiente manera:

Cuando se completa o cancela un viaje, deja de detectar actualizaciones. Para ver un ejemplo, consulta Ejemplo de detención de la escucha de actualizaciones.

Ejemplo de cómo comenzar a detectar actualizaciones

En el siguiente ejemplo, se muestra cómo registrar la devolución de llamada tripModel.

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

Ejemplo de cómo dejar de detectar actualizaciones

En el siguiente ejemplo, se muestra cómo cancelar el registro de la devolución de llamada de tripModel.

Swift

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

Objective-C

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

Ejemplo de cómo controlar las actualizaciones de viajes

En el siguiente ejemplo, se muestra cómo implementar el protocolo GMTCTripModelSubscriber para controlar las devoluciones de llamadas cuando se actualiza el estado del viaje.

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

Cómo controlar errores de viajes

Si te suscribiste a tripModel y se produce un error, puedes obtener la devolución de llamada de tripModel implementando el método de delegado tripModel(_:didFailUpdateTripWithError:). Los mensajes de error siguen el estándar de errores de Google Cloud. Para obtener definiciones detalladas de los mensajes de error y todos los códigos de error, consulta la documentación de Errores de Google Cloud.

Estos son algunos errores comunes que pueden ocurrir durante la supervisión de viajes:

HTTP RPC Descripción
400 INVALID_ARGUMENT El cliente especificó un nombre de viaje no válido. El nombre del viaje debe seguir el formato providers/{provider_id}/trips/{trip_id}. El provider_id debe ser el ID del proyecto de Cloud que pertenece al proveedor de servicios.
401 UNAUTHENTICATED Recibirás este error si no hay credenciales de autenticación válidas. Por ejemplo, si el token de JWT se firma sin un ID de viaje o si caducó.
403 PERMISSION_DENIED Recibirás este error si el cliente no tiene permiso suficiente (por ejemplo, si un usuario con el rol de consumidor intenta llamar a updateTrip), si el token JWT no es válido o si la API no está habilitada para el proyecto del cliente. Es posible que falte el token de JWT o que esté firmado con un ID de viaje que no coincide con el ID de viaje solicitado.
429 RESOURCE_EXHAUSTED La cuota de recursos es cero o la tasa de tráfico supera el límite.
503 NO DISPONIBLE Servicio no disponible. Por lo general, el servidor no está en funcionamiento.
504 DEADLINE_EXCEEDED Se excedió el plazo de la solicitud. Este error solo se produce si el llamador establece una fecha límite más corta que la fecha límite predeterminada del método (es decir, la fecha límite solicitada no es suficiente para que el servidor procese la solicitud) y la solicitud no finalizó dentro de la fecha límite.

Cómo controlar los errores del SDK de Consumer

El SDK de Consumer envía errores de actualización del viaje a la app del consumidor a través de un mecanismo de devolución de llamada. El parámetro de devolución de llamada es un tipo de devolución específico de la plataforma (TripUpdateError en Android y NSError en iOS).

Extrae códigos de estado

Por lo general, los errores que se pasan a la devolución de llamada son errores de gRPC, y también puedes extraer información adicional de ellos en forma de un código de estado. Para obtener la lista completa de códigos de estado, consulta Códigos de estado y su uso en gRPC.

Swift

Se llama a la devolución de llamada NSError en 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

Se llama a la devolución de llamada NSError en 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;
    ...
  }
}

Cómo interpretar los códigos de estado

Los códigos de estado abarcan dos tipos de errores: errores relacionados con el servidor y la red, y errores del cliente.

Errores de servidor y de red

Los siguientes códigos de estado corresponden a errores de red o del servidor, y no es necesario que realices ninguna acción para resolverlos. El SDK de Consumer se recupera automáticamente de ellos.

Código de estadoDescripción
ABORTED El servidor dejó de enviar la respuesta. Por lo general, esto se debe a un problema del servidor.
CANCELADO El servidor finalizó la respuesta saliente. Esto suele suceder cuando
la app se envía a segundo plano o cuando hay un cambio de estado en la
app para consumidores.
/, interrumpido
DEADLINE_EXCEEDED El servidor tardó demasiado en responder.
NO DISPONIBLE El servidor no estaba disponible. Por lo general, esto se debe a un problema de red.

Errores de cliente

Los siguientes códigos de estado corresponden a errores del cliente, y debes tomar medidas para resolverlos. El SDK de Consumer seguirá intentando actualizar el viaje hasta que dejes de compartirlo, pero no se recuperará hasta que tomes medidas.

Código de estadoDescripción
INVALID_ARGUMENT La app para el usuario especificó un nombre de viaje no válido. El nombre del viaje debe seguir el formato providers/{provider_id}/trips/{trip_id}.
NOT_FOUND Nunca se creó el viaje.
PERMISSION_DENIED La app de Consumer no tiene permisos suficientes. Este error ocurre en los siguientes casos:
  • La app para consumidores no tiene permisos
  • El SDK de Consumer no está habilitado para el proyecto en la consola de Google Cloud.
  • Falta el token JWT o no es válido.
  • El token de JWT está firmado con un ID de viaje que no coincide con el viaje solicitado.
RESOURCE_EXHAUSTED La cuota de recursos es cero o el flujo de tráfico supera el límite de velocidad.
UNAUTHENTICATED La solicitud no se autenticó debido a un token JWT no válido. Este error ocurre cuando el token de JWT se firma sin un ID de viaje o cuando el token de JWT venció.