GameCircle iOS Configuration

Overview

This document is for iOS developers who want to integrate GameCircle leaderboards, achievements, and Whispersync for Games in their games.

GameCircle API 2.1 and above support iOS 5.0 and above. Supported devices include iPhone (3GS and above), iPod Touch (3rd generation and above), and iPad (original and above).

The following sections will guide you through creating a GameCircle configuration, initializing the SDK, and integrating features. You must set up your GameCircle configuration, set up your game project, and initialize the GameCircle SDK (Parts 1, 2, and 3 below) before integrating GameCircle features.

Part 1: Setting Up Your GameCircle Configuration

Before continuing, confirm that you have an account on the Amazon Mobile App Distribution Portal. If you do not have an account, create one and then return here to set up your GameCircle configuration.

Step 1: Create a Security Profile and Link it to Your Game Configuration

A security profile is a shared group that enables each unique binary of a single application (for example, the HD, SD, free, and paid versions of a game) to have a unique API key that will identify the binary to Amazon. A security profile is linked to a GameCircle configuration, which enables you to share leaderboard and achievement data across every app that uses the profile, such as the SD and HD versions of a game.

The steps required to create a security profile and link it to your game configuration depend on your prior activity, if any, in the Amazon Mobile App Distribution Portal. Follow the steps for the case below that describes your situation.

Case 1: I have one or more games in the Amazon Mobile App Distribution Portal

On the Amazon Mobile App Distribution Portal, sign in and click the Apps & Services tab, and then click the GameCircle tab to open your Dashboard. If you already have GameCircle-enabled games, the Dashboard displays their GameCircle configurations, which are linked to, but separate from, their security profiles. If you see a GameCircle configuration, continue to Step 2: Create an API Key.

Note: You must be signed in in order to use the Apps & Services tab.

Case 2: I have never created a security profile, or I have no security profile for a specific game (although I may have other security profiles)

  1. On the Amazon Mobile App Distribution Portal, click the Apps & Services tab, and then click the GameCircle tab . Finally, click Create a Security Profile.

    Note: You must be signed in in order to use the Apps & Services tab.

  2. Create a Security Profile

    Illustration 1: Amazon Mobile App Distribution Portal

  3. On the Security Profile Management page, enter a Security Profile Name and Security Profile Description. For Security Profile Name, enter the common title of your game (for example, enter Star Game rather than Star Game HD or Star Game Free). The purpose of the Security Profile Description field is to help you organize your security profiles. The description field is optional; you may leave it blank.
  4. Click Save.
  5. Click the GameCircle tab, which now displays the GameCircle configuration management page.
  6. Configurations management page

    Illustration 2: GameCircle configuration management page

  7. In the Game Configuration area, in the Select a Security Profile list, select the security profile that you just created and then click Confirm. This will create a new GameCircle configuration linked to this security profile.

When you are ready, continue to Step 2: Create an API Key.

Case 3: I have a security profile for a game, but no GameCircle configuration

  1. On the Amazon Mobile App Distribution Portal, sign in and click the Apps & Services tab, and then click the GameCircle tab to open your Dashboard.

    Note: You must be signed in in order to use the Apps & Services tab.

  2. Your existing security profile will appear in the Select a Security Profile list.
  3. Select the existing security profile to create a GameCircle configuration based on it.

When the configuration is complete, continue to Step 2: Create an API Key.

Note: If you wish to create a new security profile, select Create New in the list to open the security profile management page and create a new security profile.

Step 2: Create an API Key

To obtain an API key for GameCircle, you will need to provide the bundle seed identifier from your application. Your API key is an alphanumeric string that acts as both a unique identifier and a secret token for GameCircle authentication. For more information about the bundle seed identifier, see Code Signing.

  1. In the Amazon Mobile App Distribution Portal, sign in and click the Apps & Services tab.
  2. On the Security Profile Management page, select the security profile for which you want to create an API key.
  3. Select the API Keys link, and then select the iOS tab.

    info.plist file

  4. Illustration 3: iOS and Kindle/Android tabs on Security Profile Management page

  5. Click Create a new API Key.
  6. Enter an API Key Name and your Bundle ID, and then click Generate New Key.
    iOS Generate New Key button
    Illustration 4: Create a New API Key window

Option: Share a GameCircle configuration across multiple binaries

You can create multiple API keys within a single security profile, which enables you to share a GameCircle configuration and the leaderboards and achievements associated with the security profile across multiple versions of your game.

