既存アプリのiOS9,watchOS2対応をぼちぼちと。
swift1.2 -> swift2へのconvertもあるので細かくあれこれ時間がかかる。
watchOS2では、いままでのwatchKitではiOSアプリとwatchアプリの通信に使っていた
– openParentApplication:reply:
が無くなってしまったので新しいAPI Watch Connectivity で実装し直す
– openParentApplication:reply: ではwatch側からiOS側へconnectionを開くことしかできない(iOS側から通信を開始できない)が、WCSessionでは双方ともに可能。
おかげでだいぶコードがすっきりする。
準備
delegateの宣言
iOS側のWatch Connectivityを使うクラスの宣言を以下の様に(太字部分追加)
class ViewController: UIViewController, WCSessionDelegate
watch ExtensionのWatch Connectivityを使うクラスの宣言を以下の様に(太字部分追加)
class InterfaceController: WKInterfaceController, WCSessionDelegate
両方とも
import WatchConnectivity
を忘れないように。
データを受信するためにセッションを開始する
例えばiOS側のViewControllerで使う場合、viewDidLoadの最後あたりで、以下の様にしてセッションを開始する
if (WCSession.isSupported()) { let session = WCSession.defaultSession() session.delegate = self session.activateSession() }
watch Extension側でも同様に例えばWKInterfaceControllerで使う場合、willActivateの最後あたりに同じコード追加。
ハマったのはここ。
実装したかったのは片方向(iOS -> watch)の通信のみだったので受信側(watach)のみでこのコードを実装したが、iOS側からwatchを見つけられない(後述の WCSession.defaultSession().reachable がfalse になる)。
送信側にも通信が始まる前にこのコードが実行されるようにコードを追加する必要がある
openParentApplication:replyのイメージが強くて、勝手にサーバクライアントのようなモデルを想定してしまったのが失敗。
通信開始
WCSessionの通信にはすぐに通信が開始されるInteractive messagingと、機嫌が良いときに通信しておいてくれるBackground transfersがある。Interactive messagingの場合は、iOSからwatchに送信する場合はwatchOS上でアプリが起動している必要がある。
今回はInteractive messagingを例に。
送信側:
if WCSession.defaultSession().reachable { //通信可能かチェック var message:[String: AnyObject] = ["message": "Message From Phone" ] WCSession.defaultSession().sendMessage(message, replyHandler: { (replyMessage) -> Void in // }) { (error) -> Void in print(error) } }
受信側(delegateのメソッド実装):
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void) { if let tmessage:String? = message["message"] as! String? { print(tmessage) } }
messageが[String: AnyObject]となっていることから分かるように、String以外も送信可能。
簡単に双方向通信ができる様になった。
MMWormhole要らなくなりそうだ。
(いままで(watchOS1で)動いていたMMWormholeのコードがwatchOS2のシミュレータでは動かなかった。コンパイルも通りエラーも出ないが、受信してくれず。)