使用 Swift 实现 iCloud 键值存储

Implementing iCloud Key-Value Storage with Swift

我在应用中实现键值存储时遇到了问题。我所有的 iCloud 相关代码都在我的 ViewController 中,如下所示:

import UIKit
import CloudKit
import NotificationCenter

var purchased = [[Bool]]() {
    didSet {
        instance.saveToiCloud()
        print("SAVED")
    }
}
var rockAmount = 0 {
    didSet {
        instance.saveToiCloud()
        print("SAVED")
    }
}
var goldAmount = 0 {
    didSet {
        instance.saveToiCloud()
        print("SAVED")
    }
}
var adBlock = false {
    didSet {
        instance.saveToiCloud()
        print("SAVED")
    }
}

var instance = GameViewController()

class GameViewController: UIViewController {
var iCloudKeyStore: NSUbiquitousKeyValueStore?

var interstitial: GADInterstitial!
var rewardAd = GADRewardBasedVideoAd()
var instRequest = receivingInst.GameScene //Placeholder

var instCatch = false
var rewardCatch = false

override func viewDidLoad() {
    super.viewDidLoad()

    //Set up iCloud Key-Value Storage
    iCloudKeyStore = NSUbiquitousKeyValueStore()
    iCloudKeyStore?.synchronize()

    //Retrieve Assets from database
    retrieveAssets()

    NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.ubiquitousKeyValueStoreDidChange), name: NSUbiquitousKeyValueStore.didChangeExternallyNotification, object: iCloudKeyStore)

    instance = self

    if let view = self.view as! SKView? {
        // Load the SKScene from 'GameScene.sks'
        //if let scene = SKScene(fileNamed: "GameScene") {
        if let scene = SKScene(fileNamed: "LaunchScene") {
            // Set the scale mode to scale to fit the window
            scene.scaleMode = .aspectFill //.fill
            // Present the scene
            view.presentScene(scene)
        }

        view.ignoresSiblingOrder = true

        //view.showsFPS = true
        //view.showsPhysics = true
    }
}

func retrieveAssets() {
    //notFirst is to determine if it is the first ti
    let notFirst = UserDefaults.standard.bool(forKey: "notFirst")
    if !notFirst {
        //Create iCloud References
        var c = 0
        while c < 4 {
            var keyMake = ""
            switch c {
            case 0: keyMake = "purchasedCloud"
                saveToiCloudArray(purchased, keyMake)
            case 1: keyMake = "goldCloud"
                saveToiCloudInt(0, keyMake)
            case 2: keyMake = "rocksCloud"
                saveToiCloudInt(0, keyMake)
            case 3: keyMake = "adBlockCloud"
                saveToiCloudBool(false, keyMake)
            default: break
            }
            c += 1
        }

        print("SET")
    }

    //Retrieve iCloud Data
    getValuesFromiCloud()
}

func ubiquitousKeyValueStoreDidChange(notification: NSNotification) {
    let alert = UIAlertController(title: "Change detected",
                                  message: "iCloud key-value-store change detected",
                                  preferredStyle: UIAlertControllerStyle.alert)
    let cancelAction = UIAlertAction(title: "OK",
                                     style: .cancel, handler: nil)
    alert.addAction(cancelAction)
    self.present(alert, animated: true,
                 completion: nil)
    getValuesFromiCloud()
}

func iCloudGetInt(_ key: String) -> Int {
    var str = 0
    if let saved = iCloudKeyStore?.object(forKey: key) {
        print("B-INT\(saved)")
        str = saved as! Int
        print("A-INT\(saved)")
    } else {
        str = -10
        print("IntFailed")
    }
    return str
}

func iCloudGetArray(_ key: String) -> [[Bool]] {
    var str = [[Bool]]()
    if let saved = iCloudKeyStore?.array(forKey: key){
        print("B-AR\(saved)")
        str = saved as! [[Bool]]
        print("A-AR\(saved)")
    } else {
        str = [[]]
        print("ArFailed")
    }
    return str
}

