Watch Connectivityの超シンプルなサンプルコード【Swift】

本記事では、Watch Connectivityのとてもシンプルなサンプルコードを提供します。

公式のサンプルもありますが、こちらは全部入りとなっており、初めて見るとちょっと腰が引けてしまいます。そこで、本記事では余計な処理をすべて省いたとてもシンプルなコードを提供します。

Watch Connectivityで作るサンプルアプリ

本記事で作るアプリは次のようなアプリです。

iPhoneとApple Watchの双方向で、sendMessagemメソッドを使ってメッセージの送受信を行います。

Watch Connectivityのサンプルを作る

さっそくコードを書いていきましょう。

プロジェクトの作成

まずはプロジェクトを作成します。iPhone側とApple Watch側の両方が必要なので、プロジェクトは「iOS App with watchOS App」を選択します。

iPhone側のコード

プロジェクトを作成したらiPhone側から作っていきましょう。ここでは2つのクラスを作成します。

  • WatchConnector
    • Apple Watchと通信するモジュールです
  • ContextView
    • デフォルトで作られるViewです。ここでUIを作ります。

WatchConnector

Apple Watchと通信するWatchConnectorクラスを作成します。短いので、先にすべてのコードをお見せします。

import UIKit
import WatchConnectivity

class WatchConnector: NSObject, ObservableObject, WCSessionDelegate {
    
    @Published var receivedMessage = "WATCH : 未受信"
    
    @Published var count = 0
    
    override init() {
        super.init()
        if WCSession.isSupported() {
            WCSession.default.delegate = self
            WCSession.default.activate()
        }
    }
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        print("activationDidCompleteWith state= \(activationState.rawValue)")
    }

    func sessionDidBecomeInactive(_ session: WCSession) {
        print("sessionDidBecomeInactive")
    }

    func sessionDidDeactivate(_ session: WCSession) {
        print("sessionDidDeactivate")
    }
    
    func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
        print("didReceiveMessage: \(message)")
        
        DispatchQueue.main.async {
            self.receivedMessage = "WATCH : \(message["WATCH_COUNT"] as! Int)"
        }
    }
    
    func send() {
        if WCSession.default.isReachable {
            count += 1
            WCSession.default.sendMessage(["PHONE_COUNT" : count], replyHandler: nil) { error in
                print(error)
            }
        }
    }
    
}

順番に説明していきますね。

まずはクラスの定義です。

class WatchConnector: NSObject, ObservableObject, WCSessionDelegate {

ObservableObjectはSwiftUIに受信したメッセージをバインドするためです。

WCSessionDelegateはWatch Connectivityが提供するプロトコルです。これを実装することで、Watch Connectivityのセッションのイベントや、送受信のイベント通知を受けることができます。

次に初期化を見ていきます。

    override init() {
        super.init()
        if WCSession.isSupported() {
            WCSession.default.delegate = self
            WCSession.default.activate()
        }
    }

ここでは、Watch ConnectivityのセッションであるWCSessionをアクティベートしています。Watch Connectivityがサポートしていることを確認してから、デリゲートの指定とアクティベート化を行っています。

WCSEssionのデリゲートでは下記のメソッドが必須です。

    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        print("activationDidCompleteWith state= \(activationState.rawValue)")
    }

    func sessionDidBecomeInactive(_ session: WCSession) {
        print("sessionDidBecomeInactive")
    }

    func sessionDidDeactivate(_ session: WCSession) {
        print("sessionDidDeactivate")
    }

それぞれ、アクティベート処理の完了・インアクティブ状態への遷移・ディアクティベイト状態への遷移を意味します。

WCSessionのデリゲートで、sendMessageでメッセージが送られてきた時の処理を見てみましょう。

    func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
        print("didReceiveMessage: \(message)")
        
        DispatchQueue.main.async {
            self.receivedMessage = "WATCH : \(message["WATCH_COUNT"] as! Int)"
        }
    }

