Swift 程式語言

窺探 watchOS 2 中的 Watch Connectivity

窺探 watchOS 2 中的 Watch Connectivity
窺探 watchOS 2 中的 Watch Connectivity
In: Swift 程式語言, watchOS

開心之餘,這也代表著在手機與手錶之間的溝通方式有了改變,而這正是我們要為大家所介紹的新功能Watch Connectivity。

首先,什麼是Watch Connectivity? Watch Connectivity為 watchOS 2中的一個新framework,他提供了裝置之間多種不同的溝通方式,有別於上一篇我們利用資料共享App Groups的方式去做溝通,現在有了更快速且直接的方式來進行資料溝通。

在裝置溝通上,在過去 watchOS 中只有一種預設的方式openParentApplication,詳見官方文件

watchos-2

Watch Connectivity Session – WCSession

在我們使用 Watch Connectivity 之前,我們必須為 App 建立 Watch Connectivity Session,WCSession便提供了傳送、接收、監控狀態等函式供我們使用。

if WCSession.isSupported() {
     let session = WCSession.defaultSession()
     session.delegate = self 
     session.activateSession()
}

一開始,我們檢查這個iOS裝置,是否支援Session (Session在WatchOS上永遠有效),接著建立一個defaultSession(),再將其session委派,最後再啟動session便設置完畢,別忘了記得繼承WCSessionDelegateimport WatchConnectivity

不同的溝通方式

在Watch Connectivity中有兩類溝通方式,分別為:

  • 即時訊息 Interactive Messaging – 適合立即性的訊息及必須為可傳遞的狀態(reachable)
  • 背景傳輸 Background Transfers – 當傳送資料的App存在時,會持續傳輸。對應的App(另一端),不一定需要執行。 系統將會在適當的時機傳送內容。

即時訊息 Interactive Messaging

這種方式只能在兩個運行的App之間傳遞資料,且必須確認你的裝置為可傳遞的reachable的狀態,你可以透過下列方式確認是否為可傳遞狀態。

if WCSession.defaultSession().reachable {
    // 要做的事情
}

在確認完是否為可傳遞狀態後,Interactive Messaging提供了sendMessage()sendMessageData()兩種方法讓我們使用,取決於傳輸資料的型態是否為dictionary

let dictData = // 建立一個dictionary型態的資料
WCSession.defaultSession().sendMessage(dictData,
       replyHandler: { ([String : AnyObject]) -> Void in
          
       })
        errorHandler: { (NSError) -> Void in
          
});

let data = // 建立資料
WCSession.defaultSession().sendMessageData(data,
       replyHandler: { ([String : AnyObject]) -> Void in
          
       })
        errorHandler: { (NSError) -> Void in
          
});

而在資料接收端我們則是使用session:didReceiveMessage:session:didReceiveMessage:replyHandler:來處理。

func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void)
{
    
}

背景傳輸 Background Transfers

在背景傳輸中,有三種不同方式,取決於想要傳送的資料種類,我們將會依依做介紹。

1. Application Context
首先是 Application Context。這種傳輸方式會用最新取得的資料,將原本在傳輸佇列中的舊資料做覆蓋,以保證取得的資料為最新的內容。在 App 存在的情況下,Application Context更新之後,系統將在適當的時機將資料進行傳輸。(applicationContext的內容只能是dictionary)

do {
    let dictData = // 建立一個dictionary型態的資料
    try WCSession.defaultSession().updateApplicationContext(dictData)
} catch {
    
}

透過委派之後使用session:didReceiveApplicationContext:來取得我們所傳輸的資料。

func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
    
}

2. User Info Transfer
相對於Application Context,這種方式會將所有內容依先進先出(FIFO)順序排入佇列中,因此不會有任何資料被覆蓋掉。(user info的內容只能是dictionary)

let dictData = // 建立一個dictionary型態的資料
let dataTransfer = WCSession.defaultSession().transferUserInfo(dictData)

而搭配使用的接收資料方法為session:didReceiveUserInfo:

func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject])
{
    
}

3. File Transfer
檔案傳輸這是相當直覺的,我們可以透過這個方法來傳輸你想要傳的檔案。

let fileURL = // 檔案路徑
let dictData = // 建立一個dictionary型態的資料
let fileTransfer = WCSession.defaultSession().transferFile(fileURL, metadata:dictData)

