【Swift 学习笔记】iCloud:Key-Value Storage

想让自己的程序支持iCloud。搜索发现OC的教程比较多。照猫画虎,翻译+整理一个swift版本的。 参考:http://code.tutsplus.com/tutorials/working-with-icloud-key-value-storage--pre-37542

开始之前

需要一个付费的iOS 开发者账号。 至少要2台iOS设备才可以测试数据同步功能。(iOS Simulator无法做iCloud Storage的测试)

如果你手头没有iOS开发者账号,或者没有2台iOS设备。这篇文章还是可以让你了解如何配置iCloud的,如何使用iCloud让你的程序变得更好。

Step 1:新建一个项目

首先需要对本地Xcode中的项目进行配置。

1.png

用创建一个新的项目。 template: Single View Application project
name: iCloudKeyValue
Language: Swift

Step 2: 配置iCloud

在Xcode 6中,配置iCloud比较简单。

在Project Navigator 中选择项目。在Capabilities中将iCloud的switch开关设置为On。

3.png

Xcode会自动帮你 创建App ID,将Entitlements权限文件添加到项目中

5.png

  • 进入开发者中心
  • 点击右上角的“Certificates, Identifiers & Profiles”
  • 在新的页面中,选择“Provisioning Profiles”
  • 在新页面中选择App IDs。可以看到,通过刚才的步骤a,Xcode已经自动创建了App ID: brincell.iCloudKeyValue
  • 点击这个App ID,展开。你会发现iCloud 服务处于Configurable状态。

appIDConfiguarbel.png

(以Key-Value Storage来使用iCloud,不需要iCloud Container文件。所以这里是需要配置的状态。)

Step 3: demo 程序

demo 用的是Apple官方教程YourFirstApp
稍加修改,改为iOS App。

Screen Shot 2015-01-07 at 11.33.32 AM.png

class Track: NSObject {
  var volume : Float = 0
}

class ViewController: UIViewController {
  let track = Track()


  @IBOutlet weak var textField: UITextField!
  @IBOutlet weak var slider: UISlider!

  @IBAction func mute(sender: AnyObject) {
    track.volume = 0.0
    updateUserInterface()
    println("click mute button")
  }

  @IBAction func textField(sender: AnyObject) {
    var newValue = slider.value
    track.volume = newValue
    updateUserInterface()
    println("\(slider.value)")
  }

  func updateUserInterface() {
    var volume = track.volume
    self.textField.text = "\(volume)"
    self.slider.value = volume
  }
}

运行程序。测试slider和Mute按钮能否正常工作。拖动slider,textField显示对应的值。

Step 4. 保存数据和加载数据

现在在手机上退出重启程序,数据是无法保存的。 我们这里用user defaults database 来保存数据。

添加saveVolume() loadVolume() 两个方法。

func saveVolume() {
    let ud = NSUserDefaults.standardUserDefaults()
    let trackVolume = track.volume
    ud.setValue(trackVolume, forKey: "Track")
}

func loadVolume(){
    let ud = NSUserDefaults.standardUserDefaults()
    if ud.valueForKey("Track") != nil {
        track.volume = ud.valueForKey("Track") as Float
    } else {
        track.volume = 5
    }
}

在viewDidLoad() 中调用loadVolume()。在Mute和Slider的action中调用saveVolume()方法。

class ViewController: UIViewController {
  let track = Track()


  @IBOutlet weak var textField: UITextField!
  @IBOutlet weak var slider: UISlider!

  @IBAction func mute(sender: AnyObject) {
    track.volume = 0.0
    updateUserInterface()
    saveVolume()
    println("click mute button")
  }

  @IBAction func textField(sender: AnyObject) {
    var newValue = slider.value
    track.volume = newValue
    updateUserInterface()
    saveVolume()
    println("\(slider.value)")
  }

  func updateUserInterface() {
    var volume = track.volume
    self.textField.text = "\(volume)"
    self.slider.value = volume
  }

  override func viewDidLoad() {
    super.viewDidLoad()
    loadVolume()
    updateUserInterface()

  }

    func saveVolume() {
        let ud = NSUserDefaults.standardUserDefaults()
        let trackVolume = track.volume
        ud.setValue(trackVolume, forKey: "Track")      
    }