For example, we have a game named Star Game to which we want to add achievements and leaderboards. The game has two versions, Star Game HD and Star Game Free, and we want them to share the same leaderboards and achievements. We create a security profile called Star Game and then create two API Keys, one for Star Game HD and one for Star Game Free, and embed them in the respective binaries. Finally, we link a game configuration to the Star Game security profile, and can now create leaderboards and achievements that will span both games.


Part 2: Setting Up Your Game Project

Follow the steps in this section to download the Amazon Mobile App SDK, add it to your project, and add property values to your game's info.plist.

Step 1: Download the SDK

Download the Amazon Mobile App iOS SDK from this page. The SDK is 10 MB, but takes up only 1 MB when compiled.

Step 2: Add the GameCircle SDK to Your Project

Locate the GameCircle.bundle and GameCircle.framework folders in the iOS/GameCircle directory of your zip folder, and then drag the two folders into your project. You should also drag the AmazonInsightsSDK.framework folder into your framework; the Insights SDK is used to communicate with Amazon services.

GameCircle is also dependent on multiple system-provided frameworks. Add these frameworks to your project from the Target Properties Summary tab, under Linked Frameworks and Libraries:

  • AdSupport.framework
  • GameKit.framework
  • libsqlite3.dylib
  • libc++.dylib
  • libz.1.2.5.dylib
  • MessageUI.framework
  • Security.framework
  • SystemConfiguration.framework
  • CoreTelephony.framework

Step 3: Update info.plist

To use GameCircle you will need to add property values to your project's info.plist file, as shown here.

info.plist file

Illustration 5: Project info.plist

In the Information Property List section, add your API key:

Name of Key

Type

Value

APIKey

String

The Amazon API key that you obtained in the "Setting Up GameCircle" section of this document.



In the URL types section, add a new Item and then add the following property:

Name of Key

Type

Value

URL identifier

String

your_bundle_id (Note that you must insert your_bundle_id in the value)



Under URL types/URL Schemes, add a new Item and then add the following property:

Name of Key

Type

Value

URL Scheme

String

amzn-your_bundle_id (Note that you must insert your_bundle_id in the value)



When Compiling for iOS 7, Hide the Status Bar

When compiling for iOS 7, you must compile your game to hide the device status bar. Failing to hide the status bar will result in the status bar overlapping the GameCircle overlay. This issue affects only developers who compile for iOS 7; no other versions of the operating system require hiding the status bar.

Hide the status bar by using one of these two methods:

For more information about info.plist, see About Information Property List Files.


Part 3: Initializing the GameCircle SDK

Follow the steps in this section to initialize the GameCircle SDK. This section also includes information about implementing App Delegate's Open URL to enable the GameCircle user experience on devices.

Before GameCircle can be accessed, it must be initialized. Initialization should occur when your application begins. The following example shows GameCircle being initialized by using the UIApplicationDelegate protocol didFinishLaunchingWithOptions.

GameCircleBeginWithCompletionHandler

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
      [GameCircle beginWithFeatures:@[AGFeatureAchievements, AGFeatureLeaderboards, AGFeatureWhispersync] completionHandler:^(NSError *error) {
         NSLog(@"Hello, GameCircle!"); //Do not use GameCircle SDK until this completion handler is executed
     }];
}

Note: The features array should contain the GameCircle features (as defined by the AGFeature* strings in GameCircle.h) that the game uses.

Implement App Delegate's Open URL

Add the following code to your application to enable the GameCircle sign-in experience, which allows customers to sign in by using Login with Amazon.

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    return [GameCircle handleOpenURL:url
                   sourceApplication:sourceApplication];
}

For help with UIApplicationDelegate questions, see UIApplicationDelegate Protocol Reference.

Enable Cross-Posting to Game Center Leaderboards and Achievements

When you enable cross-posting to Game Center, GameCircle achievement and leaderboard updates are also submitted to the game's equivalent Game Center achievement or leaderboard.

If you choose to enable this service, you must register your GameCircle achievements and leaderboards on iTunes Connect.

* \arg achievementIDMappings A dictionary of Game Center achievement IDs to GameCircle achievement IDs.
*                            Pass nil if mapping is one-to-one
* \arg leaderboardIDMappings A dictionary of Game Center leaderboard IDs to GameCircle leaderboard IDs.
*                            Pass nil if mapping is one-to-one
* \arg completionHandler Used to propogate back error if any during sign in
*/
+ (void) enableGameCenterWithAchievementIDMappings:(NSDictionary*)achievementIDMappings leaderboardIDMappings:(NSDictionary*)leaderboardIDMappings
                                 completionHandler:(void (^)(NSError* error)) completionHandler; 

