

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 实施推送同步
<a name="push-sync"></a>

****  
如果您是 Amazon Cognito Sync 的新用户，请使用 [AWS AppSync](https://aws.amazon.com/appsync/)。与 Amazon Cognito Sync 一样， AWS AppSync 它是一项用于跨设备同步应用程序数据的服务。  
它允许同步用户数据，如应用程序首选项或游戏状态。它还通过允许多个用户实时同步和协作处理共享的数据，来扩展这些功能。

Amazon Cognito 会自动跟踪身份和设备之间的关联。使用推送同步功能，您可以确保在身份数据发生更改后向给定身份的每个实例发送通知。推送同步可以确保，无论特定身份的同步存储数据何时发生更改，与该身份关联的所有设备都会收到一个静音推送通知，通知它们所发生的更改。

**注意**  
Unity 或 Xamarin 不支持推送同步。 JavaScript

您必须首先设置用于推送同步的账户，并在 Amazon Cognito 控制台中启用推送同步，然后才可使用推送同步。

## 创建 Amazon Simple Notification Service（Amazon SNS）应用程序
<a name="create-an-amazon-SNS-app"></a>

为支持的平台创建并配置 Amazon SNS 应用程序，如 [SNS 开发人员指南](https://docs.aws.amazon.com/sns/latest/dg/SNSMobilePush.html)中所述。

## 在 Amazon Cognito 控制台中启用推送同步
<a name="enable-push-sync-in-the-amazon-cognito-console"></a>

您可以通过 Amazon Cognito 控制台启用推送同步。从[控制台主页](https://console.aws.amazon.com/cognito/home)：

1. 单击您需要启用推送同步的身份池的名称。此时将显示身份池的 **Dashboard**（控制面板）页。

1. 在**Dashboard**（控制面板）页的右上角，单击 **Manage Identity Pools**（管理身份池）。此时将显示 **Federated Identities**（联合身份）页。

1. 向下滚动并单击 **Push synchronization**（推送同步）以将其展开。

1. 在 **Service role**（服务角色）下拉菜单中，选择授予 Cognito 发送 SNS 通知的权限的 IAM 角色。在 [AWS IAM 控制台](https://console.aws.amazon.com/iam/home)中，单击 **Create role (创建角色)** 以创建或修改与您身份池关联的角色。

1. 选择一个平台应用程序，然后单击 **Save Changes**（保存更改）。

1. 为应用程序授予 SNS 访问权限

在 AWS Identity and Access Management 控制台中，将您的 IAM 角色配置为拥有完全的 Amazon SNS 访问权限，或者创建一个具有完整 Amazon SNS 访问权限的新角色。以下示例角色信任策略授予 Amazon Cognito Sync 有限代入 IAM 角色的能力。Amazon Cognito Sync 只能在代表 `aws:SourceArn` 条件中的身份池和 `aws:SourceAccount` 条件中的账户时才能代入该角色。

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "cognito-sync.amazonaws.com"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceAccount": "123456789012"
                },
                "ArnLike": {
                    "AWS:SourceArn": "arn:aws:cognito-identity:us-east-1:123456789012:identitypool/us-east-1:177a950c-2c08-43f0-9983-28727EXAMPLE"
                }
            }
        }
    ]
}
```

------

要了解有关 IAM 角色的更多信息，请参阅[角色（委托和联合）](https://docs.aws.amazon.com/IAM/latest/UserGuide/WorkingWithRoles.html)。

## 在您的应用程序中使用推送同步：Android
<a name="push-sync-1.android"></a>

您的应用程序需要导入 Google Play 服务。您可以通过 [Android SDK Manager](http://developer.android.com/tools/help/sdk-manager.html) 下载最新版本的 Google Play 开发工具包。按照 Android 有关 [Android 实施](https://developers.google.com/instance-id/guides/android-implementation)的文档注册您的应用程序并接收来自 GCM 的注册 ID。收到注册 ID 之后，您需要向 Amazon Cognito 注册设备，如以下代码段所示：

```
String registrationId = "MY_GCM_REGISTRATION_ID";
try {
    client.registerDevice("GCM", registrationId);
} catch (RegistrationFailedException rfe) {
    Log.e(TAG, "Failed to register device for silent sync", rfe);
} catch (AmazonClientException ace) {
    Log.e(TAG, "An unknown error caused registration for silent sync to fail", ace);
}
```

现在，您可以订阅设备以接收来自特定数据集的更新：

```
Dataset trackedDataset = client.openOrCreateDataset("myDataset");
if (client.isDeviceRegistered()) {
    try {
        trackedDataset.subscribe();
    } catch (SubscribeFailedException sfe) {
        Log.e(TAG, "Failed to subscribe to datasets", sfe);
    } catch (AmazonClientException ace) {
        Log.e(TAG, "An unknown error caused the subscription to fail", ace);
    }
}
```

要停止接收来自数据集的推送通知，只需调用 unsubscribe 方法即可。要订阅 `CognitoSyncManager` 对象中的所有数据集（或特定子集），请使用 `subscribeAll()`：

```
if (client.isDeviceRegistered()) {
    try {
        client.subscribeAll();
    } catch (SubscribeFailedException sfe) {
        Log.e(TAG, "Failed to subscribe to datasets", sfe);
    } catch (AmazonClientException ace) {
        Log.e(TAG, "An unknown error caused the subscription to fail", ace);
    }
}
```

在您的 [Android BroadcastReceiver](http://developer.android.com/reference/android/content/BroadcastReceiver.html) 对象实现中，您可以检查修改后的数据集的最新版本，并决定您的应用是否需要再次同步：

```
@Override
public void onReceive(Context context, Intent intent) {

    PushSyncUpdate update = client.getPushSyncUpdate(intent);

    // The update has the source (cognito-sync here), identityId of the
    // user, identityPoolId in question, the non-local sync count of the
    // data set and the name of the dataset. All are accessible through
    // relevant getters.

    String source = update.getSource();
    String identityPoolId = update.getIdentityPoolId();
    String identityId = update.getIdentityId();
    String datasetName = update.getDatasetName;
    long syncCount = update.getSyncCount;

    Dataset dataset = client.openOrCreateDataset(datasetName);

    // need to access last sync count. If sync count is less or equal to
    // last sync count of the dataset, no sync is required.

    long lastSyncCount = dataset.getLastSyncCount();
    if (lastSyncCount < syncCount) {
        dataset.synchronize(new SyncCallback() {
            // ...
        });
    }

}
```

推送通知负载中提供以下键：
+ `source`：cognito-sync。这可以作为通知之间的区分因素。
+ `identityPoolId`：身份池 ID。这可用于验证或获取其他信息，但从接收方的角度来看，这并不是不可或缺的。
+ `identityId`：池中的身份 ID。
+ `datasetName`：已更新的数据集的名称。这是为了调用 openOrCreate数据集而提供的。
+ `syncCount`：远程数据集的同步计数。您可以使用此方法来确保本地数据集已过期，并且传入同步是新的。

## 在您的应用程序中使用推送同步：iOS – Objective-C
<a name="push-sync-1.ios-objc"></a>

要获取应用程序的设备令牌，请参阅 Apple 有关注册远程通知的文档。收到作为 NSData 对象的设备令牌后 APNs，您需要使用同步客户端的`registerDevice:`方法向 Amazon Cognito 注册设备，如下所示：

```
AWSCognito *syncClient = [AWSCognito defaultCognito];
    [[syncClient registerDevice: devToken] continueWithBlock:^id(AWSTask *task) {
        if(task.error){
            NSLog(@"Unable to registerDevice: %@", task.error);
        } else {
            NSLog(@"Successfully registered device with id: %@", task.result);
        }
        return nil;
      }
    ];
