您可以使用驱动程序 SDK 为您的 行程和订单进度应用。Driver SDK 提供的是 对按需乘车和送货解决方案舰队引擎的位置和任务更新。
驱动程序 SDK 可让 Fleet Engine 服务和您的自定义服务了解
车辆的位置和状态。例如,车辆可以是 ONLINE
或
OFFLINE
,且车辆位置会随着行程的进行而变化。
最低系统要求
前提条件
本指南假定您的应用已实现导航 SDK和舰队 引擎 后端已设置完毕且可用不过,示例代码提供了一个 如何设置 Navigation SDK。
您还必须启用 Maps SDK for iOS 创建 Google Cloud 项目,并获取 API 密钥。
项目配置
Swift Package Manager
驱动程序 SDK 可通过 进行安装 Swift Package Manager 的相关文档。要添加 SDK,请确保您已 移除了所有现有的驱动程序 SDK 依赖项。
如需将 SDK 添加到新项目或现有项目,请按以下步骤操作:
-
打开 Xcode
project
或workspace
,然后转到 File >添加软件包依赖项。 - 输入 https://github.com/googlemaps/ios-driver-sdk 作为网址,然后按 Enter 键 以提取软件包,然后点击“Add Package”(添加软件包)。
-
如需安装特定的
version
,请将 Dependency Rule 字段设置为以下其中一项: 基于版本的选项对于新项目,我们建议您指定最新版本和 使用“确切版本”选项。完成后,点击“添加软件包”。 -
在 Choose Package Products(选择软件包产品)窗口中,验证
GoogleRidesharingDriver
是否已添加到 您指定的main
目标。完成后,点击“添加软件包”。 -
如需验证安装,请前往目标的
General
窗格。 在框架、库和嵌入式内容中,您应该会看到已安装的软件包。 您还可以查看“Package Dependencies”“Project Navigator”部分 以验证软件包及其版本。
如需更新现有项目的 package
,请按以下步骤操作:
如果您要从 9.0.0 之前的版本升级,就必须移除 以下依赖项:
GoogleMapsBase
、GoogleMapsCore
和GoogleMapsM4B
。请勿移除GoogleMaps
。有关详情,请参阅 9.0.0 版版本说明。在您的 Xcode 项目配置设置中,找到 Frameworks, Libraries, 和嵌入式内容。使用减号(-) 移除以下框架:
GoogleMapsBase
(仅适用于从 9.0.0 之前的版本进行的升级)GoogleMapsCore
(仅适用于从 9.0.0 之前的版本进行的升级)GoogleMapsM4B
(仅适用于从 9.0.0 之前的版本进行的升级)
- 在 Xcode 中,转到“File”(文件)>文件包 >更新到最新的软件包版本”。
- 如需验证安装,请前往 Project Navigator 的 Package Dependencies 部分 以验证软件包及其版本。
如需移除使用以下代码添加的现有驱动程序 SDK 依赖项,请执行以下操作:
CocoaPods
,请按以下步骤操作:
- 关闭 Xcode 工作区。打开终端并执行以下命令:
sudo gem install cocoapods-deintegrate cocoapods-clean pod deintegrate pod cache clean --all
-
移除
Podfile
、Podfile.resolved
和 Xcodeworkspace
(如果您不将其用于 CocoaPods 之外的任何其他用途)。
如需移除已安装的现有驱动程序 SDK 请按照以下步骤操作:
在您的 Xcode 项目配置设置中,找到 Frameworks, 库和嵌入式内容。使用减号
(-)
移除 以下框架:GoogleRidesharingDriver.xcframework
从 Xcode 项目的顶级目录中,删除
GoogleRidesharingDriver
个套装。
CocoaPods
要使用 CocoaPods 配置驱动程序 SDK,您需要具备以下条件:
- CocoaPods 工具:要安装此工具,请打开终端并运行 。
sudo gem install cocoapods
为驱动程序 SDK 创建一个 Podfile,并使用它来安装 API, 其依赖项:在项目目录中创建名为 Podfile 的文件。 此文件定义项目的依赖项。修改 Podfile 依赖项以下是一个包含依赖项的示例:
source "https://github.com/CocoaPods/Specs.git" target 'YOUR_APPLICATION_TARGET_NAME_HERE' do pod 'GoogleRidesharingDriver' end
下面这个示例包含 驱动程序 SDK 作为依赖项:
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
保存 Podfile。打开终端并转到包含 Podfile:
cd <path-to-project>
运行 pod install 命令。这会安装 Podfile 及其可能具有的任何依赖项。
pod install
关闭 Xcode,然后打开(双击)您项目的 .xcworkspace 启动 Xcode。从现在开始,您必须使用 .xcworkspace 文件打开项目。
请参阅 CocoaPods 使用入门 指南,了解更多信息 。
手动安装
<ph type="x-smartling-placeholder">XCFramework 是一个二进制包,用于将 驱动程序 SDK。你可以将此软件包用于 平台,包括使用 Apple 设备的计算机 硅。本指南介绍了如何 手动添加包含 将驱动程序 SDK 添加到项目中并配置 build Xcode 中的设置
下载 SDK 二进制文件和资源:
解压缩文件以访问 XCFramework 和资源。
启动 Xcode,并打开现有项目或创建新项目 项目。如果您是第一次使用 iOS,请创建一个新项目,并选择 iOS 应用模板。
在项目组下创建一个框架组(如果不存在) 。
要安装驱动程序 SDK,请拖动
GoogleRidesharingDriver.xcframework
文件复制到位于以下位置的项目中: 框架、库和嵌入式内容。出现提示时,选择 根据需要复制内容。将已下载的“
GoogleRidesharingDriver.bundle
”拖动到顶层 目录中。出现提示时,选择Copy items if needed
。从项目导航器中选择您的项目,然后选择 应用的目标
打开“Build Phases”标签,并在“Link Binary with Libraries”中添加 以下框架和库(如果尚不存在):
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
选择您的项目(而不是特定目标),然后打开 Build 设置标签页。在 Other Linker Flags(其他链接器标记)部分中,添加
-ObjC
调试和发布。如果看不到这些设置,请更改 “Build Settings”(构建设置)栏中将过滤条件从“Basic”(基本)更改为“All”(全部)。
检查 Apple 隐私清单文件
Apple 要求为 App Store 中的应用提供应用隐私权详细信息。如需了解最新动态和更多信息,请访问 Apple App Store 隐私权详情页面。
Apple 隐私清单文件包含在 SDK 的资源包中。如需验证隐私清单文件是否包含在内并检查其内容,请创建应用的归档,并从归档中生成隐私报告。
实现授权和身份验证
当您的驱动程序应用生成更新并将其发送到 Fleet Engine 后端时,
请求必须包含有效的访问令牌。授权和身份验证
驱动程序 SDK 会调用您的对象,
符合 GMTDAuthorization
协议。对象负责
提供所需的访问令牌
作为应用开发者,您可以选择令牌的生成方式。您的实现 应能够执行以下操作:
- 从 HTTPS 服务器提取访问令牌(可能是 JSON 格式)。
- 解析并缓存令牌。
- 在令牌过期时刷新令牌。
如需详细了解 Fleet Engine 服务器预期的令牌,请参阅创建 JSON Web 令牌 (JWT) 授权。
提供方 ID 与 Google Cloud 项目 ID 相同。请参阅 Fleet Engine 快速入门 指南 。
以下示例实现了访问令牌提供程序:
Swift
import GoogleRidesharingDriver
private let providerURL = "INSERT_YOUR_TOKEN_PROVIDER_URL"
class SampleAccessTokenProvider: NSObject, GMTDAuthorization {
private struct AuthToken {
// The cached vehicle token.
let token: String
// Keep track of when the token expires for caching.
let expiration: TimeInterval
// Keep track of the vehicle ID the cached token is for.
let vehicleID: String
}
enum AccessTokenError: Error {
case missingAuthorizationContext
case missingData
}
private var authToken: AuthToken?
func fetchToken(
with authorizationContext: GMTDAuthorizationContext?,
completion: @escaping GMTDAuthTokenFetchCompletionHandler
) {
// Get the vehicle ID from the authorizationContext. This is set by the Driver SDK.
guard let authorizationContext = authorizationContext else {
completion(nil, AccessTokenError.missingAuthorizationContext)
return
}
let vehicleID = authorizationContext.vehicleID
// If appropriate, use the cached token.
if let authToken = authToken,
authToken.expiration > Date.now.timeIntervalSince1970 && authToken.vehicleID == vehicleID
{
completion(authToken.token, nil)
return
}
// Otherwise, try to fetch a new token from your server.
let request = URLRequest(url: URL(string: providerURL))
let task = URLSession.shared.dataTask(with: request) { [weak self] data, _, error in
guard let strongSelf = self else { return }
guard error == nil else {
completion(nil, error)
return
}
// Replace the following key values with the appropriate keys based on your
// server's expected response.
let vehicleTokenKey = "VEHICLE_TOKEN_KEY"
let tokenExpirationKey = "TOKEN_EXPIRATION"
guard let data = data,
let fetchData = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let token = fetchData[vehicleTokenKey] as? String,
let expiration = fetchData[tokenExpirationKey] as? Double
else {
completion(nil, AccessTokenError.missingData)
return
}
strongSelf.authToken = AuthToken(
token: token, expiration: expiration, vehicleID: vehicleID)
completion(token, nil)
}
task.resume()
}
}
Objective-C
#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 {
// 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 vehicletoken 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
创建 RidesharingDriverAPI 实例
如需获取 GMTDVehicleReporter
实例,您首先需要创建一个
使用 providerID、VehicleID 的 GMTDRidesharingDriverAPI
实例,
driveContext 和 accessTokenProvider。providerID 与 Google 相同
Cloud 项目 ID。您可以从以下位置访问 GMTDVehicleReporter
实例:
Driver API
以下示例将创建一个 GMTDRidesharingDriverAPI
实例:
Swift
import GoogleRidesharingDriver
private let providerID = "INSERT_YOUR_PROVIDER_ID"
class SampleViewController: UIViewController {
private let mapView: GMSMapView
override func viewDidLoad() {
super.viewDidLoad()
let vehicleID = "INSERT_CREATED_VEHICLE_ID"
let accessTokenProvider = SampleAccessTokenProvider()
let driverContext = GMTDDriverContext(
accessTokenProvider: accessTokenProvider,
providerID: providerID,
vehicleID: vehicleID,
navigator: mapView.navigator)
let ridesharingDriverAPI = GMTDRidesharingDriverAPI(driverContext: driverContext)
}
}
Objective-C
#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];
GMTDRidesharingDriverAPI *ridesharingDriverAPI = [[GMTDRidesharingDriverAPI alloc] initWithDriverContext:driverContext];
}
(可选)监听 VehicleReporter 事件
GMTDVehicleReporter
会定期在下列情况下更新车辆:
“locationTrackingEnabled
”为“true
”。为了应对这些定期更新,
对象可以订阅 GMTDVehicleReporter
事件,方法是遵循
GMTDVehicleReporterListener
协议。
您可以处理以下事件:
vehicleReporter(_:didSucceed:)
通知驱动程序应用后端服务已成功收到 车辆位置和状态更新。
vehicleReporter(_:didFail:withError:)
通知监听器车辆更新失败。只要位置 跟踪功能已启用,
GMTDVehicleReporter
会继续发送最新数据 Fleet Engine 后端。
以下示例会处理这些事件:
Swift
import GoogleRidesharingDriver
private let providerID = "INSERT_YOUR_PROVIDER_ID"
class SampleViewController: UIViewController, GMTDVehicleReporterListener {
private let mapView: GMSMapView
override func viewDidLoad() {
// Assumes you have implemented the sample code up to this step.
ridesharingDriverAPI.vehicleReporter.add(self)
}
func vehicleReporter(_ vehicleReporter: GMTDVehicleReporter, didSucceed vehicleUpdate: GMTDVehicleUpdate) {
// Handle update succeeded.
}
func vehicleReporter(_ vehicleReporter: GMTDVehicleReporter, didFail vehicleUpdate: GMTDVehicleUpdate, withError error: Error) {
// Handle update failed.
}
}
Objective-C
/*
* 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 have implemented the sample code up to this step.
[ridesharingDriverAPI.vehicleReporter addListener:self];
}
- (void)vehicleReporter:(GMTDVehicleReporter *)vehicleReporter didSucceedVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate {
// Handle update succeeded.
}
- (void)vehicleReporter:(GMTDVehicleReporter *)vehicleReporter didFailVehicleUpdate:(GMTDVehicleUpdate *)vehicleUpdate withError:(NSError *)error {
// Handle update failed.
}
@end
将 GMTDVehicleReporter 添加为 GMSRoadSnappedLocationProvider 的监听器
为了向驱动程序 SDK 提供位置信息更新,GMTDVehicleReporter
必须设置为 GMSRoadSnappedLocationProvider
的监听器。
Swift
import GoogleRidesharingDriver
private let providerID = "INSERT_YOUR_PROVIDER_ID"
class SampleViewController: UIViewController, GMTDVehicleReporterListener {
private let mapView: GMSMapView
override func viewDidLoad() {
// Assumes you have implemented the sample code up to this step.
if let roadSnappedLocationProvider = mapView.roadSnappedLocationProvider {
roadSnappedLocationProvider.add(ridesharingDriverAPI.vehicleReporter)
roadSnappedLocationProvider.startUpdatingLocation()
}
}
}
Objective-C
/*
* 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 have implemented the sample code up to this step.
[_mapView.roadSnappedLocationProvider addListener:ridesharingDriverAPI.vehicleReporter];
[_mapView.roadSnappedLocationProvider startUpdatingLocation];
}
@end
启用位置跟踪
要启用位置跟踪功能,您的应用可将 locationTrackingEnabled
设置为
GMTDVehicleReporter
true
。GMTDVehicleReporter
会自动发送
位置更新。在服务匹配并将车辆分配给行程后,
当GMSNavigator
时,GMTDVehicleReporter
会自动发送路线更新
处于导航模式(通过 setDestinations
设置目的地时)。
在行程更新期间设置的路线将与驾驶员的路线相同
在导航会话期间导航到哪里。因此,若要让行程分享功能发挥作用,
正确,通过 setDestinations
设置的航点应与
Fleet Engine 后端中设置的目标位置。
如果 locationTrackingEnabled
设置为“true
”,系统会发送行程和车辆更新
定期发送到 Fleet Engine 后端
locationUpdateInterval
。如果 locationTrackingEnabled
设置为 false
,
更新停止,并将最终的车辆更新请求发送到 Fleet Engine
将车辆状态设置为 GMTDVehicleState.offline
。请参阅
updateVehicleState
了解在 locationTrackingEnabled
时处理失败的特殊注意事项
设置为 false
。
以下示例启用位置跟踪:
Swift
import GoogleRidesharingDriver
private let providerID = "INSERT_YOUR_PROVIDER_ID"
class SampleViewController: UIViewController, GMTDVehicleReporterListener {
private let mapView: GMSMapView
override func viewDidLoad() {
// Assumes you have implemented the sample code up to this step.
ridesharingDriverAPI.vehicleReporter.locationTrackingEnabled = true
}
}
Objective-C
/*
* 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 have implemented the sample code up to this step.
ridesharingDriverAPI.vehicleReporter.locationTrackingEnabled = YES;
}
@end
默认情况下,报告间隔为 10 秒,但报告间隔可以
用 locationUpdateInterval
更改。支持的最小更新间隔
是 5 秒。支持的最长更新间隔为 60 秒。更频繁
更新可能会导致请求速度变慢和错误。
更新车辆状态
以下示例展示了如何将车辆状态设置为 ONLINE
。请参阅
updateVehicleState
了解详情。
Swift
import GoogleRidesharingDriver
private let providerID = "INSERT_YOUR_PROVIDER_ID"
class SampleViewController: UIViewController, GMTDVehicleReporterListener {
private let mapView: GMSMapView
override func viewDidLoad() {
// Assumes you have implemented the sample code up to this step.
ridesharingDriverAPI.vehicleReporter.update(.online)
}
}
Objective-C
#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 have implemented the sample code up to this step.
[ridesharingDriverAPI.vehicleReporter
updateVehicleState:GMTDVehicleStateOnline];
}
@end
当掩码为空时,可能会发生 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
停用位置信息更新并使车辆离线
您的应用可以停用更新并让车辆离线。例如,当
驾驶员换班结束时,您的应用可以将 locationTrackingEnabled
设置为 false
。
停用更新还会在 Fleet Engine 上将车辆的状态设置为“OFFLINE
”
后端。
Swift
vehicleReporter.locationTrackingEnabled = false
Objective-C
_vehicleReporter.locationTrackingEnabled = NO;