【SwiftUI】Apple Watchアプリを開発する方法!iPhoneと連携

この記事からわかること

  • Swift UIApple Watchアプリ開発する方法
  • iOS(iPhone)アプリと連携させるには?
  • WatchConnectivityフレームワークの使い方
  • WCSessionクラスとは?
  • データ送信/受信する方法
  • シミュレーター動作確認する方法

index

[open]

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

みんなの誕生日

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

posted withアプリーチ

環境

※最新のApple Watch(watchOS:10.1)はiOS17にしか対応していないようなので注意が必要です。

Apple Watchアプリの開発

公式リファレンス:watchOSアプリのプランニング

Apple製品の1つであるApple Watchで利用できるアプリを開発・公開したい場合はApple製品用アプリケーションの統合開発環境であるXcodeのインストール有料のメンバーシップであるADP(Apple Developer Program)への加入が必要になります。

Xcode

XcodeではiPhoneやiPad、Mac、Apple Watchで使用できるアプリを開発に必要な環境を提供してくれます。

App storeの中のxcode

おすすめ記事:Xcodeのインストール方法と使い方!終わらない時の対処法!

Apple Developer Program

App Storeにアプリを公開するためには「Apple Developer Program」への加入が必須になります。

Apple Developer Programへの登録方法

おすすめ記事:【iOS】Apple Developer Programとは?登録方法や費用と手順

Apple Watchアプリで実装できる機能

iPhoneとApple Watchでは利用されているOSが異なり、デバイスサイズも大きく変わりUIも流用できないため、ソースコードは別々で実装する必要があります。基本的な開発の流れはiOSアプリと変わらないのでiOSアプリを開発する知見があれば実装方法さえ掴めば開発は可能です。

Apple Watchアプリではヘルスケア情報やiPhoneとの連携、通知機能などさまざまな機能が実装できるようになっています。iPhoneでできることが全てできるわけではないと思うので実装できるかどうかは下調べが重要になってくると思います。

Apple Watchアプリのみの開発方法

ここからは実際に簡単なApple Watchアプリを開発して流れを掴んでいきたいと思います。Xcodeを起動させ「platform」に「watchOS」を選択し「App」をクリックします。

【SwiftUI】Apple Watchアプリを開発する方法!iPhoneと連携

続いてアプリ名やチームを選択し、Apple Watchアプリ単体で使うアプリであれば「Watch-only App」を、iPhoneと連携させるApple Watchアプリであれば「Watch App with New Companion iOS App」にチェックを入れて進みます。今回は「Watch-only App」で進めていきます。

【SwiftUI】Apple Watchアプリを開発する方法!iPhoneと連携

※watchOS 7以降はStoryboardを使用したUIの設計は非推奨になったのでSwift UIで開発を行う必要があります。

項目 入力値
Product Name アプリ名
Team チーム名
Organization identifier(組織ID) アプリ名を元に自動で入力
Bundle Identifier(アプリ識別ID) アプリ名を元に自動で入力

プロジェクトが作成されるとXcodeのワークスペースが開きます。Swift UIで実装されたデモアプリが表示されるのでここから自分の作りたいアプリを開発していけばOKです。iOSアプリと開発方法は基本的に変わりませんが、ボタンをタップして画面遷移を実行するアプリを実装してみます。

【SwiftUI】Apple Watchアプリを開発する方法!iPhoneと連携

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationStack {
            NavigationLink {
                ChildView()
            } label: {
                Text("ChildViewへ")
                  .padding()
            }
        }
    }
}

#Preview {
    ContentView()
}

import SwiftUI

struct ChildView: View {
    var body: some View {
        VStack {
            Image (systemName:"globe")
                .imageScale(.large)
                .foregroundStyle(.tint)
            Text ("Hello, world!")
                .padding()
        }.padding()
    }
}

#Preview {
    ChildView()
}

ボタンをクリックすると画面が遷移するアプリが実装できました。Apple Watchもシミュレーターでも動作を確認できるので実際にデバイスを持っていなくても開発自体を進めることが可能です。