Implement the API as follows. There are no restrictions on when you can make this call:

	    // Enable Game Center with same leaderboard and achievement IDs
    [GameCircle enableGameCenterWithAchievementIDMappings:nil leaderboardIDMappings:nil completionHandler:^(NSError* error) {
        if (error)
        {
            // Handle error during sign in to Game Center
        }
        else
        {
            // Successfully signed in to Game Center
        }
    }]; 

Part 4: Using Whispersync for Games

Follow the steps in this section to integrate Whispersync for Games in your game. Whispersync enables you to get and set values in a data map. It is the first solution to offer both auto-conflict resolution and player-choice conflict resolution as options, and it queues when a device is offline. You can set up the data map in just a few minutes.

This section begins with How Whispersync for Games Works and other information that you need to know in order to use the Whispersync service in your game. Implementation steps with code samples follow in these sections:

How Whispersync for Games Works

Prior to Whispersync for Games, many developers implemented separate methods to store game data to disk and to cloud. Whispersync replaces your local storage solution and provides the added benefit of background synchronization with the cloud and with Android and iOS devices.

The GameDataMap interface enables you to get and set numbers and strings, and to organize them in lists and maps. GameDataMap is a first-class citizen that you can treat as a variable, pass into a method, or return from a method.

Here is how you get your game data:

AGGameDataMap* gameDataMap = [[AGWhispersync sharedInstance] gameData]; 

Note: The Whispersync client is available immediately after initializing Amazon GameCircle. Whispersync data is always accessible, even when the player is offline.

GameDataMap provides several ways to access your data. For example, to retrieve a player's highest score:

AGSyncableNumber* highScore = [gameDataMap highestNumberForKey:@"highScore"]; 

In this example, because you retrieve highScore as a highest number, it will always reflect the maximum value ever assigned to it, from any device.

To set the high score:

// Where 1000 represents a player's actual score, not a maximum
[highScore setValue:@(1000)];  

Conflict Resolution Options

Amazon offers two conflict resolution options to enable the best possible player experience for each game:

  • Auto resolution, in which your game automatically syncs the best score (highest or lowest, depending on the game), most recent achievements, and most recent purchases across all devices and to the Amazon cloud, without further action by either the player or the developer. To implement auto-conflict resolution, use any of the Whispersync data types described below except for DeveloperString, which is used exclusively for manual conflict resolution.
  • Manual resolution via DeveloperString, in which the developer manually performs the conflict resolution. You should first get and deserialize both locally and remotely stored strings, and then set specific values via game logic to determine the current game state. If there is no easy way to auto-resolve, you can optionally prompt the player for input.

    For implementation details of manual resolution via DeveloperString, see see Example 4 below.

    IMPORTANT: Amazon recommends that you exercise caution with manual resolution because your game logic may differ from your players’ expectations. For example, if a player buys an item for 100 Coins on offline Device A, and later buys a different item with the same 100 Coins on offline Device B, the player may want to keep the item from Device A, which represents the older data. In this case, it may be best to involve the player via a popup to let them decide which item to keep. Alternatively, you may choose to model the above situation with auto-resolvable types such as AGSyncableAccumulatingNumber and AGSyncableStringSet.

When Syncing Occurs

The GameCircle SDK automatically synchronizes Whispersync data with the cloud. Whispersync enables syncing based on two event types:

  • Active sync events occur when you call the synchronize method, and when a customer signs in to your game. However, syncing occurs even if you never call the synchronize method, due to passive sync events.
  • Passive sync events occur any time that you choose to change game data, such as by setting a high score or accumulating number. Passive sync events are batched, rather than synced immediately. Whispersync throttles passive sync events to conserve network bandwidth and battery life.
  • Regardless of throttling, local storage is always synced. Whispersync stores local game data in your application's storage space in an obfuscated text file.
  • Throttling does not cause data to be lost; the customer's data is synced on the next active or passive sync attempt.
  • When throttling occurs, a notification occurs via the throttled message. These are examples:
    // We post notifications to NSNotificationCenter with the following names
    NSString* const AGWhispersyncNotificationNewDataFromCloud;
    NSString* const AGWhispersyncNotificationDataUploadedToCloud;
    NSString* const AGWhispersyncNotificationThrottled;
    NSString* const AGWhispersyncNotificationDiskWriteComplete;
    NSString* const AGWhispersyncNotificationFirstSync;  
    
    // How to listen to a notification
    // Call to start listening to notification: AGWhispersyncNotificationNewDataFromCloud
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateGameInfo:) name:AGWhispersyncNotificationNewDataFromCloud object:nil];
    - (void) updateGameInfo:(NSNotification*)notification
    {
    	// Game logic
    }

