【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中的项目进行配置。
用创建一个新的项目。
template: Single View Application project
name: iCloudKeyValue
Language: Swift
Step 2: 配置iCloud
在Xcode 6中,配置iCloud比较简单。
在Project Navigator 中选择项目。在Capabilities中将iCloud的switch开关设置为On。
Xcode会自动帮你 创建App ID,将Entitlements权限文件添加到项目中
- 进入开发者中心。
- 点击右上角的“Certificates, Identifiers & Profiles”
- 在新的页面中,选择“Provisioning Profiles”
- 在新页面中选择App IDs。可以看到,通过刚才的步骤a,Xcode已经自动创建了App ID: brincell.iCloudKeyValue
- 点击这个App ID,展开。你会发现iCloud 服务处于Configurable状态。
(以Key-Value Storage来使用iCloud,不需要iCloud Container文件。所以这里是需要配置的状态。)
Step 3: demo 程序
demo 用的是Apple官方教程YourFirstApp
稍加修改,改为iOS App。
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件事。
首先得到Key-Value Store的引用。
把我们的View Controller当做一个监听器。监听NSUbiquitousKeyValueStoreDidChangeExternallyNotification
当接受到这个notification的时候,用updateKeyValuePairs: 方法来处理它。给store发送一个synchronize消息。
-
刷新数据
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