iOS App與WatchKit Extension的數(shù)據(jù)通信
NSUserDefaults
NSUserDefaults是快速共享信息的途徑。它適合存儲各種快速訪問和計算的小型數(shù)據(jù),比如用戶名與檔案信息。如果希望使用UserDefaults,請用于靜態(tài)數(shù)據(jù)這樣用戶不必考慮數(shù)值的變化。
你需要設(shè)定App Group來讓設(shè)備通過共享容器來實現(xiàn)數(shù)據(jù)共享,確保手表擴(kuò)展和ios target都已如此設(shè)置?;旧暇褪轻槍蓚€設(shè)備創(chuàng)建一個統(tǒng)一的App Group標(biāo)識符。如果需要刪除它,可以以類似的方法進(jìn)行。
你可以通過之前創(chuàng)建的App Group名來使用defaults,基本上就是為特定的key鍵值設(shè)置對象。在iPhone上,用戶輸入了文本,保存,文本就存到了應(yīng)用共享的UserDefaults里。在Watch上,你可以從AppGroup得到defaults然后進(jìn)行手表顯示內(nèi)容的更新。
- // on the iPhone app
- letdefaults=NSUserDefaults(suiteName:"group.com.natashatherobot.userdefaults")
- letkey="userInput"
- overridefuncviewDidLoad(){
- super.viewDidLoad()
- textLabel.text=defaults?.stringForKey(key)??"Type Something..."
- }
- @IBActionfunconSaveTap(sender:AnyObject){
- letsharedText=textField.text
- textLabel.text=sharedText
- defaults?.setObject(sharedText,forKey:key)
- defaults?.synchronize()
- }
- // WatchKit
- classInterfaceController:WKInterfaceController{
- @IBOutletweakvartextLabel:WKInterfaceLabel!
- letdefaults=NSUserDefaults(suiteName:
- "group.com.natashatherobot.userdefaults")
- varuserInput:String?{
- defaults?.synchronize()
- returndefaults?.stringForKey("userInput")
- }
NSFileCoordinator
對更大型的數(shù)據(jù)來說,NSFileCoordinator是管理應(yīng)用和watch擴(kuò)展的共享空間里文件的方式之一。對于有限列表的內(nèi)容它很合適,同時也適用于圖像文件。
下面的例子是個簡單的代辦事項列表app,在手機(jī)上增加任務(wù)然后暑假傳輸?shù)絎atchKit擴(kuò)展并在手表上顯示。你的視圖控制器需要遵循NSFilePresenter協(xié)議,除了實現(xiàn)兩個必需方法,其它不是很關(guān)鍵。FilePresenter協(xié)議有一個item URL,就是填你的AppGroup標(biāo)識符的地方。通過URL,你在對應(yīng)目錄建立一個文件。有必要的話你也可以通過操作隊列來控制多線程訪問。
另外,presentedItemDidChange這個代理方法,在FilePresenter里通知你是否一個對象發(fā)生了改變,來讓你更新app數(shù)據(jù)而無需用戶手動刷新。
然而這里還是有個關(guān)于NSFileCoordinator與NSFilePresenter 的bug而不方便在擴(kuò)展里使用。具體可參見Natasha的網(wǎng)站。
在代辦事項數(shù)組里利用FileCoordinator寫入一個文件,可以通過讀寫文件以實現(xiàn)打包和解包事項的數(shù)據(jù)到事項數(shù)組,接下來可以依據(jù)文件里的事項數(shù)據(jù)計算生成表格。需要注意的是如果你設(shè)計了刪除功能,而watch擴(kuò)展和iPhone應(yīng)用都能修改文件,會遇到線程同步的麻煩。
- // iPhone app
- privatefuncsaveTodoItem(todoItem:String){
- // write item into the todo items array
- ifletpresentedItemURL=presentedItemURL{
- fileCoordinator.coordinateWritingItemAtURL(presentedItemURL,options:nil,error:nil)
- {[unownedself](newURL)->Voidin
- self.todoItems.insert(todoItem,atIndex:0)
- letdataToSave=NSKeyedArchiver.archivedDataWithRootObject(self.todoItems)
- letsuccess=dataToSave.writeToURL(newURL,atomically:true)
- }
- }
- }
- // in the Watch
- // MARK: Populate Table From File Coordinator
- privatefuncfetchTodoItems(){
- letfileCoordinator=NSFileCoordinator()
- ifletpresentedItemURL=presentedItemURL{
- fileCoordinator.coordinateReadingItemAtURL(presentedItemURL,options:nil,error:nil)
- {[unownedself](newURL)->Voidin
- ifletsavedData=NSData(contentsOfURL:newURL){
- self.todoItems=NSKeyedUnarchiver.unarchiveObjectWithData(savedData)as[String]
- self.populateTableWithTodoItems(self.todoItems)
- }
- }
- }
- }
Frameworks
“If the code appears more than once, it probably belongs in a framework.(如果代碼出現(xiàn)超過一次,應(yīng)該考慮能否放到框架里)”
-WWDC 2014, Building Modern Frameworks
框架對于業(yè)務(wù)邏輯、CoreData、可重用UI組件來說很棒。就像WWDC里說的那樣,你可以將重復(fù)代碼放到框架里。在FileCoordinator的例子里,我們獲取和讀寫文件的代碼出現(xiàn)了兩次,可以把它們提取到一個framework框架里。建立框架很簡單:建立新target,選擇Cocoa Touch framework,然后命名。它會在你的iOS應(yīng)用里自動鏈接,因此也不要忘了在WatchKit擴(kuò)展里進(jìn)行鏈接。
關(guān)鍵的一點,特別是對于Swift語言來說,應(yīng)該把框架認(rèn)作一個API。它需要聲明為公共的(public),因為這是iOS應(yīng)用和watchkit擴(kuò)展共用的接口。因此如果你在建立對象類,確保public關(guān)鍵字也加上了。這樣在手機(jī)和手表應(yīng)用里你導(dǎo)入了框架就可以訪問任何公共內(nèi)容。
- importWatchKit
- importMySharedDataLayer
- classInterfaceController:WKInterfaceController{
- @IBOutletweakvarfavoriteThingsLabel:WKInterfaceLabel!
- overridefuncawakeWithContext(context:AnyObject?){
- super.awakeWithContext(context)
- letmyFavoriteThings=MySharedData().myFavoriteThings
- letfavoriteThingsString=", ".join(myFavoriteThings)
- favoriteThingsLabel.setText("My favorite things are \(favoriteThingsString)")
- }
- }
Keychain Sharing
鑰匙鏈共享是針對更高安全性要求的數(shù)據(jù)的。UserDefaults提供的安全性不滿足時,你可以用鑰匙鏈共享來保障信息安全及跨擴(kuò)展的共享能力。
WatchKit目前的一個大問題是沒有認(rèn)證機(jī)制。蘋果提供了KeychainIteamWrapper的示例,但API太老不支持ARC。我推薦使用這個版本https://gist.github.com/dhoerl/1170641,它基于ARC并有清晰的接口。
根據(jù)問題是如何通過access group初始化KeychainItemWrapper。與AppGroup的概念類似,設(shè)備之間有共享空間。你在iOS和WatchKit擴(kuò)展中都需要鑰匙鏈來訪問用戶數(shù)據(jù)。通過鍵值存儲體系,你設(shè)定用戶名和密碼并用同一個標(biāo)識符建立同一類型的keychain項。這個例子里僅展示了當(dāng)用戶填好用戶名密碼時WatchKit擴(kuò)展展示數(shù)據(jù)的這一工作過程。
- // iPhone app
- @IBActionfunconSaveTap(sender:AnyObject){
- letusername=usernameTextField.text
- letpassword=passwordTextField.text
- letkeychainItem=KeychainItemWrapper(identifier:"SharingViaKeychain",accessGroup:"W6GNU64U6Q.com.natashatherobot.SharingViaKeychain")
- // WatchKit extension
- letkeychainItem=KeychainItemWrapper(identifier:"SharingViaKeychain",accessGroup:"W6GNU64U6Q.com.natashatherobot.SharingViaKeychain")
- letpasswordData=keychainItem.objectForKey(kSecValueData)asNSData
- letpassword=NSString(data:passwordData,encoding:NSUTF8StringEncoding)
- letusername=keychainItem.objectForKey(kSecAttrAccount)as?String