0. 環境

[OS] Mac OS X 10.11.6
[IDE] Xcode 8.1
[Swift] 3.0

1. 問題

  • WCSession#sendMessage()を使ってAppleWatchからiPhoneアプリへメッセージを送っても、30秒ほどしないと通知されない。

    • AppleWatch側

      @IBAction func tapButton() {
      
          let message = [ "fromChild": "AppleWatchからのメッセージ" ]
      
          WCSession.default().sendMessage(
                  message
                  , replyHandler: { reply in }
                  , errorHandler: { error in }
          )
      
      }
      
    • iPhone側

      func session(_ session: WCSession, didReceiveMessage message: [String: Any], replyHandler: @escaping ([String: Any]) -> Void) {
      
          if let watchMessage = message["fromChild"] as? String {
              print(watchMessage)
          }
          else{
              print("error")
          }
      
      }
      

2. 対策

  • replyHandlerを設定するなど、いろいろ情報はあったのですが、結局 sendMessage() を諦め、updateApplicationContext() を使うことにしました。

  • ただ updateApplicationContext() は状態が変わった際に通知するためのメソッドなので、二回目以降の値が変わらないとiPhone側のメソッドが呼び出されません。

    API仕様書には明記されていませんがそのような動きをしています。(「3. 参考ページ」参照)

    ですので、sendMessage() と同じように使いたい場合は、毎回変わる値をダミーとして設定すると実現できます。(多少強引ですが、下記例では時間を設定しています)

    • AppleWatch側

      @IBAction func tapButton() {
      
          let dateFormatter = DateFormatter()
          dateFormatter.locale = Locale(identifier: "ja_JP")
          dateFormatter.timeStyle = .medium
          dateFormatter.dateStyle = .medium
      
          let item: Dictionary<String, String> = [
              "message": "AppleWatchからのメッセージ"
              , "date": dateFormatter.string(from: NSDate() as Date)]
      
          let message = [ "fromChild": item ]
      
          do{
              try WCSession.default().updateApplicationContext(message)
          }catch{
              print(error)
          }
      
      }
      
    • iPhone側

      func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
      
          DispatchQueue.main.async { () -> Void in
              if let watchMessage = applicationContext["fromChild"] as? Dictionary<String, String> {
                  self.label.text = watchMessage["message"]! as String
              }
          }
          return
      
      }
      

3. 参考ページ