【SwiftUI】Apple Watchアプリを開発する方法!iPhoneと連携

iPhoneアプリと連携させるアプリの開発方法

続いてiPhone(iOS)アプリと連携して利用できるwatchOSアプリを作成してみます。連携できることを確かめるにはBluetoothでペアリングした状態のiPhoneとApple Watchが必要になります。実機であれば最初にペアリングをしておいてください。実機がない場合はシミュレーターでも動作確認ができるので「こちら」を参考にしてください。

実際に実装していく流れは以下の通りになります。セクションを区切って要件をわかりやすくするために、読みにくくなってしまいましたがご容赦ください。

流れ

  1. 連携プロジェクトの作成
  2. Apple WatchとiPhoneの接続を確認する
  3. Watchからデータを送信する

連携プロジェクトの作成

連携アプリを開発するためにはプロジェクト作成の際に「Watch App with New Companion iOS App」にチェックを入れて進みます。アプリ名を「TestLinkWatch」にしたせいでややこしくなってしまいましたがプロジェクトの中に2つのターゲット「TestLinkWatch(iOSアプリ用)」と「TestLinkWatch Watch(watchOSアプリ用)」があるのを確認できます。

【SwiftUI】Apple Watchアプリを開発する方法!iPhoneと連携

この場合Bundle IDはiOSアプリ側がTestLinkWatch、watchOS側がTestLinkWatch.watchkitappになります。接尾辞に自動でwatchkitappが付与されるようです。

Apple WatchとiPhoneの接続を確認する

まずはApple WatchとiPhoneが正常に接続できているかを確かめます。Apple Watchアプリ開発にはWatchConnectivityフレームワークを利用して機能を実装していきます。まずはiOS側にWatch側と接続するためのコードを実装します。WCSessionクラスが実際に通信を開始するためのオブジェクトになります。


import UIKit
import WatchConnectivity

class WatchConnectViewModel: NSObject {
    
    var session: WCSession
    init(session: WCSession = .default) {
        self.session = session
        super.init()
        if WCSession.isSupported() {
            self.session.delegate = self
            self.session.activate()
        }
    }
}

extension WatchConnectViewModel: WCSessionDelegate {
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        if let error = error {
            print(error.localizedDescription)
        } else {
            print("セッション:アクティベート")
        }
    }
    
    func sessionDidBecomeInactive(_ session: WCSession) { }

    func sessionDidDeactivate(_ session: WCSession) { }
}

Watch側にも同じようなクラスを作成しておきます。


import WatchConnectivity

class iOSConnectViewModel: NSObject {

    var session: WCSession
    init(session: WCSession = .default) {
        self.session = session
        super.init()
        if WCSession.isSupported() {
            self.session.delegate = self
            self.session.activate()
        }
    }
}

extension iOSConnectViewModel: WCSessionDelegate {
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        if let error = error {
            print(error.localizedDescription)
        } else {
            print("セッション:アクティベート")
        }
    }
}

作成したそれぞれのクラスは各ContentView側でインスタンス化しておきます。


struct ContentView: View {
    
    private let viewModel = WatchConnectViewModel()
    
    @State  var isConnect = false
    
    var body: some View {
        VStack {
            Text(isConnect ? "Connect" : "No Connect")
            
            Button {
                isConnect = viewModel.session.isReachable
            } label: {
                Text("Check")
            }
        }.padding()
    }
}

struct ContentView: View {

    private let viewModel = iOSConnectViewModel()
    @State  var isConnect = false
    
    var body: some View {
        VStack {
            Text(isConnect ? "Connect" : "No Connect")
            
            Button {
                isConnect = viewModel.session.isReachable
            } label: {
                Text("Check")
            }
        }.padding()
    }
}

アプリを起動して、ボタンをそれぞれタップするとペアリングが成功していれば「Connect」、未接続であれば「No Connect」が表示されるようになります。

