Pierwsze kroki z pakietem SDK Driver na iOS

Pakiet SDK Driver to biblioteka, którą integrujesz z aplikacją kierowcy. Odpowiada ona za aktualizowanie Fleet Engine o lokalizację kierowcy, trasę, odległość i szacowany czas dotarcia. Integruje się też z pakietem Navigation SDK, który zapewnia szczegółowe instrukcje nawigacji dla kierowcy.

Minimalne wymagania systemowe

  • Na urządzeniu mobilnym musi być zainstalowany iOS 14 lub nowszy.
  • Xcode w wersji 14 lub nowszej.
  • Wymagania wstępne

    W tym przewodniku przyjęto założenie, że Twoja aplikacja korzysta już z nawigacji SDK oraz że backend Fleet Engine jest skonfigurowany i dostępny. Przykładowy kod pokazuje, jak skonfigurować pakiet SDK nawigacji.

    Musisz też włączyć w projekcie Google Cloud pakiet SDK Map Google na iOS i uzyskać klucz interfejsu API.

    Uzyskaj dostęp

    Jeśli jesteś klientem Google Workspace, podczas rejestracji utwórz grupę Workspace, na przykład google-maps-platform-sdk-users@workspacedomain.com, i podaj jej nazwę Google. To zalecane podejście. Twoja grupa Workspace zostanie dodana do listy dozwolonych, która zapewnia dostęp do odpowiednich repozytoriów CocoaPods. Sprawdź, czy adresy e-mail użytkowników i konta usługi, które potrzebują dostępu, znajdują się na tej liście.

    Jeśli Twoja organizacja nie może tworzyć grup Workspace, wyślij do Google listę adresów e-mail użytkowników i kont usługi, którzy potrzebują dostępu do tych artefaktów.

    Programowanie lokalne

    W przypadku programowania lokalnego wystarczy zalogować się przy użyciu pakietu SDK Cloud.

    gcloud

    gcloud auth login
    

    Adres e-mail używany do logowania musi należeć do grupy Workspace.

    Automatyzacja (tworzenie systemów lub integracja ciągła)

    Skonfiguruj hosty automatyzacji zgodnie ze sprawdzonymi metodami:

    • Jeśli proces działa w środowisku Google Cloud, użyj automatycznego wykrywania danych logowania.

    • W przeciwnym razie zapisz plik klucza konta usługi w bezpiecznej lokalizacji w systemie plików hosta i odpowiednio ustaw zmienną środowiskową GOOGLE_APPLICATION_CREDENTIALS.

    Adres e-mail konta usługi powiązany z danymi logowania musi należeć do grupy Workspace.

    Konfiguracja projektu

    Pakiet SDK sterownika możesz skonfigurować za pomocą CocoaPods.

    Używaj CocoaPods

    Aby skonfigurować pakiet SDK sterownika za pomocą CocoaPods, potrzebujesz tych elementów:

    1. Utwórz plik Podfile dla pakietu Driver SDK i użyj go do zainstalowania interfejsu API i jego zależności. Utwórz plik o nazwie Podfile w katalogu projektu. Ten plik definiuje zależności projektu. Edytuj plik Podfile i dodaj zależności. Oto przykład, który przedstawia zależności:

      source "https://github.com/CocoaPods/Specs.git"
      
      target 'YOUR_APPLICATION_TARGET_NAME_HERE' do
        pod 'GoogleRidesharingDriver'
      end
      
    2. Zapisz plik Podfile. Otwórz terminal i przejdź do katalogu zawierającego plik Podfile:

      cd <path-to-project>
      
    3. Uruchom polecenie instalacji poda. Spowoduje to zainstalowanie interfejsów API określonych w pliku Podfile oraz wszystkich zależności, jakie mogą one mieć.

      pod install
      
    4. Zamknij Xcode i otwórz (dwukrotnie) plik .xcworkspace swojego projektu, aby uruchomić Xcode. Od tej pory projekt będzie można otwierać tylko za pomocą pliku .xcworkspace.

    Wersje pakietu SDK (alfa i beta)

    Aby skonfigurować wersję alfa lub beta pakietu Driver SDK na iOS, potrzebujesz tych elementów:

    • Narzędzie CocoaPods: aby zainstalować to narzędzie, otwórz Terminal i uruchom następujące polecenie.

      sudo gem install cocoapods
      

      Więcej informacji znajdziesz w przewodniku dla początkujących na temat CocoaPods.

    • Twoje konto dewelopera znajduje się na liście dostępu Google. Repozytorium poda wersji alfa i beta pakietu SDK nie jest publicznym źródłem. Aby uzyskać dostęp do tych wersji, skontaktuj się z inżynierem Google ds. obsługi klienta. Inżynier dodaje Twoje konto programisty do listy dostępu, a potem ustawia plik cookie na potrzeby uwierzytelniania.

    Gdy projekt znajdzie się na liście dostępu, uzyskasz dostęp do poda.

    1. Utwórz plik Podfile dla pakietu Driver SDK na iOS i użyj go do zainstalowania interfejsu API i jego zależności. Utwórz plik o nazwie Podfile w katalogu projektu. Ten plik definiuje zależności projektu. Edytuj plik Podfile i dodaj zależności. Oto przykład, który przedstawia zależności:

      source "https://cpdc-eap.googlesource.com/ridesharing-driver-sdk.git"
      source "https://github.com/CocoaPods/Specs.git"
      
      target 'YOUR_APPLICATION_TARGET_NAME_HERE' do
        pod 'GoogleRidesharingDriver'
      end
      
    2. Zapisz plik Podfile. Otwórz terminal i przejdź do katalogu zawierającego plik Podfile:

      cd <path-to-project>
      
    3. Uruchom polecenie instalacji poda. Spowoduje to zainstalowanie interfejsów API określonych w pliku Podfile oraz wszystkich zależności, jakie mogą one mieć.

      pod install
      
    4. Zamknij Xcode i otwórz (dwukrotnie) plik .xcworkspace swojego projektu, aby uruchomić Xcode. Od tej pory projekt będzie można otwierać tylko za pomocą pliku .xcworkspace.

    Instalowanie XCFramework

    XCFramework to pakiet binarny służący do instalacji pakietu Driver SDK. Tego pakietu możesz używać na wielu platformach, w tym na maszynach z chipsetem M1. Ten przewodnik pokazuje, jak ręcznie dodać XCFramework zawierający pakiet Driver SDK do projektu i skonfigurować ustawienia kompilacji w Xcode.

    Pobierz plik binarny pakietu SDK i zasoby:

    1. Rozpakuj spakowane pliki, aby uzyskać dostęp do XCFramework i zasobów.

    2. Uruchom Xcode i otwórz istniejący projekt lub utwórz nowy. Jeśli dopiero zaczynasz korzystać z iOS, utwórz nowy projekt i wybierz szablon aplikacji na iOS.

    3. W grupie projektów utwórz grupę Platformy, jeśli jeszcze jej nie ma.

    4. Przeciągnij pobrany plik gRPCCertificates.bundle do katalogu najwyższego poziomu projektu Xcode. Gdy pojawi się odpowiedni komunikat, wybierz Kopiuj elementy, jeśli to konieczne.

    5. Aby zainstalować pakiet SDK sterownika, przeciągnij plik GoogleRidesharingDriver.xcframework do swojego projektu w sekcji Frameworks, Libraries and Embedded Content. Gdy pojawi się odpowiedni komunikat, wybierz Kopiuj elementy, jeśli to konieczne.

    6. Przeciągnij pobrany plik GoogleRidesharingDriver.bundle do katalogu najwyższego poziomu projektu Xcode. Gdy pojawi się taka prośba, wybierz Copy items if needed.

    7. Wybierz projekt z Nawigatora projektów i wskaż środowisko docelowe aplikacji.

    8. Otwórz kartę Etapy kompilacji i w sekcji Link Binary with Libraries dodaj te platformy i biblioteki, jeśli jeszcze ich nie ma:

      • Accelerate.framework
      • AudioToolbox.framework
      • AVFoundation.framework
      • CoreData.framework
      • CoreGraphics.framework
      • CoreLocation.framework
      • CoreTelephony.framework
      • CoreText.framework
      • GLKit.framework
      • ImageIO.framework
      • libc++.tbd
      • libxml2.tbd
      • libz.tbd
      • LocalAuthentication.framework
      • OpenGLES.framework
      • QuartzCore.framework
      • SystemConfiguration.framework
      • UIKit.framework
      • WebKit.framework
    9. Wybierz projekt (a nie konkretny cel) i otwórz kartę Ustawienia kompilacji. W sekcji Inne flagi łączące dodaj parametr ‑ObjC zarówno na potrzeby debugowania, jak i opublikowania wersji. Jeśli te ustawienia nie są widoczne, zmień filtr na pasku ustawień kompilacji z Podstawowe na Wszystkie.

    Wdrażanie autoryzacji i uwierzytelniania

    Gdy aplikacja sterownika generuje i wysyła aktualizacje do backendu Fleet Engine, żądania muszą zawierać prawidłowe tokeny dostępu. Aby autoryzować i uwierzytelnić te żądania, pakiet SDK sterownika wywołuje Twój obiekt zgodny z protokołem GMTDAuthorization. Obiekt jest odpowiedzialny za udostępnienie wymaganego tokena dostępu.

    Jako deweloper aplikacji decydujesz o sposobie generowania tokenów. Implementacja powinna umożliwiać:

    • Pobrać z serwera HTTPS token dostępu (prawdopodobnie w formacie JSON).
    • Przeanalizuj i zapisz token w pamięci podręcznej.
    • Odśwież token, gdy straci ważność.

    Informacje o tokenach oczekiwanych przez serwer Fleet Engine znajdziesz w artykule o tworzeniu tokena internetowego JSON (JWT) na potrzeby autoryzacji.

    Identyfikator dostawcy jest taki sam jak identyfikator projektu Google Cloud. Więcej informacji znajdziesz w przewodniku użytkownika interfejsu Fleet Engine Deliveries API.

    Oto przykład implementacji dostawcy tokena dostępu:

    #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
    

    Tworzenie instancji DeliveryDriverAPI

    Aby pobrać instancję GMTDDeliveryVehicleReporter, musisz najpierw utworzyć instancję GMTDDeliveryDriverAPI z użyciem identyfikatorów providerID, vehicleID, driveContext i accessTokenProvider. Identyfikator dostawcy jest taki sam jak identyfikator projektu Google Cloud. Możesz też uzyskać dostęp do instancji GMTDDeliveryVehicleReporter bezpośrednio z poziomu interfejsu API sterownika.

    Ten przykład tworzy instancję GMTDDeliveryDriverAPI:

    #import “SampleViewController.h”
    #import “SampleAccessTokenProvider.h”
    #import <GoogleRidesharingDriver/GoogleRidesharingDriver.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];
    }
    

    Opcjonalnie nasłuchuj zdarzeń VehicleReporter

    GMTDDeliveryVehicleReporter okresowo aktualizuje pojazd, gdy locationTrackingEnabled ma wartość TAK. Aby odpowiedzieć na te okresowe aktualizacje, dowolny obiekt może zasubskrybować zdarzenia GMTDDeliveryVehicleReporter, działając zgodnie z protokołem GMTDVehicleReporterListener.

    Możesz obsługiwać te zdarzenia:

    • vehicleReporter:didSucceedVehicleUpdate

      Informuje aplikację sterownika, że usługi backendu pomyślnie otrzymały aktualizację lokalizacji i stanu pojazdu.

    • vehicleReporter:didFailVehicleUpdate:withError

      Informuje słuchacza, że aktualizacja pojazdu się nie powiodła. Dopóki śledzenie lokalizacji jest włączone, GMTDDeliveryVehicleReporter nadal wysyła najnowsze dane do backendu Fleet Engine.

    Poniższy przykład obsługuje te zdarzenia:

    SampleViewController.h
    @interface SampleViewController : UIViewController<GMTDVehicleReporterListener>
    @end
    
    SampleViewController.m
    #import “SampleViewController.h”
    #import “SampleAccessTokenProvider.h”
    #import <GoogleRidesharingDriver/GoogleRidesharingDriver.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:(GMTDDeliveryVehicleReporter *)vehicleReporter didSucceedVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate {
      // Handle update succeeded.
    }
    
    - (void)vehicleReporter:(GMTDDeliveryVehicleReporter *)vehicleReporter didFailVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate withError:(NSError *)error {
      // Handle update failed.
    }
    
    @end
    

    Włącz śledzenie lokalizacji

    Aby włączyć śledzenie lokalizacji, ustaw locationTrackingEnabled na YES w: GMTDDeliveryVehicleReporter. Następnie GMTDDeliveryVehicleReporter będzie automatycznie wysyłać aktualizacje lokalizacji. Gdy GMSNavigator jest w trybie nawigacji (gdy miejsce docelowe jest ustawione w setDestinations), a locationTrackingEnabled ma wartość YES, GMTDDeliveryVehicleReporter automatycznie wysyła też aktualizacje trasy i szacowanego czasu dotarcia.

    Trasa ustawiona podczas tych aktualizacji będzie tą samą trasą, którą pokonuje kierowca podczas sesji nawigacji. Dlatego, aby śledzenie przesyłki działało prawidłowo, punkt pośredni ustawiony w -setDestinations:callback: powinien być zgodny z miejscem docelowym ustawionym w backendzie Fleet Engine.

    Poniższy przykład umożliwia włączenie śledzenia lokalizacji:

    SampleViewController.m
    #import “SampleViewController.h”
    #import “SampleAccessTokenProvider.h”
    #import <GoogleRidesharingDriver/GoogleRidesharingDriver.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
    

    Domyślnie interwał raportowania wynosi 10 sekund, ale można go zmienić za pomocą locationUpdateInterval. Minimalny obsługiwany interwał aktualizacji to 5 sekund. Maksymalny obsługiwany interwał aktualizacji to 60 sekund. Częstsze aktualizacje mogą skutkować wolniejszym przesyłaniem żądań i błędami.

    Wyłącz aktualizacje lokalizacji

    Aplikacja może wyłączyć aktualizacje lokalizacji pojazdu. Jeśli na przykład kończy się zmiana kierowcy, aplikacja może ustawić locationTrackingEnabled na NO.

      _vehicleReporter.locationTrackingEnabled = NO
    

    Obsługa błędów update_mask

    Gdy GMTDDeliveryVehicleReporter wysyła aktualizację pojazdu, gdy maska jest pusta, może wystąpić błąd update_mask. Zwykle pojawia się on przy pierwszej aktualizacji po uruchomieniu. Z przykładu poniżej dowiesz się, jak postępować w przypadku tego błędu:

    Swift

    import GoogleRidesharingDriver
    
    class VehicleReporterListener: NSObject, GMTDVehicleReporterListener {
      func vehicleReporter(
        _ vehicleReporter: GMTDVehicleReporter,
        didFail vehicleUpdate: GMTDVehicleUpdate,
        withError error: Error
      ) {
        let fullError = error as NSError
        if let innerError = fullError.userInfo[NSUnderlyingErrorKey] as? NSError {
          let innerFullError = innerError as NSError
          if innerFullError.localizedDescription.contains("update_mask cannot be empty") {
            emptyMaskUpdates += 1
            return
          }
        }
        failedUpdates += 1
      }
    
      override init() {
        emptyMaskUpdates = 0
        failedUpdates = 0
      }
    }
    
    

    Objective-C

    #import "VehicleReporterListener.h"
    #import <GoogleRidesharingDriver/GoogleRidesharingDriver.h>
    
    @implementation VehicleReporterListener {
      NSInteger emptyMaskUpdates = 0;
      NSInteger failedUpdates = 0;
    }
    
    - (void)vehicleReporter:(GMTDVehicleReporter *)vehicleReporter
      didFailVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate
                 withError:(NSError *)error {
      for (NSError *underlyingError in error.underlyingErrors) {
        if ([underlyingError.localizedDescription containsString:@"update_mask cannot be empty"]) {
          emptyMaskUpdates += 1;
          return;
        }
      }
      failedUpdates += 1
    }
    
    @end