Watch Connectivity

Watch Connectivityを使うことでApple WatchとiPhoneのAppがお互いにメッセージ(データ)をやりとりすることが出来るようになります. watchOSのメジャーバージョンが2になってから利用できるようになりました. ここではApple Watch AppからiPhone Appを操作するというシチュエーションでWatch Connectivityを利用する方法を記録します. この時のiPhone Appはバックグラウンドでも問題ありません(CapabilitiesタブのBackground ModesがOFFでも大丈夫です). 言語はSwift2.0を使います.
  1. WatchConnectivityフレームワークをimport
  2. ViewControllerにWCSessionDelegateを追加
  3. WCSessionが利用可能ならばセッションを準備する
  4. sendMessageでメッセージをやりとり!

フレームワークをimportする

Watch Connectivityは"WatchConnectivity"というフレームワークとして提供されています. Watch側の"InterfaceController.swift"とiPhone側の"ViewController.swift"両方にこのフレームワークをimportします. "WCSessionDelegate"の追記も忘れずに.

InterfaceController.swift

import WatchKit
import Foundation
import WatchConnectivity

class ViewController: UIViewController, WCSessionDelegate {
...

ViewController.swift

import UIKit
import WatchConnectivity

class InterfaceController: WKInterfaceController, WCSessionDelegate {
...

WCSessionクラスで通信の準備をする

WCSessionクラスがWatch - iPhone間の通信を管理してくれます. まずはWCSessionが利用可能か否かを判定し,可能ならばWCSessionをセットアップします. Watch側はwillActivate内に,iPhone側はviewDidLoad内に次のコードを書き込むと良いでしょう. WCSession型のプロパティを用意しておくと便利です.

InterfaceController.swift

var session: WCSession?
...
override func willActivate() {
  super.willActivate()
  
  if WCSession.isSupported() {
    session = WCSession.defaultSession()
    session!.delegate = self
    session!.activateSession()
  }
}
...

ViewController.swift

var session: WCSession?
...
override func viewDidLoad() {
  super.viewDidLoad()
  
  if WCSession.isSupported() {
    session = WCSession.defaultSession()
    session!.delegate = self
    session!.activateSession()
  }
}
...

メッセージを送る!

WCSessionの準備が出来たら後はメッセージを送るだけです. Watch App側(送り手側)はWCSessionのメソッド"sendMessage"でメッセージを送ります. iPhone App側(受け手側)はWCSessionDelegateのメソッド"session"で受信します. Watch Connectivityはメッセージを送れるだけではなく,返信を返すことも出来ます. 返信の処理はreplyHandlerというハンドラーで行います. ここではシンプルにするため,Watch App側にボタン(UIButton)をひとつ用意します. このボタンがタップされたらメッセージを送信するようにします. ラベル(UILabel)も用意して,iPhoneからの返信内容を表示するようにしましょう. iPhone App側はラベル(UILabel)をひとつ用意して,Watchからのメッセージを表示します.
Watch AppのUI
iPhone AppのUI
コードは次のとおりです. まずはUILabelのアウトレットを用意します. そしてWatch側のみUIButtonのアクションも用意します. 定数"message"と"reply"はディクショナリ,すなわち連想配列です. これが実際に送られるメッセージの本体になります. ["キー": 値]の形ですが,キーの部分はお好みの文字列にします. 受け手側が値を取り出す時のキーになるので,分かりやすい文字列が良いです. 値はAnyObject型なので文字通りどんなオブジェクトでも良いです. 今回はメッセージのやりとりという意味を込めて文字列にしました.

InterfaceController.swift

@IBAction func sendButtonTapped() {
  guard session != nil else {
    return
  }
  
  let message = ["body": "From Watch App"]
  
  session!.sendMessage(message, replyHandler: {(responses) -> Void in
    if let answer = responses["answer"] as? String {
      self.label.setText(answer)
    }
  }, errorHandler: {(error) -> Void in
    print(error)
  })
}
...

ViewController.swift

func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) {
  guard let message = message["body"] as? String else {
    return
  }
  
  dispatch_async(dispatch_get_main_queue()) {
    self.label.text = message
  }
  
  let reply = ["answer": "Reply from iPhone"]
  
  replyHandler(reply)
}
...
上から順に説明を. Watch AppのボタンがタップされたらWCSessionが準備されているかチェックします. 準備されていれば先へ進みます. メッセージ本体である定数"message"です. キーには"body",値には"From Watch App"と書きました. WCSessionのメソッド"sendMessage:replyHandler:errorHandler:"を使って定数"message"をiPhone側へ送ります. ハンドラ"replyHandler"には返信を受け取った後の処理を書きます. 今回はiPhoneからの返信であるディクショナリ"responses"の値をラベルへセットしています. クロージャ内なのでselfをつけ忘れないよう注意しましょう.
iPhone側はWCSessionDelegateのメソッド"session:didReceiveMessage:replyHandler:"でメッセージを受信します. まずはじめにメッセージの値をString型で取り出そうとしています. 上手く取り出せれば先へ進めます. ここであまりお目にかかれない函数を使います. dispatch_asyncです. 詳細は理解していませんが,この函数のおかげでキューの処理を非同期に実行してくれるようです. これが無いと画面の更新に時間がかかります. 定数"reply"に返信ディクショナリを用意します. replyHandlerに返信ディクショナリを渡せば返信は完了です. この返信を受け取ったWatch側はreplyHandlerの処理を実行します. つまりラベルの文字を返信"Reply from iPhone"にセットするはずです.

Appを試す

コードをビルドし,iPhoneとWatchの両方にインストールしたら両方起動してUIを確認しましょう. Watch Appのボタンを押すと,まずiPhoneに"From Watch App"の文字が現れ,続けてWatchに"Reply from iPhone"の文字が表示されるはずです.
Watch App 送り手
iPhone App 受け手
見事,Watch Connectivityによるメッセージのやりとりに成功しました! iPhone側のsessionメソッド内でiPhoneのデータを読み出す処理を呼び出し返信にそのデータを送れば,WatchからiPhoneのデータを読み出すことも可能です. 先にも書いたようにこのWatch ConnectivityはiPhone Appがバックグラウンドでも動作するので,iPhoneはポケットの中でもWatchのボタンを押せば返信を受け取ることが出来ます. もちろん次回iPhone Appを立ち上げた時には受信した証拠がちゃんと表示されます. 今回はWatchからiPhoneへの送信を例に紹介しましたが,iPhoneからWatchへの送信も同様の手順で実装できます. iPhone側でsendMessageし,Watch側のsessionで受信する,という全く同じ流れです.
9198570280932029721 http://www.storange.jp/2016/03/watch-connectivity.html http://www.storange.jp/2016/03/watch-connectivity.html Watch Connectivity 2016-03-25T21:59:00+09:00 http://www.storange.jp/2016/03/watch-connectivity.html Hideyuki Tabata 200 200 72 72