【Swift/Apple Watch】iOSへデータを送受信するメソッドの違いと使い方!

【Swift/Apple Watch】iOSへデータを送受信するメソッドの違いと使い方!

この記事からわかること

  • SwiftApple Watchアプリ開発する方法
  • iOSデータ送信/受信するには?
  • sendMessageメソッド使い方
  • transferUserInfo/updateApplicationContext/transferFile(_:metadata:)違い

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

iOS↔︎watchOS間でデータを送受信する方法

iOSとwatchOSの連携アプリでは両OS間でデータをやり取りすることが可能です。送受信するための方法は4種類用意されており、それぞれ挙動やできることが異なるので違いと使い方をまとめていきたいと思います。使用するメソッドはWatchConnectivityフレームワークから提供されているおり、iOS/watchOSともに共通の実装で行うことが可能です。

データを送信するため役割を持っているのは以下の4種類です。

transferFileメソッドを除いてですが両OS間でのデータのやり取りは辞書型[String: Any]形式で送受信が可能になっています。

またデータの受信側はWCSessionDelegateに準拠させることで各デリゲートメソッドよりデータを受信できるようになります。

送信できるデータ型

sendMessagetransferUserInfoなどのデータを送信するメソッドは[String: Any]形式の辞書型でデータを送信できますが、サポートしているのはプロパティタイプリストのデータ型(基本的なデータ型)のみのようです。そのため独自のクラスなどはサポートされていないのでJSONなどに変換してString型として送信するのが定石です。

公式リファレンス:Property List Types and Objects

sendMessageメソッドの定義を確認してみるとコメントの部分に「メッセージ ディクショナリはプロパティ リスト タイプのみを受け入れることができます」と記述されていました。


/**  クライアントはこのメソッドを使用して、対応するアプリにメッセージを送信できます。特定のメッセージに対する応答を受け取りたいクライアントは、replyHandler ブロックを渡す必要があります。メッセージを送信できない場合、または応答を受信できない場合は、errorHandler ブロックがエラーで呼び出されます。 ReplyHandler と errorHandler の両方が指定されている場合は、そのうちの 1 つだけが呼び出されます。メッセージは送信アプリの実行中にのみ送信できます。メッセージが送信される前に送信側アプリが終了すると、送信は失敗します。対応するアプリが実行されていない場合、メッセージの受信時に対応するアプリが起動されます (iOS 対応アプリのみ)。メッセージ ディクショナリはプロパティ リスト タイプのみを受け入れることができます。 */
open func sendMessage(_ message: [String : Any], replyHandler: (([String : Any]) -> Void)?, errorHandler: ((Error) -> Void)? = nil)

sendMessageメソッド

公式リファレンス:sendMessageメソッド

open func sendMessage(_ message: [String : Any], replyHandler: (([String : Any]) -> Void)?, errorHandler: ((Error) -> Void)? = nil)

sendMessageペアリングされたアクティブなデバイスにメッセージを送信するメソッドです。引数replyHandlerには受け取り側にデータが到達後に実行したい処理を、引数errorHandlerでは送信エラーが発生した時に実行したい処理を渡すことができます。

送信処理の実装例

let dataDic: [String: String] = ["response": text]
self.session.sendMessage(dataDic, replyHandler: { message in
    print("正常に送信し、相手が受信しました", message)
} ) { error in
    print(error)
}

受信処理の実装例

受信側はsession(_: WCSession, didReceiveMessage: , replyHandler:)から受け取ることができます。受信したデータは[String : Any]型になっているので送信したキーから値を取り出し、適切な型にキャストすることで中身を取得することができます。replyHandlerで受け取ったデータを返すことで送信側に受信したことを知らせることができます。

func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
    replyHandler(message) // これをしないとsendMessage(送信側)のreplyHandlerは呼ばれない
    guard let result = message["response"] as? String else { return }
    print(result)
}

replyHandlerにnilを渡すとデリケートメソッドが変化

引数replyHandlerにはnilを渡すことも可能です。その場合は受信するデリゲートメソッドが変化するので注意してください。

送信処理の実装例

self.session.sendMessage(dataDic, replyHandler: nil) { error in
    print(error)
}

受信処理の実装例

受信側はsession(_: WCSession, didReceiveMessage:)から受け取ることができます。

func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
  print("replyHandler:nil")
  guard let result = message["response"] as? String else { return }
  print(result)
}