【SwiftUI】Apple Watchアプリを開発する方法!iPhoneと連携

Watchからデータを送信する

Watch側からデータを送信しiPhone側で表示させるように実装していきます。まずはWatch側からデータを送信する処理を実装します。sendMessageメソッドは辞書型でデータをiPhone側へ送信します。


func send(lang: String) {
    let langDic: [String: String] = ["lang": lang]
    self.session.sendMessage(langDic) { _ in }
}

UI側でもデータを送信処理を実装しておきます。


struct ContentView: View {
    
    private let langs = ["Swift", "Kotlin", "Objective-C", "Java", "Dart"]
    private let viewModel = iOSConnectViewModel()
    
    var body: some View {
        List(langs.indices, id:\.self) { index in
            Button {
                viewModel.send(lang: langs[index])
            } label: {
                Text(langs[index])
            }
        }
    }
}

iOS側ではsendMessageメソッドで送信されたデータを受け取るデリゲートメソッドを実装していきます。またlangsプロパティを追加しObservableObjectを継承してUIに反映させられるようにしていきます。


class WatchConnectViewModel: NSObject, ObservableObject {
    
    @Published  var langs: [String] = []
    
    var session: WCSession
    init(session: WCSession = .default) {
        self.session = session
        super.init()
        if WCSession.isSupported() {
            self.session.delegate = self
            self.session.activate()
        }
    }
}

extension WatchConnectViewModel: WCSessionDelegate {
    
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        if let error = error {
            print(error.localizedDescription)
        } else {
            print("セッション:アクティベート")
        }
    }
    
    func sessionDidBecomeInactive(_ session: WCSession) { }
    
    func sessionDidDeactivate(_ session: WCSession) { }
    
    /// sendMessageメソッドで送信されたデータを受け取るデリゲートメソッド
    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
        guard let lang = message["lang"] as? String else {
            return
        }
        self.langs.append(lang)
    }
}

iOS側のビューで表示させれば完了です。


struct ContentView: View {
    
    @ObservedObject  var viewModel = WatchConnectViewModel()
    
    @State  var isConnect = false
    
    var body: some View {
        VStack {
            Text(isConnect ? "Connect" : "No Connect")
            
            Button {
                isConnect = viewModel.session.isReachable
            } label: {
                Text("Check")
            }
            Text("\(viewModel.langs.count)")
            List(viewModel.langs.indices, id:\.self) { index in
                Text(viewModel.langs[index])
            }
        }.padding()
    }
}

WatchのボタンをクリックするとiPhoneでタップされた値が表示されていくようになります。

【SwiftUI】Apple Watchアプリを開発する方法!iPhoneと連携

iOS側からデータを送る

Watch側からデータを送信した実装と同じ方法でiOS側からWatch側へデータを送信することも可能です。その場合はWatchConnectViewModel側にsendメソッドを実装してiOSConnectViewModel側にデリゲートメソッドを実装すればOKです。^


func send(lang: String) {
    let langDic: [String: String] = ["lang": lang]
    self.session.sendMessage(langDic) { _ in }
}

 /// sendMessageメソッドで送信されたデータを受け取るデリゲートメソッド
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
    guard let lang = message["lang"] as? String else {
        return
    }
    print("iOSから受け取ったよ")
    print(lang)
}

シミュレーターでiPhoneとApple Watchをペアリングする

ペアリングさせたシミュレーターを用意するためにはXcodeの「シミュレーター」>「Manage Run Destinations...」をクリックして「」をクリックします。新規でデバイスを追加できるようになるので「Paired Apple Watch」にチェックを入れてiPhoneとApple Watchのシミュレーターを追加するだけです。

【SwiftUI】Apple Watchアプリを開発する方法!iPhoneと連携

ビルドする際も両方ともペアリングシミュレーターを起動させるだけでペアリングされた状態になり、動作確認をすることができるようになります。

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

searchbox

スポンサー

ProFile

ame

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

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

New Article

index