    func loadVolume(){
        let ud = NSUserDefaults.standardUserDefaults()
        if ud.valueForKey("Track") != nil {
            track.volume = ud.valueForKey("Track") as Float
        } else {
            track.volume = 5
        }
    }
}

运行程序。双击Home,在程序列表中上划完全退出demo。重新启动demo看是否能够成功保存数据。

Step 5. iCloud 同步数据

为了让demo程序更加好用,我们用iCloud来同步设备间的数据。这个过程非常的简单。 iCloud's Key-Value Storage 和 NSUserDefaults 用法非常的像,也是存储key-value键值对。iCloud对应的类叫做 NSUbiquitousKeyValueStore。

我们先来实现iCloud的存储。 在saveVolume() 方法中,仿造NSUserDefaults写NSUbiquitousKeyValueStore方法。(注:NSUserDefaults可以用setValue方法来存储。NSUbiquitousKeyValueStore用这个方法程序缺一直crash。不知道是swift的坑还是什么。这里先转换成double值,用setDouble试一下。)

func saveVolume() {
    let ud = NSUserDefaults.standardUserDefaults()
    let trackVolume = track.volume
    ud.setValue(trackVolume, forKey: "Track")

    // Save to iCloud
    let store = NSUbiquitousKeyValueStore.defaultStore()
    let trackVolumeDouble = Double(trackVolume)
    store.setDouble(trackVolumeDouble, forKey: "Track")
    store.synchronize()
    println("Saving to iCloud")

}

那么我们的demo程序是如何知道其他设备中的程序已经改变了iCloud 中的Key-Value,把改动同步到当前设备的呢? 每当 Key-Value 被改变,程序会发送一个notification出来,我们的程序收到这个notification,把数据覆盖到本地就可以了。

在 viewDidLoad() 中做4件事。 1. 首先得到Key-Value Store的引用。
2. 把我们的View Controller当做一个监听器。监听NSUbiquitousKeyValueStoreDidChangeExternallyNotification
当接受到这个notification的时候,用updateKeyValuePairs: 方法来处理它。 3. 给store发送一个synchronize消息。
4. 刷新数据

    override func viewDidLoad() {
    super.viewDidLoad()

    let store = NSUbiquitousKeyValueStore.defaultStore()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("updateKeyValuePairs:"), name: NSUbiquitousKeyValueStoreDidChangeExternallyNotification, object: store)
    store.synchronize()
    loadVolume()
    updateUserInterface()

   }

   func updateKeyValuePairs(notification: NSNotification) {
    println("updateKeyValuePairs")
    let userInfo = notification.userInfo
    let changeReason: AnyObject? = userInfo?["NSUbiquitousKeyValueStoreChangeReasonKey"]
    var reason = -1


if (changeReason == nil) {
      return
    } else {
      reason = changeReason as Int
      println("reason is: \(reason)")
    }

    if (reason == NSUbiquitousKeyValueStoreServerChange) || (reason == NSUbiquitousKeyValueStoreInitialSyncChange) {
      let changeKeys = userInfo?["NSUbiquitousKeyValueStoreChangedKeysKey"] as NSArray
      let store = NSUbiquitousKeyValueStore.defaultStore()

      for key in changeKeys {
        if key.isEqualToString("Track") {
          // Update Data Source
          let trackVolumeFromStore = store.doubleForKey("Track") as Double
          println("track volume from store: \(trackVolumeFromStore)")
          // Save Local Copy
          track.volume = Float(trackVolumeFromStore)
          let ud = NSUserDefaults.standardUserDefaults()
          ud.setValue(trackVolumeFromStore, forKey: "Track")

          updateUserInterface()
        }
      }
    }
  }

在两台设备中运行程序。改变一台设备的数据。观察另一台设备的打印日志。可以看到它接受到通知,调用了 updateKeyValuePairs:

GitHub:

https://github.com/vivijie/iCloudTest

参考文献:

http://code.tutsplus.com/tutorials/working-with-icloud-key-value-storage--pre-37542

https://developer.apple.com/library/ios/documentation/General/Conceptual/iCloudDesignGuide/Chapters/Introduction.html#//apple_ref/doc/uid/TP40012094

Neil Wang

产品经理。喜欢摄影,滑板,电影。微信公众号:NeilOnly