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

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

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

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

    يفترض هذا الدليل أن تطبيقك يستخدم حزمة 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)

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

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

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

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

    3. قم بإنشاء مجموعة أطر عمل ضمن مجموعة المشروعات إذا لم تكن هناك مجموعة بالفعل.

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

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

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

    7. اختَر مشروعك من Project Navigator، ثم اختَر الهدف الذي يستهدفه تطبيقك.

    8. افتح علامة التبويب "Build Stages" (مراحل الإنشاء)، وفي Link Binary with Libraries (رابط Binary مع المكتبات)، أضف أطر العمل والمكتبات التالية إذا لم تكن موجودة بالفعل:

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

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

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

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

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

    لمعرفة تفاصيل الرموز المميّزة التي يتوقعها خادم 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

      تُعلم تطبيق 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