watchOS側から送信するとiOSがバックグラウンドで起動?

WatchKit 拡張機能がアクティブで実行中にこのメソッドを呼び出すと、対応する iOS アプリがバックグラウンドで起動され、アクセス可能になります。 iOS アプリからこのメソッドを呼び出しても、対応する WatchKit 拡張機能は起動されません。

引用:公式リファレンス:Discussion

公式ドキュメントには上記のように記述されていました。動作を確認してみたところ、iOS側が停止している状態でもwatchOS側からsendMessageを送信するとreplyHandlerには受信が成功した通知が取得できました。

transferUserInfo

公式リファレンス:transferUserInfoメソッド

func transferUserInfo(_ userInfo: [String : Any] = [:]) -> WCSessionUserInfoTransfer

transferUserInfoペアリングされたデバイスに辞書形式でデータを送信できるメソッドです。しかしsendMessageとは異なりキューにデータをスタックしていき、相手が受信可能になっていれば送信する仕組みになっています。

そのため相手のデバイスが非アクティブ(停止している)状態でもデータの送信が可能(正確にはキューイング)になっており、相手がアクティブになったタイミングでデータが送信され始めるため、相手側はアクティブになったと同時にデータを受信することができます。

このメソッドには相手が受信したかどうかやエラーをハンドリングする手段がないのでデータを確実に一方的に送信したい場合などに活用することができます。連続で実行すればどんどんキューが溜まっていきアクティブになると順番通りに送信されていくようです。

送信処理の実装例

let requestDic: [String: String] = ["request": true]
self.session.transferUserInfo(requestDic)

受信処理の実装例

受信側はsession(_:, didReceiveUserInfo:)から受け取ることができます。

func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
    guard let result = userInfo["request"] as? Bool else { return }
    if result {
        send("request")
    }
}

シミュレーターでは動作しない

またこのメソッドはシミュレーターでは動作確認することができないと公式に記述されているので実機を使用してテストする必要があるので注意してください。

updateApplicationContext

公式リファレンス:updateApplicationContextメソッド

func updateApplicationContext(_ applicationContext: [String : Any]) throws

updateApplicationContextペアリングされたデバイスに辞書形式でデータを送信できるメソッドです。transferUserInfoと同じように相手がバックグラウンドなどの状態でもデータを送信することが可能になっています。

transferUserInfoとの違いはキューに蓄積せずに常に更新することです。連続で実行してもアクティブになった時に送信されるのは最後に送信したデータになります。

このメソッドには相手が受信したかどうかやエラーをハンドリングする手段がないのでデータを確実に一方的に送信したい場合などに活用することができます。連続で実行すればどんどんキューが溜まっていきアクティブになると順番通りに送信されていくようです。

またこのメソッドはシミュレーターでも正常に動作します。

送信処理の実装例

let dataDic: [String: String] = ["response": text]
do {
  try session.updateApplicationContext(dataDic)
} catch {
  print(error.localizedDescription)
}

受信処理の実装例

受信側はsession(_:, didReceiveApplicationContext:)から受け取ることができます。

func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
    guard let result = applicationContext["response"] as? String else { return }
    print(result)
}

transferFile

公式リファレンス:transferFileメソッド

func transferFile(_ file: URL, metadata: [String : Any]?) -> WCSessionFileTransfer

transferFileは他の送信メソッドと異なりペアリングされたデバイスに指定されたファイルを送信できるメソッドです。非常に大きなデータを送信する際などに活用することができます。

こちらも相手がバックグラウンドでも送信が可能であり、transferUserInfo同じくキューに溜まっていく仕組みのようです。データが大きいためパフォーマンスと充電を考慮し配信速度が調整される場合があります。そのためoutstandingFileTransfersプロパティから未配信のファイルを参照できるようになっています。

var outstandingFileTransfers: [WCSessionFileTransfer] { get }

またこのメソッドもシミュレーターでは動作確認することができないと公式に記述されているので実機を使用してテストする必要があるので注意してください。

送信処理の実装例

self.session.transferFile(fileURL, metadata: nil)

受信処理の実装例

受信側はsession(_:, didReceive:)から受け取ることができます。

func session(_ session: WCSession, didReceive file: WCSessionFile) {
  let data = try? Data.init(contentsOf: file.fileURL)
}

各メソッドの違いとまとめ

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index