Forcing a Sync

Use this code to force a sync:

[AGWhispersync synchronize];  

Offline Scenarios

Whispersync easily manages offline scenarios involving mergeable data types. For example, say that a player starts a game online and unlocks six levels, each with a rating of one star. The player then might go offline with another device and play the first three levels again, and earn three stars on each level. When the offline device goes back online and syncs, both devices will then show three stars on levels 1 - 3 and one star on levels 4 - 6.

Syncable Data Types

The table below shows the data types that you can sync, the GameDataMap Selectors for each data type, and sample use cases.

Table 1: Syncable Data Types

Whispersync Data Type

Data Type Represents

GameDataMap Selectors

Sample Use Case

AGSyncableNumber

A highest, lowest or most recent number.

highestNumberForKey

Stars earned on level 7

lowestNumberForKey

Fastest lap time

latestNumberForKey

Most recent lap time

AGSyncableNumberList

A list of numbers in ascending, descending, or latest order.

highNumberListForKey

List of N best game scores

lowNumberListForKey

List of N fastest lap times

latestNumberListForKey

List of N most recent scores

AGSyncableAccumulatingNumber

A number that can be incremented and decremented.

accumulatingNumberForKey

Total distance or
Remaining health potions

AGSyncableString

The most recent string.

latestStringForKey

Most recent car added to collection

AGSyncableStringList

A list of strings, ordered by most recent.

latestStringListForKey

Names of last N enemies encountered

AGSyncableStringSet

A set of strings.*

* Special Case This is an unbounded set of strings, to which items can only be added, as long as the set does not duplicate an existing string.

stringSetForKey

Levels completed for a non-linear game

AGGameDataMap

A nested map that enables hierarchical data.

mapForKey

A data map for each level in a game

Step 1. Set Up the Data You Want to Sync

Model your game data by using the basic types in the Syncable Data Types table. Add new accessors simply by retrieving them by name; if they don’t exist, they’ll be created on the fly and synchronized from that point on.

You can model both simple and complex game data by using Whispersync’s syncable data types, and, if necessary, by nesting data maps.

Start by setting the name and type of game data that you want to synchronize. It’s not strictly necessary to do this all in one place, or even before the variables are actually used. But having a single, authoritative definition can improve code clarity and make it easier to understand and maintain the structure of your game data.

Example 1: Maintaining a List of Spells Mastered

In the snippet below, for example, we can see at a glance what is stored and how the developer laid it out. The game maintains a list of all spells the player has ever mastered, as well as the last few power-ups collected. In addition, it will store data for each level, including the number of stars earned, fastest completion time, and total time played for each level.

public static final int NUM_LEVELS = 10;
public static final int MAX_POWERUPS = 5;
 
. . .
 
AGGameDataMap *gameDataMap;
 
void setupGameData() {
   // Save a reference to the root object for later use.
   gameDataMap =  [[AGWhispersync sharedInstance] gameData];
 
   // Create a sub-map for each level.
   AGGameDataMap *levelMaps[NUM_LEVELS];

   for(int i = 0; i < NUM_LEVELS; i++) {
      levelMaps[i] = [gameDataMap mapForKey:@"level"]; 
 
      // Track the highest score (stars) obtained for each level.
      AGSyncableNumber* starsEarned = [levelMaps[i] highestNumberForKey:@"starsEarned"];
 
      // Remember the fast time to completion.
      AGSyncableNumber* fastestTime = [levelMaps[i] lowestNumberForKey:@"fastestTime"];
 
      // Record total time playing each level.
      AGSyncableAccumulatingNumber* totalTime = [levelMaps[i] accumulatingNumberForKey:@"totalTime"];
   }
 
   // Maintain an inventory of spells mastered.  Spell names will be
   // added to this over time (and can never be removed).
   AGSyncableStringSet* spells = [gameDataMap stringSetForKey:@"spells"];
 
   // Establish the size of a FIFO list of power-ups.
   //This list will always hold the last MAX_POWERUPS power-ups.
   AGSyncableStringList* powerUps = [gameDataMap latestStringListForKey:@"powerUps"];
   [powerUps setCapacity:MAX_POWERUPS];
}  

Example 2: Tracking a Player’s Experience Level

