WatchKit AppからiPhone上の親アプリケーション本体にデータを渡す


この投稿は[WatchKit Advent Calendar 2014](http://qiita.com/advent-calendar/2014/watchkit)の15日目の記事です。

iOS 8.2のリリースノートを眺めていると以下のような一文があり気になったので試してみます。

>A WatchKit app can ask its containing iOS app to perform functionality using the WKInterfaceController API openParentApplication:reply:. The app delegate of the containing app must implement application:handleWatchKitExtensionRequest:reply:.
[iOS SDK Release Notes for iOS 8.2 Beta 2](https://developer.apple.com/library/prerelease/ios/releasenotes/General/RN-iOSSDK-8.2/index.html)

WatchKit AppとiPhone上の親アプリケーション本体でデータのやり取りができるようです。
WatchKit AppとiPhone上の親アプリケーションのデータの受け渡しには、以下の２つのことが必要なようです。

* WatchKit Extension側(WKInterfaceControllerの実装)で、[openParentApplication:reply:](https://developer.apple.com/library/prerelease/ios/documentation/WatchKit/Reference/WKInterfaceController_class/index.html#//apple_ref/occ/clm/WKInterfaceController/openParentApplication:reply:)を呼び出すこと。
* 本体側のAppDelegateで、 [application:handleWatchKitExtensionRequest:reply:](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:handleWatchKitExtensionRequest:reply:) を実装しておくことです。

以下ではサンプルアプリを作って確認した際のコードを元に、どのような実装になっていくのかを確認します。


## サンプルコード

### openParentApplication:reply:の実装

WatchKit App側コードで関連のある部分のみを抜粋しています。
このコードは、WatchKit Appに表示するボタン押下時に実行するsendCounterメソッドの部分を示しています。


```Swift:InterfaceController.swift
@IBAction func sendCounter() {
    //Send count to parent application
    WKInterfaceController.openParentApplication(["countValue": "\(self.count)"],
        reply: {replyInfo, error in
            println(replyInfo["fromApp"])
    })
}

```
openParentApplication:reply:の第一引数のuserInfo: [NSObject : AnyObject]!が本体側に渡されます。
ここでは、countValueという文字列をKeyにして、countで定義した値を設定し、iPhone上の親アプリケーションに渡しています。
また、第二引数のクロージャの中では、親アプリケーション側で設定されたreplyInfoの内容を表示しています。

### application:handleWatchKitExtensionRequest:reply:の実装

ここでは、iPhone上の親アプリケーション本体で定義する必要のあるapplication:handleWatchKitExtensionRequest:reply:の部分をAppDelegate.swiftから抜粋しています。

```Swift:AppDelegate.swift

func application(application: UIApplication!, handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!, reply: (([NSObject : AnyObject]!) -> Void)!) {
    
    var counterValue: String = userInfo["countValue"] as String!

    var appDel = UIApplication.sharedApplication().delegate as AppDelegate
    var vc:ViewController? = appDel.window?.rootViewController as? ViewController
    vc?.ulabel.text = counterValue

    reply(["fromApp":"AppDelegate"])
    
}

```

最初にopenParentApplication:reply:の第一引数userInfoで設定されたcountValueの値を取得しています。
そしてその値を、Viewに設置したLabelのTextに設定しています。
これで、WatchKit App側からsendCounterが呼ばれるたびにラベルの表示が変更されることが確認できます。
また、replyメソッドを利用してWatchKit App側に値（AppDelegate）を渡してます。

なお、iPhone上の親アプリケーションでは、ここに記載しているapplication:handleWatchKitExtensionRequest:replyの部分以外は特に意識する必要はありません。

### WatchKit App側コード（全体）

上記では、openParentApplication:reply:に関係のあるsendCounterメソッドのみを抜粋しました。

InterfaceController.swift全体では、ラベルとボタン２つを作成し、１つ目のボタンを押下するとラベルに表示している数が１ずつ増え（countUpメソッド）、２つ目のボタンを押下するとiPhone上の親アプリケーションにラベルに表示している値が渡されるという処理を実装しています。

以下にInterfaceController.swift全体を示します。


```Swift:InterfaceController.swift
import WatchKit
import Foundation

class InterfaceController: WKInterfaceController {
    var count = 0
    @IBOutlet weak var label: WKInterfaceLabel!
    
    override func awakeWithContext(context: AnyObject?) {
        super.awakeWithContext(context)
        
        // Configure interface objects here.
        NSLog("%@ awakeWithContext", self)
    }

    override func willActivate() {
        // This method is called when watch view controller is about to be visible to user
        super.willActivate()
        label.setText("0")
        NSLog("%@ will activate", self)
    }

    override func didDeactivate() {
        // This method is called when watch view controller is no longer visible
        NSLog("%@ did deactivate", self)
        super.didDeactivate()
    }

    @IBAction func countUp() {
        self.count += 1
        label.setText("\(self.count)")
    }
    
    @IBAction func sendCounter() {
        //Send count to parent application
        println("Watch \(self.count)")
        WKInterfaceController.openParentApplication(["countValue": "\(self.count)"],
            reply: {replyInfo, error in
                println(replyInfo["fromApp"])
        })
    }
}
```