```

在调试模式下，您的设备将在 APNs 沙盒中注册；在发布模式下，设备将在沙盒中注册。 APNs要接收来自特定数据集的更新，请使用 `subscribe` 方法：

```
[[[syncClient openOrCreateDataset:@"MyDataset"] subscribe] continueWithBlock:^id(AWSTask *task) {
        if(task.error){
            NSLog(@"Unable to subscribe to dataset: %@", task.error);
        } else {
            NSLog(@"Successfully subscribed to dataset: %@", task.result);
        }
        return nil;
      }
    ];
```

要停止接收来自数据集的推送通知，只需调用 `unsubscribe` 方法即可。

```
[[[syncClient openOrCreateDataset:@”MyDataset”] unsubscribe] continueWithBlock:^id(AWSTask *task) {
        if(task.error){
            NSLog(@"Unable to unsubscribe from dataset: %@", task.error);
        } else {
            NSLog(@"Successfully unsubscribed from dataset: %@", task.result);
        }
        return nil;
      }
    ];
```

要订阅 `AWSCognito` 对象中的所有数据集，请调用 `subscribeAll`：

```
[[syncClient subscribeAll] continueWithBlock:^id(AWSTask *task) {
        if(task.error){
            NSLog(@"Unable to subscribe to all datasets: %@", task.error);
        } else {
            NSLog(@"Successfully subscribed to all datasets: %@", task.result);
        }
        return nil;
      }
    ];
```

在调用 `subscribeAll` 之前，请确保在每个数据集上至少同步一次，以便数据集存在于服务器上。

要对推送通知做出反应，您需要在应用程序委托上实施 `didReceiveRemoteNotification` 方法：

```
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
    {
        [[NSNotificationCenter defaultCenter] postNotificationName:@"CognitoPushNotification" object:userInfo];
    }
```

如果您使用通知处理程序发布通知，则可以在您拥有数据集句柄的应用程序中的其他位置响应通知。如果您按照如下方式订阅通知...

```
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceivePushSync:)
    name: :@"CognitoPushNotification" object:nil];
