بدء استخدام حزمة تطوير البرامج (SDK) لتطبيق Drive لنظام التشغيل iOS

حزمة Driver SDK هي مكتبة تدمجها في تطبيق السائق الخاص بك. وتكون مسؤولة عن تحديث Fleet Engine حسب الموقع الجغرافي للسائق ومساره والمسافة المتبقية والوقت المقدَّر للوصول. كما أنها تتكامل مع SDK للتنقل، والتي توفر إرشادات التنقل خطوة بخطوة للسائق.

الحد الأدنى من متطلبات النظام

  • يجب أن يعمل الجهاز الجوّال بنظام التشغيل iOS 14 أو بإصدار أحدث.
  • الإصدار 14 من Xcode أو إصدار أحدث.
  • المتطلبات الأساسية

    يفترض هذا الدليل أنّ تطبيقك يستخدم حزمة تطوير البرامج (SDK) للتنقّل، وأنّ الخلفية Fleet Engine قد تم إعدادها وتوفّرها. ومع ذلك، يوفّر الرمز النموذجي نموذجًا عن كيفية إعداد حزمة تطوير البرامج (SDK) الخاصة بالتنقّل.

    عليك أيضًا تفعيل حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" لنظام التشغيل iOS في مشروعك على Google Cloud والحصول على مفتاح واجهة برمجة تطبيقات.

    الحصول على إذن بالوصول

    إذا كنت أحد عملاء Google Workspace، عليك إنشاء مجموعة Workspace، مثل google-maps-platform-sdk-users@workspacedomain.com أثناء عملية الإعداد، وتقديم الاسم إلى Google. وهذا هو النهج الموصى به. ستتم بعد ذلك إضافة مجموعة Workspace إلى قائمة مسموح بها تمنح إمكانية الوصول إلى مستودعات CocoaPods الصحيحة. تأكد من أن هذه القائمة تتضمن عناوين البريد الإلكتروني للمستخدم ورسائل البريد الإلكتروني لحساب الخدمة التي تحتاج إلى الوصول.

    إذا لم تتمكّن مؤسستك من إنشاء "مجموعات Workspace"، يمكنك إرسال قائمة بعناوين البريد الإلكتروني لحساب المستخدم والخدمة التي تحتاج إلى الوصول إلى هذه العناصر إلى Google.

    التنمية المحلية

    يكفي تسجيل الدخول باستخدام حزمة تطوير البرامج (SDK) للسحابة الإلكترونية لأغراض التطوير المحلي.

    gcloud

    gcloud auth login
    

    يجب أن يكون عنوان البريد الإلكتروني المُستخدَم لتسجيل الدخول عضوًا في مجموعة Workspace.

    التشغيل الآلي (أنظمة بناء أو دمج مستمر)

    عليك إعداد مضيفي التشغيل الآلي من خلال اتّباع أفضل الممارسات:

    • في حال إجراء العملية داخل بيئة Google Cloud، يمكنك استخدام الرصد التلقائي لبيانات الاعتماد.

    • بخلاف ذلك، عليك تخزين ملف مفتاح حساب الخدمة في موقع آمن على نظام ملفات المضيف وضبط متغيّر بيئة GOOGLE_APPLICATION_CREDENTIALS بشكل مناسب.

    يجب أن يكون عنوان البريد الإلكتروني لحساب الخدمة المرتبط ببيانات الاعتماد عضوًا في Workspace Goup.

    تكوين المشروع

    يمكنك ضبط حزمة تطوير البرامج (SDK) للسائق باستخدام CocoaPods.

    استخدام CocoaPods

    لإعداد "حزمة تطوير البرامج (SDK) للسائق" باستخدام CocoaPods، ستحتاج إلى العناصر التالية:

    • أداة CocoaPods: لتثبيت هذه الأداة، افتح المحطة الطرفية وشغِّل الأمر التالي. shell sudo gem install cocoapods يمكنك الرجوع إلى دليل بدء استخدام CocoaPods للحصول على مزيد من التفاصيل.
    1. أنشئ Podfile لحزمة Driver SDK واستخدمه لتثبيت واجهة برمجة التطبيقات وتبعياتها: أنشئ ملفًا باسم Podfile في دليل المشروع. يحدد هذا الملف تبعيات مشروعك. قم بتحرير Podfile وأضف تبعياتك. فيما يلي مثال يتضمن التبعيات:

      source "https://github.com/CocoaPods/Specs.git"
      
      target 'YOUR_APPLICATION_TARGET_NAME_HERE' do
        pod 'GoogleRidesharingDriver'
      end
      
    2. احفظ Podfile. افتح الوحدة الطرفية وانتقل إلى الدليل الذي يحتوي على Podfile:

      cd <path-to-project>
      
    3. شغِّل أمر pod install. سيؤدي هذا إلى تثبيت واجهات برمجة التطبيقات المحددة في Podfile، بالإضافة إلى أي تبعيات قد تكون لديهم.

      pod install
      
    4. أغلق Xcode، ثم افتح (انقر نقرًا مزدوجًا) ملف .xcworkspace الخاص بمشروعك لتشغيل Xcode. من هذا الوقت فصاعدًا، يجب عليك استخدام ملف .xcworkspace لفتح المشروع.

    إصدارات الإصدار الأولي أو التجريبي من حزمة تطوير البرامج (SDK)

    لإعداد الإصدارات "ألفا" أو "الإصدار التجريبي" من "حزمة تطوير البرامج (SDK) لبرنامج التشغيل" لنظام التشغيل iOS، ستحتاج إلى العناصر التالية:

    • أداة CocoaPods: لتثبيت هذه الأداة، افتح المحطة الطرفية وشغِّل الأمر التالي.

      sudo gem install cocoapods
      

      للحصول على مزيد من التفاصيل، يمكنك الاطّلاع على دليل البدء في CocoaPods.

    • حساب التطوير الخاص بك في قائمة الوصول على Google. مستودع مجموعات الإعلانات المتسلسلة من الإصدار الأولي والإصدار التجريبي من حزمة تطوير البرامج (SDK) ليس مصدرًا عامًا. وللوصول إلى هذه الإصدارات، اتصل بمهندس عملاء Google. يضيف المهندس حساب التطوير الخاص بك إلى قائمة الوصول، ثم يضبط ملف تعريف ارتباط للمصادقة.

    بعد أن يصبح مشروعك في قائمة الوصول، يمكنك الوصول إلى المجموعة.

    1. أنشئ Podfile لحزمة Driver SDK لنظام التشغيل iOS واستخدمه لتثبيت واجهة برمجة التطبيقات وتبعياتها: أنشئ ملفًا باسم Podfile في دليل مشروعك. يحدد هذا الملف تبعيات مشروعك. قم بتحرير Podfile وأضف تبعياتك. فيما يلي مثال يتضمن التبعيات:

      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. احفظ Podfile. افتح الوحدة الطرفية وانتقل إلى الدليل الذي يحتوي على Podfile:

      cd <path-to-project>
      
    3. شغِّل أمر pod install. سيؤدي هذا إلى تثبيت واجهات برمجة التطبيقات المحددة في Podfile، بالإضافة إلى أي تبعيات قد تكون لديهم.

      pod install
      
    4. أغلق Xcode، ثم افتح (انقر نقرًا مزدوجًا) ملف .xcworkspace الخاص بمشروعك لتشغيل Xcode. من هذا الوقت فصاعدًا، يجب عليك استخدام ملف .xcworkspace لفتح المشروع.

    تثبيت XCFramework

    XCFramework هي حزمة ثنائية تستخدمها لتثبيت Driver SDK. يمكنك استخدام هذه الحزمة على أنظمة أساسية متعددة، بما في ذلك الأجهزة التي تستخدم شرائح M1. يوضِّح هذا الدليل كيفية إضافة XCFramework الذي يحتوي على حزمة Driver SDK يدويًا إلى مشروعك وضبط إعدادات الإصدار في Xcode.

    تنزيل البرنامج الثنائي لحزمة SDK ومواردها:

    1. فُكَّ ضغط الملفات المضغوطة للوصول إلى XCFramework والموارد.

    2. شغِّل Xcode وافتح مشروعًا حاليًا أو أنشئ مشروعًا جديدًا. إذا كنت مستخدمًا جديدًا لنظام التشغيل iOS، أنشئ مشروعًا جديدًا واختَر نموذج تطبيق iOS.

    3. أنشئ مجموعة أطر العمل ضمن مجموعة المشاريع إذا لم تكن موجودة بالفعل.

    4. اسحب ملف gRPCCertificates.bundle الذي تم تنزيله إلى دليل المستوى الأعلى لمشروع Xcode. عندما يُطلب منك، حدد "نسخ العناصر" إذا لزم الأمر.

    5. لتثبيت Driver SDK، اسحب الملف GoogleRidesharingDriver.xcframework إلى مشروعك ضمن الأطر والمكتبات والمحتوى المضمّن. عندما يُطلب منك، حدد "نسخ العناصر" إذا لزم الأمر.

    6. اسحب ملف GoogleRidesharingDriver.bundle الذي تم تنزيله إلى دليل المستوى الأعلى لمشروع Xcode. اختَر "Copy items if needed" عندما يُطلب منك ذلك.

    7. حدد مشروعك من Project Navigator (أداة استكشاف المشروع)، واختر هدف تطبيقك.

    8. افتح علامة التبويب "مراحل الإنشاء"، وفي "ربط النظام الثنائي مع المكتبات"، أضف أطر العمل والمكتبات التالية إذا لم تكن موجودة بالفعل:

      • 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. اختَر مشروعك بدلاً من هدف محدّد، وافتح علامة التبويب إعدادات الإنشاء. في القسم علامات رابط أخرى، أضِف ‑ObjC لكل من تصحيح الأخطاء والإصدار. إذا لم تظهر هذه الإعدادات، عليك تغيير الفلتر في شريط "إعدادات الإصدار" من أساسي إلى الكل.

    تنفيذ التفويض والمصادقة

    عندما ينشئ تطبيق Drive تحديثات لخلفية Fleet Engine ويرسلها، يجب أن تتضمّن الطلبات رموز دخول صالحة. ولتفويض هذه الطلبات ومصادقتها، تستدعي Driver SDK العنصر الخاص بك بما يتوافق مع بروتوكول GMTDAuthorization. الكائن مسؤول عن توفير رمز الدخول المطلوب.

    بصفتك مطوِّر التطبيق، يمكنك اختيار طريقة إنشاء الرموز المميّزة. ينبغي أن يوفر التنفيذ لديك إمكانية القيام بما يلي:

    • استرجع رمز الدخول من خادم HTTPS، قد يكون ذلك بتنسيق JSON.
    • تحليل الرمز المميّز وتخزينه مؤقتًا
    • أعِد تحميل الرمز المميّز عند انتهاء صلاحيته.

    لمعرفة تفاصيل الرموز المميّزة التي يتوقعها خادم Fleet Engine، يُرجى الاطّلاع على المقالة إنشاء رمز JSON المميّز للويب (JWT) للتفويض.

    ويكون رقم تعريف الموفِّر هو نفسه رقم تعريف مشروع Google Cloud. يُرجى الاطّلاع على دليل مستخدم Fleet Engine Deliveries API للحصول على مزيد من المعلومات.

    يقوم المثال التالي بتنفيذ موفر رمز دخول:

    #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
    

    إنشاء مثيل DeliveryDriverAPI

    للحصول على مثيل GMTDDeliveryVehicleReporter، عليك أولاً إنشاء مثيل GMTDDeliveryDriverAPI باستخدام providerID وvehicleID وdriverContext وaccessTokenProvider. ويكون معرِّف الخدمة مماثلاً لرقم تعريف مشروع Google Cloud. ويمكنك الوصول إلى النسخة الافتراضية GMTDDeliveryVehicleReporter من واجهة برمجة تطبيقات برنامج التشغيل مباشرةً.

    ينشئ المثال التالي مثيل 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];
    }
    

    يمكنك الاستماع اختياريًا إلى أحداث AutomotiveReporter.

    يتم تعديل المركبة بشكل دوري من خلال "GMTDDeliveryVehicleReporter" عندما تكون قيمة "locationTrackingEnabled" "نعم". للاستجابة لهذه التحديثات الدورية، يمكن لأي كائن الاشتراك في أحداث GMTDDeliveryVehicleReporter من خلال التوافق مع بروتوكول GMTDVehicleReporterListener.

    يمكنك معالجة الأحداث التالية:

    • vehicleReporter:didSucceedVehicleUpdate

      يخبر تطبيق Driver تطبيق Drive بأنّ خدمات الخلفية قد تلقّت بنجاح موقع السيارة وتعديل الحالة.

    • vehicleReporter:didFailVehicleUpdate:withError

      يُعلِم المستمع أنّه تعذّر تعديل المركبة. طالما تم تفعيل ميزة "تتبُّع الموقع الجغرافي"، يواصل GMTDDeliveryVehicleReporter إرسال أحدث البيانات إلى خلفية Fleet Engine.

    يتناول المثال التالي هذه الأحداث:

    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
    

    تفعيل تتبُّع الموقع الجغرافي

    لتفعيل تتبُّع الموقع الجغرافي، يمكن لتطبيقك ضبط locationTrackingEnabled على YES في GMTDDeliveryVehicleReporter. بعد ذلك، سيرسل GMTDDeliveryVehicleReporter = تحديثات الموقع الجغرافي تلقائيًا. عندما تكون ميزة GMSNavigator في وضع التنقّل (عند ضبط وجهة من خلال setDestinations) ويتم ضبط locationTrackingEnabled على YES، سيرسل GMTDDeliveryVehicleReporter تلقائيًا أيضًا تعديلات المسار والوقت المقدّر للوصول.

    سيكون المسار الذي تم ضبطه أثناء هذه التحديثات هو المسار نفسه الذي يتنقل إليه السائق أثناء جلسة التنقّل. وبالتالي، لكي تعمل ميزة تتبُّع الشحن بشكل صحيح، يجب أن تتطابق نقطة الطريق المحدَّدة عبر -setDestinations:callback: مع الوجهة المحدّدة في الواجهة الخلفية لـ Fleet Engine.

    يعمل المثال التالي على تفعيل تتبع الموقع الجغرافي:

    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
    

    يبلغ الفاصل الزمني لإعداد التقارير 10 ثوانٍ تلقائيًا، ولكن يمكن تغيير الفاصل الزمني لإعداد التقارير باستخدام locationUpdateInterval. الحد الأدنى للفاصل الزمني للتحديث المتوافق هو 5 ثوانٍ. الحد الأقصى للفاصل الزمني للتحديث المتوافق هو 60 ثانية. قد تؤدي التحديثات الأكثر تكرارًا إلى بطء الطلبات والأخطاء.

    إيقاف تحديثات الموقع الجغرافي

    يمكن لتطبيقك إيقاف تحديثات الموقع الجغرافي لمركبة. على سبيل المثال، عند انتهاء وردية السائق، يمكن لتطبيقك ضبط السمة locationTrackingEnabled على NO.

      _vehicleReporter.locationTrackingEnabled = NO
    

    التعامل مع أخطاء update_mask

    عندما يرسل GMTDDeliveryVehicleReporter تحديثًا للمركبة، يمكن أن يحدث خطأ update_mask عندما يكون القناع فارغًا، ويحدث هذا الخطأ عادةً عند أول تحديث بعد بدء التشغيل. يوضح المثال التالي كيفية التعامل مع هذا الخطأ:

    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