接收檔案的方法為session:didReceiveFile:,也可以透過outstandingFileTransfers來取得傳輸的狀態(例如:沒有被取消、失敗、或是已經被對應的App接收)。

func session(session: WCSession, didReceiveFile file: WCSessionFile)
{
    
}

範例實作

接下來,這個範例我們將會透過Apple Watch的介面去移動iPhone中的綠色小方塊。

watch-connect-app-demo

註:本範例使用Xcode Version 7.1 (7B91b) 與watchOS 2。

你可以透過下載Starter Project來快速開始。下載完之後,打開Starter Project,你會發現我已為你建構好 Watch App 的介面並連結了IBAction

watch-app-interface

開啟InterfaceController.swift,在開首加入import WatchConnectivity和補上WCSessionDelegate

import WatchConnectivity

class InterfaceController: WKInterfaceController, WCSessionDelegate {

之後,我們宣告一個sessionWCSession.defaultSession(),並於willActivate()中建立先前提過的WCSession

let session = WCSession.defaultSession()

override func willActivate() {
    // This method is called when watch view controller is about to be visible to user
    super.willActivate()
    
    if WCSession.isSupported() {
        session.delegate = self
        session.activateSession()
    }
}

接著於buttonPressed()函式中實作我們所需要用到的sendMessage()用法,我們將四個按鈕加上了編號並且放入 data 這個dictionary傳出去。

func buttonPressed(num : Int) {
    print("You pressed \(num)")
    
    if WCSession.isSupported() {
        
        let data = ["buttonNum" : num]
        
        session.sendMessage(data, replyHandler: { (content:[String : AnyObject]) -> Void in
            print("Our counterpart sent something back.")
            }, errorHandler: {  (error ) -> Void in
                print(error.domain)
        })
    }
}

完成後,我們來實作接收端,也就是ViewController.swift,一樣記得import WatchConnectivity和補上WCSessionDelegate

import UIKit
import WatchConnectivity

class ViewController: UIViewController, WCSessionDelegate {

    var greenBox: UIView?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        self.greenBox = UIView()
        self.greenBox!.backgroundColor = .greenColor()
        self.greenBox!.frame = CGRectMake(CGRectGetMidX(self.view.frame) - 50, CGRectGetMidY(self.view.frame) - 50, 100, 100)
        
        self.view.addSubview(self.greenBox!)
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

並且於viewDidLoad()中建立WCSession

let session = WCSession.defaultSession()

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    
    ...
    
    if WCSession.isSupported() {
        session.delegate = self
        session.activateSession()
    }
}

最後我們來實作didReceiveMessage()函式,將接收到的資料加以應用。根據按鈕的編號來去對greenBox移動其位子,而dispatch_async的意思就是將任務進行異步並行處理,不一定需要一個任務處理完後才能處理下一個。而當我們移動greenBox後,應該更新畫面,從 queue 的概念去設計,就是要將更新畫面的動作放到main queue中,因為iOS裡面永遠是主執行緒來更新重畫UI,dispatch_get_main_queue() 函式就是返回主執行緒。

func session(session: WCSession, didReceiveMessage data: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
    
    
    if let pressNum = data["buttonNum"] as? Int {
        
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            switch pressNum {
                
                // Up
            case 0: self.greenBox!.frame.origin.y -= 20
                
                // Down
            case 1: self.greenBox!.frame.origin.y += 20
                
                // Left
            case 2: self.greenBox!.frame.origin.x -= 20
                
                // Right
            case 3: self.greenBox!.frame.origin.x += 20
                
            default:
                print("Error!")
            }
        })
        
    }
}

完成之後,Run起來看看,你是不是也可以透過Apple Watch去遙控iPhone中的綠色小方塊,很有趣吧!

watch-app-connectivity-final

你可在這裡下載完整示範程式,如有任何疑問,歡迎留言。

作者
Max Chen
一名熱愛新技術的大學生,於就讀輔仁大學資管系期間曾任多門程式設計課程助教,專長為前端介面設計與iOS程式開發,目前於HackNTU 擔任iOS 課程講師及助教,並往UI/UX設計師與iOS開發者方向努力。可以透過LinkedIn與我聯繫。
評論
更多來自 AppCoda 中文版
很好! 你已成功註冊。
歡迎回來! 你已成功登入。
你已成功訂閱 AppCoda 中文版 電子報。
你的連結已失效。
成功! 請檢查你的電子郵件以獲取用於登入的連結。
好! 你的付費資料已更新。
你的付費方式並未更新。