```

...则可以按照如下所示处理通知：

```
- (void)didReceivePushSync:(NSNotification*)notification
    {
        NSDictionary * data = [(NSDictionary *)[notification object] objectForKey:@"data"];
        NSString * identityId = [data objectForKey:@"identityId"];
        NSString * datasetName = [data objectForKey:@"datasetName"];
        if([self.dataset.name isEqualToString:datasetName] && [self.identityId isEqualToString:identityId]){
            [[self.dataset synchronize] continueWithBlock:^id(AWSTask *task) {
                if(!task.error){
                    NSLog(@"Successfully synced dataset");
                }
                return nil;
            }];
        }
    }
```

推送通知负载中提供以下键：
+ `source`：cognito-sync。这可以作为通知之间的区分因素。
+ `identityPoolId`：身份池 ID。这可用于验证或获取其他信息，但从接收方的角度来看，这并不是不可或缺的。
+ `identityId`：池中的身份 ID。
+ `datasetName`：已更新的数据集的名称。这可用于 `openOrCreateDataset` 调用。
+ `syncCount`：远程数据集的同步计数。您可以使用此方法来确保本地数据集已过期，并且传入同步是新的。

## 在您的应用程序中使用推送同步：iOS – Swift
<a name="push-sync-1.ios-swift"></a>

要获取应用程序的设备令牌，请参阅 Apple 有关注册远程通知的文档。收到作为 NSData 对象的设备令牌后 APNs，您需要使用同步客户端的 RegisterDevice: 方法向 Amazon Cognito 注册设备，如下所示：

```
let syncClient = AWSCognito.default()
syncClient.registerDevice(devToken).continueWith(block: { (task: AWSTask!) -> AnyObject! in
    if (task.error != nil) {
        print("Unable to register device: " + task.error.localizedDescription)

    } else {
        print("Successfully registered device with id: \(task.result)")
    }
    return task
})
```

在调试模式下，您的设备将在 APNs 沙盒中注册；在发布模式下，设备将在沙盒中注册。 APNs要接收来自特定数据集的更新，请使用 `subscribe` 方法：

```
syncClient.openOrCreateDataset("MyDataset").subscribe().continueWith(block: { (task: AWSTask!) -> AnyObject! in
  if (task.error != nil) {
      print("Unable to subscribe to dataset: " + task.error.localizedDescription)

  } else {
      print("Successfully subscribed to dataset: \(task.result)")
  }
  return task
})
```

要停止接收来自数据集的推送通知，请调用 `unsubscribe` 方法：

```
syncClient.openOrCreateDataset("MyDataset").unsubscribe().continueWith(block: { (task: AWSTask!) -> AnyObject! in
  if (task.error != nil) {
      print("Unable to unsubscribe to dataset: " + task.error.localizedDescription)

  } else {
      print("Successfully unsubscribed to dataset: \(task.result)")
  }
  return task
})
```

要订阅 `AWSCognito` 对象中的所有数据集，请调用 `subscribeAll`：

```
syncClient.openOrCreateDataset("MyDataset").subscribeAll().continueWith(block: { (task: AWSTask!) -> AnyObject! in
  if (task.error != nil) {
      print("Unable to subscribe to all datasets: " + task.error.localizedDescription)

  } else {
      print("Successfully subscribed to all datasets: \(task.result)")
  }
  return task
})
```

在调用 `subscribeAll` 之前，请确保在每个数据集上至少同步一次，以便数据集存在于服务器上。

要对推送通知做出反应，您需要在应用程序委托上实施 `didReceiveRemoteNotification` 方法：

```
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject],
  fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
        NSNotificationCenter.defaultCenter().postNotificationName("CognitoPushNotification", object: userInfo)
})
```

如果您使用通知处理程序发布通知，则可以在您拥有数据集句柄的应用程序中的其他位置响应通知。如果您按照如下方式订阅通知...

```
NSNotificationCenter.defaultCenter().addObserver(observer:self,
   selector:"didReceivePushSync:",
   name:"CognitoPushNotification",
   object:nil)
```

...则可以按照如下所示处理通知：

```
func didReceivePushSync(notification: NSNotification) {
    if let data = (notification.object as! [String: AnyObject])["data"] as? [String: AnyObject] {
        let identityId = data["identityId"] as! String
        let datasetName = data["datasetName"] as! String

        if self.dataset.name == datasetName && self.identityId == identityId {
          dataset.synchronize().continueWithBlock {(task) -> AnyObject! in
              if task.error == nil {
                print("Successfully synced dataset")
              }
              return nil
          }
        }
    }
}
```

推送通知负载中提供以下键：
+ `source`：cognito-sync。这可以作为通知之间的区分因素。
+ `identityPoolId`：身份池 ID。这可用于验证或获取其他信息，但从接收方的角度来看，这并不是不可或缺的。
+ `identityId`：池中的身份 ID。
+ `datasetName`：已更新的数据集的名称。这可用于 `openOrCreateDataset` 调用。
+ `syncCount`：远程数据集的同步计数。您可以使用此方法来确保本地数据集已过期，并且传入同步是新的。