sendMessageでは辞書型のデータをメッセージとして送受信します。このアプリはApple Watchから”WATCH_COUNT”というキーで、Int型のカウンタが送られてくる仕様にしました。なので、ここではWATCH_COUNTの値を取り出し、receivedMessageに格納します。receivedMessageは@Publishedに指定しており、あとでSwiftUIで表示します。

最後に送信ボタンが押された時に、Apple WatchにsendMessageするためのsend()メソッドを作ります。

    func send() {
        if WCSession.default.isReachable {
            count += 1
            WCSession.default.sendMessage(["PHONE_COUNT" : count], replyHandler: nil) { error in
                print(error)
            }
        }
    }

iPhone側からは”PHONE_COUNT”というキーでカウンタを送る仕様にしました。

これでApple Watchと通信するクラスの作成は終わりです。とても簡単ですね。

ContextView

通信する部分ができたので、UIを作ります。

import SwiftUI

struct ContentView: View {
    
    @ObservedObject var connector = WatchConnector()
    
    var body: some View {
        VStack {
            HStack {
                Text("\(connector.count)")
                Button(action: { self.connector.send() }) { Text("送信") }
            }
            Text("\(self.connector.receivedMessage)")
        }
    }
}

説明は不要ですね。

Apple Watch側のコード

Apple Watch側でも同様に次の2つのクラスを作ります。

  • PhoneConnector
    • iPhoneと通信するモジュールです。
  • ContextView
    • デフォルトで作られるViewです。ここでUIを作ります。

PhoneConnector

iPhone側とほとんど同じです。

import UIKit
import WatchConnectivity

class PhoneConnector: NSObject, ObservableObject, WCSessionDelegate {
    
    @Published var receivedMessage = "PHONE : 未受信"
    
    @Published var count = 0
    
    override init() {
        super.init()
        if WCSession.isSupported() {
            WCSession.default.delegate = self
            WCSession.default.activate()
        }
    }
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        print("activationDidCompleteWith state= \(activationState.rawValue)")
    }
    
    func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
        print("didReceiveMessage: \(message)")
        
        DispatchQueue.main.async {
            self.receivedMessage = "PHONE : \(message["PHONE_COUNT"] as! Int)"
        }
    }
    
    func send() {
        if WCSession.default.isReachable {
            count += 1
            WCSession.default.sendMessage(["WATCH_COUNT" : count], replyHandler: nil) { error in
                print(error)
            }
        }
    }
    
}

違うのは、WCSessionDelegateの下記の2つのメソッドがない点です。Apple Watch側ではこれらのメソッドはサポートされていません。

    func sessionDidBecomeInactive(_ session: WCSession) {
        print("sessionDidBecomeInactive")
    }

    func sessionDidDeactivate(_ session: WCSession) {
        print("sessionDidDeactivate")
    }

Context View

説明不要なくらい同じです。

struct ContentView: View {
    
    @ObservedObject var connector = PhoneConnector()
    
    var body: some View {
        VStack {
            HStack {
                Text("\(connector.count)")
                Button(action: { self.connector.send() }) { Text("送信") }
            }
            Text("\(self.connector.receivedMessage)")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

以上でコードの作成は完了です。すべてのコードは GitHubに公開していますので、ご自由に利用してください。

WatchConnectivitySample – GitHub

動作確認

コードの作成が終わったら実機にインストールして動作確認してみましょう。

お互いにカウンタが送受信できていることを確認してください。

終わりに

本記事では、Watch Connectivityの超シンプルなコードを紹介しました。これを動かすことで、Watch Connectivityの基本を抑えられると思います。

今回はsendMessageしか使いませんでしたが、他にも通信方式があります。どういった通信があるかは、下記の記事を参考にしてください。

iPhoneとApple Watch間で通信するための2つの方法【Swift】

他の通信方式のサンプルは公式を見るのが良いでしょう。本記事のコードを理解したあとであれば、そこまで難しくないと思います。

Using Watch Connectivity to Communicate Between Your Apple Watch App and iPhone App

以上です。お疲れ様でした!