Website Links

Thursday 3 December 2015

Basic CloudKit Setup

As with a lot of their recent products, Apple has made iCloud quite easy to setup. The following post will show you how to setup basic iCloud storage for an app:

Setting up

  1. Go to Xcode and go to the capabilities section under your app's settings. You should set it up to look like this (most of which will automatically be done by Apple when you turn CloudKit on):

  2. Go to your CloudKit dashboard and set up how you want your data to be stored by adding records. You can also add data to your public/private databases!


The Code

Surprisingly, there is also minimal code. Working with our previous records that we created in the CloudKit dashboard. This is the only code we require to fetch and update highscores (excuse the poor layout, I'm working with limited space):

 //  CloudKitManager.swift
 //  Angle Reader
 //
 //  Created by Chelsea Farley on 7/02/15.
 //  Copyright (c) 2015 Trip Wire. All rights reserved.
 //

 import CloudKit

 class CloudKitManager {
    
    var container : CKContainer!
    var isICloudAvailable : Bool = false
    var highscore : CKRecord!
    
    init() {
        // container which is used to access private 
        // and public databases
        container = CKContainer.defaultContainer()
    }
    
    func fetchRecord(mode: String) {
        
        if (self.isICloudAvailable) {
            let predicate = NSPredicate(format: "Mode = %@", mode)
            let query = CKQuery(recordType: "Highscores", 
                                predicate: predicate)
        
            // load the highscore record with the matching mode
            container.privateCloudDatabase.performQuery(query, 
                inZoneWithID: nil, completionHandler: 
            ({results, error in
            
                var length = results!.count
                if (error == nil && results!.count > 0) {
                    self.highscore = results[0] as! CKRecord
                } else {
                    // create a highscore record if none exists
                    var record = CKRecord(recordType: "Highscores")
                    record.setObject(mode as CKRecordValue, 
                        forKey: "Mode")
                    record.setObject(0 as CKRecordValue, 
                        forKey: "Highscore")
                    
                    self.container.privateCloudDatabase.saveRecord(
                        record, completionHandler:
                    ({result, error in
                        if (error == nil) {
                            self.highscore = result
                        }
                    }))
                }
            }))
        }
    }
    
    func updateHighscore(score: Int) -> Int {
        if (self.isICloudAvailable) {
            if (highscore != nil ) {
                var highscoreValue = highscore
                    .valueForKey("Highscore") as! Int
                
                if (highscoreValue < score) {
                    highscore.setObject(score as CKRecordValue, 
                        forKey: "Highscore")
                    self.container.privateCloudDatabase.saveRecord
                        (highscore, completionHandler: nil)
                }
                
                return highscoreValue
            }
        }
        
        return 0
    }
 }



The above code handles loading, creating and updating highscores. I have put this in its own class as I believe this avoids it complicating game logic. There were also some minor adjustments required in the GameViewController, I will only show the code I added:

 //  GameViewController.swift
 //  Angle Reader
 //
 //  Created by Chelsea Farley on 7/02/15.
 //  Copyright (c) 2015 Trip Wire. All rights reserved.
 //

 import UIKit
 import CloudKit
 import SpriteKit

 class GameViewController : UIViewController {
    
    var ckManager : CloudKitManager!
    
    override func viewDidLoad() {
        // Standard code represented by ...
        ...
        
        ckManager = CloudKitManager()
        
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: "applicationBecameActive:",
            name: UIApplicationDidBecomeActiveNotification,
            object: nil)
        
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: "applicationBecameInactive:",
            name: UIApplicationWillResignActiveNotification,
            object: nil)
    }
    
    func handleIdentityChanged(notification: NSNotification){
        
        let fileManager = NSFileManager()
        
        if let token = fileManager.ubiquityIdentityToken{
            ckManager.isICloudAvailable = true
        } else {
            ckManager.isICloudAvailable = false
        }
        
    }
    
    func applicationBecameActive(notification: NSNotification){
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: "handleIdentityChanged:",
            name: NSUbiquityIdentityDidChangeNotification,
            object: nil)
    }
    
    func applicationBecameInactive(notification: NSNotification){
        NSNotificationCenter.defaultCenter().removeObserver(self,
            name: NSUbiquityIdentityDidChangeNotification,
            object: nil)
    }
    
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        
        ckManager.container.accountStatusWithCompletionHandler{
            [weak self] (status: CKAccountStatus, error: NSError!) 
            in
            
            // Be careful, we might be on a different thread 
            // so make sure that your UI operations go 
            // on the main thread
            dispatch_async(dispatch_get_main_queue(), {
                
                if error == nil{
                    self!.ckManager.isICloudAvailable = 
                        status == .Available
                }
                
            })
            
        }
    }
    
 }



The above code is all you need to identify whether iCloud is currently available, and it updates every time you exit or enter the application.

No comments:

Post a Comment