func iCloudGetBool(_ key: String) -> Bool {
    var str = false
    if let saved = iCloudKeyStore?.bool(forKey: key){
        print("B-BOOL\(saved)")
        str = saved as! Bool
        print("A-BOOL\(saved)")
    } else {
        str = false
        print("BoolFailed")
    }
    return str
}

//Set if stored icloud value is greater than current (on button activation)
func saveToiCloudArray(_ txt: [[Bool]],_ key: String) {
    iCloudKeyStore?.set(txt, forKey: key)
    //iCloudKeyStore?.synchronize()
}

func saveToiCloudInt(_ txt: Int,_ key: String) {
    iCloudKeyStore?.set(txt, forKey: key)
    //iCloudKeyStore?.synchronize()
}

func saveToiCloudBool(_ txt: Bool,_ key: String) {
    iCloudKeyStore?.set(txt, forKey: key)
    //iCloudKeyStore?.synchronize()
}

func getValuesFromiCloud() {
    var c = 0
    while c < 4 {
        var keyMake = ""
        switch c {

        case 0: keyMake = "purchasedCloud"
            let ar = iCloudGetArray(keyMake)
            if !ar.isEmpty {
                purchased = ar
                UserDefaults.standard.set(purchased, forKey: "purchased")
            } else {
                print("Error\(keyMake)")
            }
        case 1: keyMake = "goldCloud"
            let g = iCloudGetInt(keyMake)
            if g != -10 {
                goldAmount = g
                UserDefaults.standard.set(goldAmount, forKey: "goldAmount")
            } else {
                print("Error\(keyMake)")
            }
        case 2: keyMake = "rocksCloud"
            let r = iCloudGetInt(keyMake)
            if r != -10 {
                rockAmount = r
                UserDefaults.standard.set(rockAmount, forKey: "rockAmount")
            } else {
                print("Error\(keyMake)")
            }
        case 3: keyMake = "adBlockCloud"
            let r = iCloudGetBool(keyMake)
            adBlock = r
            UserDefaults.standard.set(adBlock, forKey: "adBlock")

        default: break
        }
        c += 1
    }
    print(purchased, rockAmount, goldAmount)
}

func saveToiCloud() {
    var c = 0
    while c < 4 {
        var keyMake = ""
        switch c {
        case 0: keyMake = "purchasedCloud"
            saveToiCloudArray(purchased, keyMake)
        case 1: keyMake = "goldCloud"
            saveToiCloudInt(goldAmount, keyMake)
        case 2: keyMake = "rocksCloud"
            saveToiCloudInt(rockAmount, keyMake)
        case 3: keyMake = "adBlockCloud"
            saveToiCloudBool(adBlock, keyMake)
        default: break
        }
        c += 1
    }
}

[我意识到代码没有优化,也不是很干净,但这是因为我一直在尝试很多不同的东西]

我已经在我的功能中启用了 iCloud,并像这样为 Provisioning Profile 做了所有事情(容器存在并且有效):

我想知道我的问题可能是以下两件事之一:

  1. 我的代码应该在 AppDelegate 中,而不是在 ViewController。

  1. 我需要在这里写一个权利(可以通过 .entitlements 文件访问):

突出显示的红色部分是我想更改的部分(iCloud Key-Value Store)。我是不是打算写我自己的东西而不是 $(TeamIdentifierPrefix)$(CFBundleIdentifier)?它说那是 com.apple.developer.ubiquity-kvstore-identifier 但这是什么意思,如果需要我应该如何填写它(例如什么是 ubiquity-kvstore-identifier)?

任何形式的帮助和指导都将不胜感激。谢谢。

我找到的解决方案是为 iCloud 使用 默认 容器而不是 specified 容器。我不确定两者之间的真正区别是什么,但更改为这个对我有用。

此外,我不确定这是否有助于解决存储 iCloud 键值数据的问题,但我将代码放入 AppDelegate 而不是 ViewController