Here’s a second example employing getGameData(). The snippet looks up a player’s experience level and updates the level if certain criteria are met. In this case, any player who has performed magic is automatically elevated to magician’s apprentice.

// Get the global game data map for the current player.
AGGameDataMap* gameDataMap = [[AGWhispersync sharedInstance] gameData];
 
// Look up the object representing the player’s experience level.
// If none exists, one will be created.
AGSyncableNumber* experience = [gameDataMap highestNumberForKey:@"experience"];
 
// If the player has ever used a magic spell, they’re at least a
// magician’s apprentice.
if (hasUsedMagic() && MAGICIANS_APPRENTICE > [[experience value] intValue]) {
  // As long as this device is online (or as soon as it is),
  // local and cloud storage will be synced.
  [experience setValue:@(MAGICIANS_APPRENTICE)];
}  

Example 3: Creating Hierarchical data

This example illustrates how to use embedded maps to create a hierarchical data structure. In this case, the top-level GameDataMap singleton contains a child GameDataMap object for each level in the game.

This method also shows how to look up the syncable object accessors corresponding to a particular type. You can use this approach to iterate over existing objects, rather than simply passing a string to one of the GameDataMap accessors, which might have the undesirable side effect of instantiating a new syncable element.

AGGameDataMap* getLevelData(NSString name) {
   AGGameDataMap *gdm = [[AGWhispersync sharedInstance] gameData];
 
   // Get all the string keys associated with GameDataMap objects.
   NSArray* maps = [gdm allMapKeys];

   // Look for a match among the maps.
   for (NSString* s in maps) {
      if ([s isEqualToString:name) {
         // A map exists for the name specified.
         return [gdm mapForKeY: s];
      }
   }
 
   // No match found. Don't create one.
   return null;
}  

Example 4: Manually Resolve Conflicts Via DeveloperString

To implement manual conflict resolution in your game, write code modeled on these samples:

# import < GameCircleAGWhispersync.h>
- (void) initGameCircle
{
    NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
    [notificationCenter addObserver:self 
                           selector:@selector(handlePotentialGameDataConflicts:) 
                               name:AGWhispersyncNotificationNewDataFromCloud
                             object:nil];
    [notificationCenter addObserver:self 
                           selector:@selector(handlePotentialGameDataConflicts:) 
                               name:AGWhispersyncNotificationDataUploadedToCloud
                             object:nil];      
}

- (void) handlePotentialGameDataConflicts
{
    AGGameDataMap* gameDataMap = [[AGWhispersync sharedInstance] gameData];
    SyncableDeveloperString developerString = [gameDataMap developerStringForKey:@"gameData"];

    if ([developerString inConflict])
    {
        NSString* localGameDataString = [developerString value];
        NSString* cloudGameDataString = [developerString cloudValue];

        // Deserialize the two values and resolve the conflicts.
        // Serialize the final result into a string, let's say that it's called resolvedString

        [developerString setValue:resolvedString];
        [developerString markAsResolved];
    }
}

- (void) dealloc
{
    NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
    [notificationCenter removeObserver:self 
                                  name:AGWhispersyncNotificationNewDataFromCloud
                                object:nil];      
    [notificationCenter removeObserver:self 
                                  name:AGWhispersyncNotificationDataUploadedToCloud
                                object:nil];
}  

Step 2. Add Accessor Names to Your Code

The following code snippets demonstrate how to add accessors (getters) to your code.

Table 2: Whispersync Code Snippets
Accessor Name Code Snippet
SyncableNumber
   HighestNumber






   Lowest Number






   LatestNumber

// Retrieve the highest number of stars earned on the current level.
AGSyncableNumber* starsEarned = [gameDataMap highestNumberForKey:@"starsEarned"];
NSLog(@"Stars earned on current level: %d", [[starsEarned value] intValue]); 
// Save the current lap time if it's the fastest one (lowest value).
AGSyncableNumber* fastestLapTime = [gameDataMap lowestNumberForKey:@"fastestLapTime"];
[fastestLapTime setValue:@(current_lap_time)];
NSLog(@"Fastest lap time so far: %f", [[fastestLapTime value] floatValue]);  
// Determine the id of the last obstacle blown up.
AGSyncableNumber* lastTargetId = [gameDataMap lastestNumberForKey:@"lastTargetId")];
NSLog(@"Id of last thing destroyed: %d", [[lastTargetId value] intValue]);  
SyncableNumberList
   HighNumberList












   LowNumberList











   LatestNumberList

