

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 實作推送同步
<a name="push-sync"></a>

****  
如果您第一次使用 Amazon Cognito Sync，請改用 [AWS AppSync](https://aws.amazon.com/appsync/)。如同 Amazon Cognito Sync， AWS AppSync 是一種跨裝置同步應用程式資料的服務。  
可同步使用者資料，如應用程式偏好設定或遊戲狀態。也擴充這些功能，允許多個使用者在共用資料上即時同步及協作。

Amazon Cognito 會自動追蹤身分與裝置之間的關聯。使用推播同步功能可讓您確保在身分資料變更時，指定身分的每個執行個體都會收到通知。每當特定身分的同步存放區資料變更時，推播同步可確保與該身分相關聯的所有裝置都會收到靜音推送通知，通知他們有所變更。

**注意**  
JavaScript、Unity 或 Xamarin 不支援推播同步。

您必須先設定帳戶來進行推播同步，並且在 Amazon Cognito 主控台中啟用推播同步，才能使用推播同步。

## 建立 Amazon Simple Notification Service (Amazon SNS) 應用程式
<a name="create-an-amazon-SNS-app"></a>

依照 [SNS 開發人員指南](https://docs.aws.amazon.com/sns/latest/dg/SNSMobilePush.html)中的說明，為您的支援平台建立並設定 Amazon SNS 應用程式。

## 在 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 許可的 IAM 角色來傳送 SNS 通知。按一下 **Create role (建立角色)**，在 [AWS IAM 主控台](https://console.aws.amazon.com/iam/home)中建立或修改與身分集區相關聯的角色。

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 SDK。請遵循 [Android 實作](https://developers.google.com/instance-id/guides/android-implementation)的 Android 說明文件來註冊您的應用程式，並從 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`：已更新之資料集的名稱。這可以用來進行 openOrCreateDataset 呼叫。
+ `syncCount`：遠端資料集的同步計數。您可以利用它來確定本機資料集已過期，而傳入的同步是新的。

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

若要取得應用程式的裝置權杖，請依照有關「註冊遠端通知」的 Apple 說明文件來操作。從 APN 收到 NSData 物件形式的裝置權杖之後，您需要使用同步用戶端的 `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;
      }
    ];
```

在偵錯模式下，您的裝置會向 APN 沙盒註冊；在版本模式下，則是向 APN 註冊。若要從特定資料集接收更新，請使用 `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 說明文件來操作。從 APN 收到 NSData 物件形式的裝置權杖之後，您需要使用同步用戶端的 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
})
```

在偵錯模式下，您的裝置會向 APN 沙盒註冊；在版本模式下，則是向 APN 註冊。若要從特定資料集接收更新，請使用 `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`：遠端資料集的同步計數。您可以利用它來確定本機資料集已過期，而傳入的同步是新的。