Swift MkMapViewで地図アプリ作成してみた（14）- iOSから送信したデータをwatchOSで受信する

#記事一覧
[Swift MkMapViewで地図アプリ作成してみた（記事一覧）](https://qiita.com/rain_00_00_09/items/3151b3faf2303fe78312)

#iOSからwatchOSにデータを送信する
###セッションをアクティブにする
iOSアプリとwatchOSアプリ間の通信に必要なWatchkitのWCSeesionオブジェクトを使用するため、WatchConnectivityをimportする。

```swift:ViewController.swift：
import WatchConnectivity
```

セッションをアクティブにするために、WCSessionDelegateを継承し、デフォルトのセッションオブジェクトにデリゲートを割り当て、activate()メソッドを呼び出す。

```swift:ViewController.swift：セッションをアクティブにする
class ViewController:   UIViewController,
                        CLLocationManagerDelegate,
                        UIGestureRecognizerDelegate,
                        WCSessionDelegate {

    var session: WCSession!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        // セッションをアクティブにする
        if (WCSession.isSupported()) {
            self.session = WCSession.default
            self.session.delegate = self
            self.session.activate()
        }
```

WCSessionDelegateに次の3つのメソッドを実装しないとエラーになる。

* public func session(_ session: WCSession, activationDidCompleteWith activationState:    WCSessionActivationState, error: Error?)
    
    ```swift:ViewController.swift：sessionの実装
    // セッションアクティブ化の結果が通知される
    public func session(_ session: WCSession, activationDidCompleteWithactivationState:WCSessionActivationState, error: Error?){
        switch activationState {
        case .activated:
            print("セッションアクティブ")
        case .inactive:
            print("セッションはアクティブでデータ受信できる可能性はあるが、相手にはデータ送信できない")
        case .notActivated:
            print("セッション非アクティブで通信できない状態")
            let errStr = error?.localizedDescription.description
            print("error: " + (errStr?.description)!)
        }
    }
```
    errorは問題が発生したことを示すエラーオブジェクト。
    アクティブ化が正常に完了した場合はnilで、activationStateパラメーターが.notActivatedの場合、失敗の理由が格納される。
    breakは不要。なのは驚いた。。

* public func sessionDidBecomeInactive(_ session: WCSession)

    ```swift:ViewController.swift：sessionDidBecomeInactiveの実装
    public func sessionDidBecomeInactive(_ session: WCSession){
        print("sessionDidBecomeInactive")
    }
    ```

* public func sessionDidDeactivate(_ session: WCSession)

    ```swift:ViewController.swift：sessionDidDeactivateの実装
    public func sessionDidDeactivate(_ session: WCSession){
        print("sessionDidDeactivate")
    }
    ```

###watchOSにデータを送信する
activationStateプロパティが.activatedに設定されている場合にのみ、以下のいずれかの方法でwatchOSのアプリにデータを送信できる。

* [updateApplicationContext(_:)](https://developer.apple.com/documentation/watchconnectivity/wcsession/1615621-updateapplicationcontext)
* [sendMessage(_:replyHandler:errorHandler:)](https://developer.apple.com/documentation/watchconnectivity/wcsession/1615687-sendmessage)
* [sendMessageData(_:replyHandler:errorHandler:)](https://developer.apple.com/documentation/watchconnectivity/wcsession/1615617-sendmessagedata)
* [transferUserInfo(_:)](https://developer.apple.com/documentation/watchconnectivity/wcsession/1615671-transferuserinfo)
* [transferFile(_:metadata:)](https://developer.apple.com/documentation/watchconnectivity/wcsession/1615667-transferfile)
* [transferCurrentComplicationUserInfo(_:)](https://developer.apple.com/documentation/watchconnectivity/wcsession/1615639-transfercurrentcomplicationuseri)

今回はsendMessageでデータを送信する。
[Swift MkMapViewで地図アプリ作成してみた（10）](https://qiita.com/rain_00_00_09/items/d426a32ab8356a97fc78)で取得した緯度経度をsendMessageで送信する。

```swift:ViewController.swift：watchOSに緯度経度を送信
// UILongPressGestureRecognizerのdelegate：ロングタップを検出する
@IBAction func mapViewDidLongPress(_ sender: UILongPressGestureRecognizer) {
    // ロングタップ開始
    if sender.state == .began {
        // ロングタップ開始時に古いピンを削除する
        mapView.removeAnnotation(pointAno)
    }
    // ロングタップ終了（手を離した）
    else if sender.state == .ended {

        //
        // 処理は割愛、上記の[Swift MkMapViewで地図アプリ作成してみた（6）]をご参照ください。
        //

        // watchOSに緯度経度を送信
        let dlon:Double = center.longitude
        let dlat:Double = center.latitude
        let contents =  ["lon":dlon, "lat":dlat]
        self.session.sendMessage(contents, replyHandler: { (replyMessage) -> Void in
            print ("receive from apple watch");
        }) { (error) -> Void in
            print(error)
        }
```

#watchOSでiOSから送信されたデータを受信する
###セッションをアクティブにする
iOSアプリとのセッションを確立するために、watchOS側のInterfaceController.swiftを実装する。

```swift:InterfaceController.swift：WatchConnectivityのimportとWCSessionDelegateの継承
import WatchConnectivity

class InterfaceController:  WKInterfaceController,
                            WCSessionDelegate {

```

デフォルトのセッションオブジェクトにデリゲートを割り当て、activate()メソッドを呼び出す。
storyboardのlabelとコード上のlabelを紐付ける。

```swift:InterfaceController.swift：セッションをアクティブにする
class InterfaceController:  WKInterfaceController,
                            WCSessionDelegate {
    var session:WCSession!
    @IBOutlet weak var label: WKInterfaceLabel!
    
    override func awake(withContext context: Any?) {
        super.awake(withContext: context)
        
        // Configure interface objects here.
        if (WCSession.isSupported()) {
            self.session = WCSession.default
            self.session.delegate = self
            self.session.activate()
        }
    }
```

WCSessionDelegateに次の1つのメソッドを実装しないとエラーになる。

* func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {

```swift:InterfaceController.swift：sessionの実装
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        print("session")
    }
```

###iOSからデータを受信する

iOSからsendMessageで送信されたデータを受信する。

```swift:InterfaceController.swift：iOSからsendMessageで送信されたデータを受信
    // iPhoneからMessage受信
    public func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Swift.Void){
        print("receiveMessage::\(message)")
        
        guard let lon = message["lon"] as? Double else {
            return
        }

        guard let lat = message["lat"] as? Double else {
            return
        }
        
        label.setText("lon: " + lon.description + "\n" + "lat: " + lat.description)
    }
```

実行すると以下の様になる。
<img width="750" alt="image1.png" src="https://qiita-image-store.s3.amazonaws.com/0/362635/0b1715a3-a444-0422-3bfb-4bcd65f09aa3.png">

#参考文書
[WCSession - WatchConnectivity | Apple Developer Documentation](https://developer.apple.com/documentation/watchconnectivity/wcsession)