// Set the number of high scores to preserve.
AGSyncableNumberList* highScoresList = [gameDataMap highNumberListForKey:@"highScoresList"];

[highScoresList setCapacity: MAX_HIGHSCORES];
. . .
 
// Retrieve the a list of the highest scores recorded.
int n = 0;
for(AGSyncableNumberElement* i in [highScores values]) {
  NSLog(@"Score #%d: %d", n++, [[i value] intValue]);
}  
// Set the number of fastest lap times to preserve.
SyncableNumberList fastestLapTimesList = gameDataMap.getLowNumberList("fastestLapTimesList");
fastestLapTimesList.setMaxSize(MAX_FASTESTLAPTIMES);
	
. . .
	
// Retrieve the list of fastest laps clocked so far.
AGSyncableNumberList* fastestLapTimesList = [gameDataMap lowNumberListForKey:@"fastestLapTimesList"];
[fastestLapTimesList setCapacity: MAX_FASTESTLAPTIMES];
 
. . .
 
// Retrieve the list of fastest laps clocked so far.
for(AGSyncableNumberElement* num in [fastestLapTimesList values]) {
  NSLog(@"Lap time: %f", [[num value] intValue]);
}  
// Get a list of the most recent items collected (defaults to length 3).
AGSyncableNumberList* items = [gameDataMap latestNumberListForKey:@"itemsList"];
for(int i = 0; i < [[items values] count]; i++) {
  NSLog(@"item[%d] = %d" , i, [[[items values] objectAtIndex:i] intValue]);
}
 
. . .
 
// Record an item whenever the player collects one.
[items addValue: collectedItem.getId()]; 
SyncableAccumulatingNumber
   AccumulatingNumber
// Add to the total time spent solving the current puzzle.
AGSyncableAccumulatingNumber totalTime = [gameDataMap accumulatingNumberForKey:@"totalTime"];
[totalTime incrementValue:getTimeElapsed()];

. . .
 
// Reduce a player's energy, clamping it to 0.0 if necessary.
AGSyncableAccumulatingNumber energy = [gameDataMap accumulatingNumberForKey:@"energy"];
[energy decrementValue:getPowerDrain()];

if(0.0 > [[energy value] doubleValue])
[energy incrementValue:[[energy value] doubleValue]); 
SyncableString
   String
// Retrieve the value of the current level.
AGSyncableString* curLevelName = [gameDataMap latestStringForKey: @"curLevelName"];
NSLog(@"The current level is called %@", curLevelName);	
SyncableStringList
   StringList
// Set the number of phrases an NPC parrot will remember.
AGSyncableStringList* phrasesList = [gameDataMap latestStringListForKey:@"phrasesList"];
[phrasesList setCapacity:MAX_PHRASES];
 
. . .
 
// Display the last few phrases the player has taught his parrot.
int i = 0;
for(AGSyncableString* s in [phrasesList values]) {
NSLog(@"phrase[%d] = %@", i++, [s value]);
}
 
. . .
 
// Teach the player's parrot some new phrases.
[phrasesList addValue:@"Ahoy, matey"];
[phrasesList addValue:@"Avast!"]; 
SyncableStringSet
   StringSet
// Display all enemies encountered in the game so far.
AGSyncableStringSet* allEnemies = [gameDataMap stringSetForKey:@"allEnemiesSet"];
 
for(AGSyncableStringElement sse : [allEnemies values]) {
  NSLog(@"Encountered enemy: %@", [sse value]);
}
GameDataMap
// Nested maps can be used just like the root map
AGGameDataMap* antarctica = [gameDataMap mapForKey:@"Antarctica"];
[[antarctica highestNumberForKey:@"highScore"] setValue:[self score]];

Step 3. Test Your Whispersync Implementation

You can test your Whispersync for Games implementation by using a single test device. Whispersync doesn’t require nickname whitelisting. If the game is registered, Whispersync will work.

Note: There is no mechanism for clearing Whispersync test data. Amazon recommends that you test Whispersync by using accounts that you’ve created just for testing.

Follow these steps to test Whispersync with a single test device:

  1. Sign into your game and play until you hit a milestone that you have set as a syncable event, such as reaching a new level.
  2. Save and close the game.
  3. Remove the game from the device.
  4. Re-provision the game back onto the device.
  5. Sign into the game and observe whether the game presents the correct level.

    Note: If you have a second device to test with, follow steps 1 and 2 above, and sign into the same account on the second device and observe whether the game presents the correct level.

During QA, you may want to know when throttling is occurring. To make throttling more visible during testing, you can add an observer for the AGWhispersyncNotificationThrottled notification, as defined in AGWhispersync.

