How to Save your Game’s Data: Part 2/2

In the second part of this tutorial, you will be adding your own anti-cheat system as well as saving your game’s data into iCloud. By Marin Todorov.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Enabling iCloud

Unless you’ve spent the last five years living in a cave on the moon, then you know “the cloud” is all the rage these days. When you have the cloud you can: do some work on your laptop, continue working on your iPhone in the subway and then pick up where you left on your iPad during your flight; it’s all connected as long as your iOS device is connected to iCloud.

With so many iOS devices in your life that are suitable for gaming (c’mon Apple we want a gaming Apple TV already!), you’ll definitely want the ability to pick up any of your devices and continue blasting asteroids to smithereens. You’ll also want to have your pilot photo and high scores synchronized across your iOS devices, right?

Luckily, enabling iCloud for your game is straightforward. Here’s how you set your game up to take advantage of the cloud

Select the SpaceShooter project file in the project navigator and select the SpaceShooter target. Click on the Capabilities tab.

sgd_19_icloud

Click the ON/OFF switch to toggle its state to ON.

The settings will change to look like the following image:

sgd_20_icloudON

Look at the project file list to make sure Xcode completed the listed steps. Look for a new entitlements file that enables iCloud capability for the game:

sgd_21_entitlements

So far, so good! The next step is to enable key-value storage for your game; you’ll use this kind of iCloud storage to persist the game data between synchronized devices. That’s technology at its best.

sgd_22_entitlementsON

Believe it or not, Apple’s iCloud service already backs up your game.

Time to put your head in the cloud. Literally

The next question is, “How do I get my handsome mug shot synced across all my devices?”

SGD_post_image

The logic behind storing the game data is a little bit more complicated. Consider the following:

  • The user might or might not have iCloud enabled on their devices. Therefore, local storage should be primary, with iCloud as an optional secondary storage.
  • The user might have played for a while, and only used disc storage. Then one sunny day they might decide to turn iCloud on, and the game will need to handle this situation properly, i.e. not delete the locally stored high score.
  • The user could delete the game completely and then re-install it. In this scenario you need to initialize the game data from iCloud the first time it launches.

So there’s a little more to it than just syncing to iCloud. Fortunately, for each of these scenarios there is a solution, and by the end of this tutorial you’ll know what to do to manage the most common.

In the beginning of this tutorial, you read that you’ll keep some game data in the cloud and some will save to the device. This means you’ll:

  • Check if the local high score is higher than the iCloud high score, and update the one in iCloud
  • Store the total distance flown locally, which effectively keeps track of the distance the player flies the ship on a particular device
  • Share the pilot photo in iCloud and monitor for changes across synced devices. This way if you take a new selfie on your iPhone, it will change on your iPad too

Here’s a recap of the final logic for storing the game data:

So far, you have all of these in working order, except for the high score and pilot photo.

Note: If you are interested in learning more about iCloud, check out these tutorials:
Beginning iCloud in iOS 5
and iCloud and UIDocument: Beyond the Basics. Also, you should check out our book, iOS 5 by Tutorials, which has been recently updated for Xcode 5 and iOS 7.

Updating iCloud

Open RWGameData.m and add this new method anywhere inside the class body:

-(void)updateiCloud
{
  NSUbiquitousKeyValueStore *iCloudStore = [NSUbiquitousKeyValueStore defaultStore];
  long cloudHighScore = [iCloudStore doubleForKey: SSGameDataHighScoreKey];
  
}

Since you’ll need to store just a couple of key-value pairs in your iCloud bucket, you’ll automatically work with the default storage. NSUbiquitousKeyValueStore is the class that provides you access to iCloud data. You can think of NSUbiquitousKeyValueStore as an NSDictionary in the cloud :]

After you get the default NSUbiquitousKeyValueStore, you use its doubleForKey:. It’s similar to what you would use with an NSCoder to get the stored high score value. Use the same constant for name of the key when archiving/unarchiving RWGameData. In the end, why not? The key name is just a string constant!

Now that you have the high score from iCloud, it’s time to compare it to the local data. Add these few lines:

if (self.highScore > cloudHighScore) {
  [iCloudStore setDouble:self.highScore forKey: SSGameDataHighScoreKey];
  [iCloudStore synchronize];
}

The code is simple enough. If the local high score is more impressive than the one on iCloud, then it’s time to update iCloud. setDouble:forKey: sets the value for the high score key and finally a call to synchronize fires up an iCloud sync.

Note: If you have ever used NSUserDefaults you probably already feel at home. The iCloud API and device key-value storage are similar by design.

Next, scroll to the method called save and add at the end:

if([NSUbiquitousKeyValueStore defaultStore]) {
  [self updateiCloud];
}

Checking if [NSUbiquitousKeyValueStore defaultStore] returns a class instance tells you whether iCloud is enabled or not. After the game data saves to the disc you check if iCloud is enabled, and if so, you call your shiny and new updateiCloud method.

That takes care of updating the high score to iCloud. Next, you’ll sync your photo.

Consider what you’re going to do for a moment. The logic for the high score is simple enough; compare two numbers and if the local one is bigger about the game updates the data on iCloud. Can you do the same for the photo? You certainly can’t compare photo sizes. What you could do is keep the last modified date of the photo separate and compare the last modification date of both photos. That could work, but there’s a better way.

You’re going to be ultra-smart about updating the photo to iCloud because you’ll implement a custom setter for the pilotPhoto.

Add this new method to RWGameData:

-(void)setPilotPhoto:(UIImage *)pilotPhoto
{
  //1
  _pilotPhoto = pilotPhoto;
    
  //2
  if([NSUbiquitousKeyValueStore defaultStore]) {
        
    //3
    NSUbiquitousKeyValueStore *iCloudStore = [NSUbiquitousKeyValueStore defaultStore];
        
    //4
    NSData* imageData = UIImagePNGRepresentation(pilotPhoto);
    [iCloudStore setObject:imageData forKey: SSGameDataPilotPhotoKey];
    [iCloudStore synchronize];
  }
}

Here’s what the code does:

  1. First, you store the new image in the backing instance variable _pilotPhoto.
  2. Then, just as before, you check whether iCloud is enabled by probing the default store.
  3. You fetch the default store in iCloudStore.
  4. Finally, you get the PNG data out of the photo and store it in your key-value iCloud storage. You wrap up with a call to storage’s synchronize method.

Now every time the player takes a new photo it will upload to iCloud. Nice.

Contributors

Over 300 content creators. Join our team.