Non-Syncable Data

Some types of game state data can’t be merged automatically. For example, say that a player starts a chess match on an online device, and then continues playing the game on two offline devices. The player will almost certainly make different moves on each offline device. When the two offline devices later go online, the individual moves and final game piece positions can’t be reconciled automatically among the three devices. For this type of data, consider using SyncableDeveloperString to manually resolve conflicts. Possible resolution strategies include:

  • Ask the player to select either the local or the cloud value (this is the recommended approach).
  • Select a value programmatically. This approach may work only for games that have measurable progress, such as the number of pieces placed in a jigsaw puzzle.

Best Practices

  • The best customer experience is to enable your customers to sync consumables. If you worry that some customers will use in-game currency to make multiple offline purchases, add code to determine if a device is offline and then block offline purchases.
  • Don’t store personally identifiable information by using Whispersync for Games. Amazon may revoke your access to GameCircle if you use Whispersync to store any data other than game metadata.

Part 5: Using Achievements

Step 1: Set Up Achievements in the Amazon Mobile App Distribution Portal

To integrate GameCircle achievements in your game, follow Step 1 in Implementing Achievements to create draft achievements and publish them, and then return to this page to complete steps 2-5.

Note: On the Implementing Achievements page, follow Step 1 and review the Best Practices section only. Later steps on that page do not apply to iOS development.

Step 2: Send an Achievement Update

After initializing Amazon GameCircle in your game, you can submit achievement progress updates to the service.

[[AGAchievement achievementWithID:@"MY_ACHIEVEMENT_ID"]
    updateWithProgress:100.0f
    completionHandler:^(BOOL newlyUnlocked, NSError *error) {
        // executed upon submission
    }
];

The second input parameter to the updateProgress method is a floating-point value representing the player’s completion percentage for the achievement. The valid range of this parameter is 0.0 to 100.0, where 0.0 represents fully locked and 100.0 represents fully unlocked. Completion percentage can only increase. If the method submits a completion percentage value that is lower than the current value, the completion percentage shown for that achievement will not change.

Step 3: Add a Link to the Achievement Overlay in Your Game

To help customers find their achievements, make sure to add a link to the in-game overlay somewhere in your game.

[[AGOverlay sharedOverlay] showWithState:AGOverlayAchievements animated:YES];

Step 4: Test Achievements

To test your achievements, play your game on your test device and verify that you can unlock the achievements that you have set.

  1. Confirm that you have added test nicknames in the Amazon Mobile App Distribution Portal. If it is necessary, add one or more test nicknames. Instructions for adding test nicknames are here.
  2. On your test device, sign in to your game by using a test nickname.
    Optional: If you wish, sign in to your game by using more than one test nickname, and create score data for each player.
  3. Play your game so that you have score data, and verify that you can unlock the achievements that you have set.

Step 5: Publish Achievements

To make achievements available to your customers, you must publish them.

  1. Sign in to your account on the Amazon Mobile App Distribution portal, and then click the Apps & Services tab.
  2. Click the GameCircle tab.
  3. In the Achievements column, click the View link for a game that has draft achievements.
  4. In the Status column, select an achievement in Draft status. When the box is selected, the label will change to Ready to Publish. You can select as many draft achievements as you wish.
  5. At the bottom of the page, click Start Publishing. Confirm that you want to publish, and all selected achievements will be published.

Control Placement of Achievement Toasts

You can control the placement of achievement toasts in your game. Possible locations are TopLeft, TopRight, BottomLeft, BottomRight, TopCenter, and BottomCenter:

[GameCircle setToastLocation:AGToastLocationTopLeft];
[GameCircle setToastLocation:AGToastLocationTopRight];
[GameCircle setToastLocation:AGToastLocationBottomLeft];
[GameCircle setToastLocation:AGToastLocationBottomRight];
[GameCircle setToastLocation:AGToastLocationTopCenter];
[GameCircle setToastLocation:AGToastLocationBottomCenter]; 

Notes

  • Set achievement and leaderboard toasts to the same location. The default location is TopRight.

  • Welcome toasts are always BottomCenter.

Turn Off Toasts

To turn off all toasts in your game except for welcome toasts, execute the following:

[GameCircle setToastsEnabled:NO]; 

Part 6: Using Leaderboards

Step 1: Set Up Leaderboards in the Amazon Mobile App Distribution Portal

To integrate GameCircle leaderboards in your game, follow Step 1 in Implementing Leaderboards to create draft leaderboards and publish them, and then return to this page to complete steps 2-5.

Note: On the Implementing Leaderboards, page, follow Step 1 and the Best Practices section only. Later steps on that page do not apply to iOS developers.

Step 2: Send a Score to Your Leaderboard

After initializing Amazon GameCircle in your game and publishing leaderboards, you can submit leaderboard scores to the service. Scores must be positive numbers and updates must always be better than a player's current best score (which may be a higher or a lower score, depending on the game). The service will ignore a score update that is equal to or less than a player's current best score.

[[AGLeaderboard leaderboardWithID:@"MY_LEADERBOARD_ID"]
    submitWithScore:12345
    completionHandler:
    ^(NSDictionary *rank, NSDictionary *rankImproved, NSError *error) {
        //executes on submission success
    }
];  

Step 3: Add a Link to the Leaderboard Overlay in Your Game

To help customers find the leaderboard, make sure to add a link to the in-game overlay somewhere in your game.

[[AGOverlay sharedOverlay] showWithState:AGOverlayLeaderboards animated:YES]; 

Step 4: Test Leaderboards

To test your leaderboards, follow this procedure:

  1. Confirm that you have added test nicknames in the Amazon Mobile App Distribution Portal. If it is necessary, add one or more test nicknames. Instructions for adding test nicknames are here.
  2. On your test device, sign in to your game by using a test nickname.
    Optional: If you wish, sign in to your game by using more than one test nickname, and create score data for each player.
  3. Play your game so that you have score data.
  4. In the Amazon Mobile App Distribution Portal:

    1. Navigate to your game.
    2. Click the GameCircle tab.
    3. Click the Leaderboards tab to see your leaderboard data. Confirm that the correct information is displayed.

Step 5: Publish Leaderboards

To make leaderboards to your customers, you must publish them.

  1. Sign in to your account on the Amazon Mobile App Distribution portal, and then click the Apps &amp; Services tab.
  2. Click the GameCircle tab.
  3. In the Leaderboards column, click the View link for a game that has draft leaderboards.
  4. In the Status column, select a leaderboard in Draft status. When the box is selected, the label will change to Ready to Publish. You can select as many draft leaderboards as you wish.
  5. At the bottom of the page, click Start Publishing. Confirm that you want to publish, and all selected leaderboards will be published.

Get a Time-Filtered High Score

All Amazon GameCircle leaderboards come with three time filters by default: All-Time, Weekly, and Daily. All-Time leaderboards include the highest scores for each player since you created the leaderboard or manually reset the leaderboard. Weekly leaderboards are cleared every Saturday night at midnight Pacific Daylight Time (PDT). Amazon resets daily leaderboards every day at midnight PDT.

  AGLeaderboard* leaderboard = [AGLeaderboard leaderboardWithID:@"Leaderboard ID"];
    // Retrieving local weekly high score
    [leaderboard localPlayerScoreWithFilter:AGLeaderboardFilterGlobalWeek completionHandler:^(AGScore* score, NSError* error) {
        if (error)
        {
            NSLog(@"Unable to get high score with error: %@", [error description]);
            // Handle error
        }
        else
        {
            NSLog(@"High score: %lld, rank: %d on leaderboard with ID: %@", score.score, score.rank, score.leaderboardID);
            // Do work with weekly high score
        }
    }];  

Get a List of Leaderboards

The leaderboards client can generate a list of leaderboards in your game.

[AGLeaderboard allDescriptionsWithCompletionHandler:
    ^(NSArray *leaderboards, NSError *error) {
        //iterate through leaderboards
    }
}];

Control Placement of Leaderboard Toasts

You can control the placement of leaderboard toasts in your game. Possible locations are TopLeft, TopRight, BottomLeft, BottomRight, TopCenter, and BottomCenter:

[GameCircle setToastLocation:AGToastLocationTopLeft];
[GameCircle setToastLocation:AGToastLocationTopRight];
[GameCircle setToastLocation:AGToastLocationBottomLeft];
[GameCircle setToastLocation:AGToastLocationBottomRight];
[GameCircle setToastLocation:AGToastLocationTopCenter];
[GameCircle setToastLocation:AGToastLocationBottomCenter];  

Notes

  • Set achievement and leaderboard toasts to the same location. The default location is TopRight.

  • Welcome toasts are always BottomCenter.

Turn Off Toasts

To turn off all toasts in your game except for welcome toasts, execute the following:

[GameCircle setToastsEnabled:NO]; 

Return to the API overview

Unavailable During Maintenance