ZhgChg.Li

Apple Watch App 開発|watchOS 5でゼロから作成する手順とコツ

watchOS 5を使い、初めてのApple Watchアプリ開発に挑戦する方必見。基本から実践まで丁寧に解説し、スムーズに完成まで導く具体的な手順を紹介します。

Apple Watch App 開発|watchOS 5でゼロから作成する手順とコツ
本記事は AI による翻訳です。お気づきの点があればお知らせください。

Apple Watchアプリを作ってみよう!(Swift)

watchOS 5 ハンズオンで学ぶApple Watchアプリ開発 入門編

[最新] Apple Watch Series 6 開封&2年間の使用体験レビュー >>>こちらをクリック

前書き:

前回の Apple Watch 入手開箱文 からほぼ3ヶ月が経ち、最近ようやくApple Watchアプリの開発に取り組む機会ができました。

結婚しよう — 最大の結婚準備アプリ

結婚吧 — 最大婚礼準備App

3ヶ月使用した後の感想を補足します:

    1. e-sim(LTE)はまだいつ使うかわからないので、申請も使用もしていません。
    2. よく使う機能:近づくだけでMacのロック解除、手を上げて通知確認、Apple Pay。
    3. 健康リマインダー:3ヶ月過ぎてからはだんだん怠けがちで、通知は見るものの、リングが達成できなくてもあまり気になりません。
    4. サードパーティAppの対応は依然としてあまり良くありません。
    5. 時計の文字盤は気分に合わせて自由に変更でき、新鮮さを増やせます。
    6. より詳細な運動記録:例えば少し遠くまで夕食を買いに行くと、時計が自動で検知して運動記録を取るかどうかを尋ねてきます。

3ヶ月使ってみて、全体的には最初の開封レビューに書かれていた通り、複数の生活の小さなアシスタントのようで、細かいことを助けてくれます。

サードパーティアプリのサポートは依然として非常に悪い

私が実際にApple Watchアプリを開発する前は、なぜApple Watchのアプリはどれも簡素で、使える程度にしか作られていないのか不思議に思っていました。例えばLINE(メッセージが同期されず、更新もされていない)、Messenger(使えるだけ)などです。しかし実際にApple Watchアプリを開発してみて、開発者たちの苦労が分かりました…。

まずは、Apple Watchアプリの位置づけを理解し、シンプルにすること

Apple Watchの位置付け 「iPhoneの代わりではなく、補助ツール」 公式の紹介や公式App、watchOSのAPIもすべてこの方向性です。そのため、サードパーティのAppはシンプルで機能が少ないと感じることがあります(すみません、私が欲張りすぎましたOrz)

我們的A アプリを例に挙げると、店舗検索、コラム閲覧、掲示板、オンライン問い合わせなどの機能があります。オンライン問い合わせは即時性が求められ、迅速な返信が受注のチャンスを高めるため、Apple Watchに搭載する価値があります。一方、店舗検索、コラム閲覧、掲示板は比較的複雑な機能であり、画面に表示できる情報が少なく、即時性も必要ないため、Apple Watchで実装してもあまり意味がありません。

核心のコンセプトは「補助を主とする」ため、すべての機能をApple Watchに移す必要はありません。そもそもユーザーが腕時計だけを身に着けてスマホを持っていない時間は非常に少なく、そのような場合でもユーザーのニーズは重要な機能だけに限られます(例えば、コラム記事の閲覧はすぐに時計で見るほど重要ではありません)。

さあ、始めましょう!

これも私にとって初めてのApple Watchアプリ開発です。内容が十分に深くないかもしれませんが、どうぞご指導ください!!

本記事はiOSアプリ/UIKitの基礎知識がある方にのみ適しています

本記事で使用:iOS ≥ 9、watchOS ≥ 5

iOSプロジェクトにwatchOSターゲットを新規作成する:

File -> New -> Target -> watchOS -> WatchKit App

File -> New -> Target -> watchOS -> WatchKit App

Apple Watch Appは単独でインストールできず、必ずiOS Appに付随する必要があります

新規作成後、ディレクトリはこのようになります:

あなたは2つのTarget項目があることに気づくでしょう。どちらも欠かせません:

  1. WatchKit App: リソースとUI表示を担当
    /Interface.storyboard:iOSと同様に、システムでデフォルト作成されたインターフェースコントローラが含まれる
    /Assets.xcassets:iOSと同様に、使用するリソースを格納
    /info.plist:iOSと同様に、WatchKit Appの関連設定

  2. WatchKit Extension: プログラム呼び出しとロジック処理を担当(*.swift)
    /InterfaceController.swift:デフォルトのインターフェースコントローラープログラム
    /ExtensionDelegate.swift:SwiftのAppDelegateに相当し、Apple Watch Appの起動エントリポイント
    /NotificationController.swift:Apple Watch App上のプッシュ通知表示を処理
    /Assets.xcassets:ここでは使用せず、WatchKit AppのAssets.xcassetsにまとめて配置
    /info.plist:iOSと同様に、WatchKit Extensionの関連設定
    /PushNotificationPayload.apns:プッシュ通知データ。シミュレーターでプッシュ通知機能のテストに使用可能

詳細は後ほど紹介しますので、まずは目次とドキュメントの内容・機能をざっと把握してください。

ビューコントローラー:

Apple WatchではビューコントローラはViewControllerではなくInterfaceControllerと呼ばれます。WatchKit AppのInterface.storyboardでInterface Controller Sceneを見つけることができ、その制御用のコードはWatchKit ExtensionのInterfaceController.swiftに置かれます(iOSと同じ概念です)。

SceneはデフォルトでNotification Controller Sceneと一緒に配置されます(私は少し上に移動して分けます)

SceneはデフォルトでNotification Controller Sceneと一緒に配置されます(私は少し上に移動して分けます)。

右側でInterfaceControllerのタイトル表示文字を設定できます。

タイトルの色はInterface Builder Document/Global hintの設定を使用しており、アプリ全体のスタイルカラーが統一されます。

コンポーネントライブラリ:

複雑なコンポーネントはあまりなく、コンポーネントの機能もシンプルで分かりやすい

複雑なコンポーネントはあまりなく、コンポーネントの機能もシンプルでわかりやすいです。

UI レイアウト:

万丈の高楼はViewから始まる。レイアウト部分はUIKit(iOS)のAuto Layoutや制約、レイヤーを使わず、すべてパラメータでレイアウト設定を行うため、よりシンプルで強力(UIKitのUIStackViewに似ている部分もある)。

すべてのレイアウトはGroupで構成されており、UIKitのUIStackViewに似ていますが、より多くのレイアウトパラメータを設定できます

Groupのパラメータ設定

Groupのパラメータ設定

  1. Layout:内部の子Viewのレイアウト方法を設定します(水平、垂直、レイヤースタック)

  2. Insets:Groupの上下左右の余白を設定します

  3. Spacing:内包されている子View同士の間隔を設定します

  4. Radius:Groupの角丸を設定します。そうです!WatchKitには角丸設定のパラメータが標準で備わっています。

  5. Alignment/Horizontal:水平の配置方法(左、中央、右)を設定します。隣接するビューや外側のラップビューの設定と連動します。

  6. Alignment/Vertical:垂直方向の配置(上、中、下)を設定します。隣接するビューや外側のラップビューの設定と連動します。

  7. Size/Width:Groupのサイズを設定します。3つのモードがあります。「Fixed:幅を指定」、「Size To Fit Content:子Viewの内容サイズに合わせて幅を決定」、「Relative to Container:親Viewのサイズを基準に幅を決める(%や+-の補正値を設定可能)」

  8. Size/Height:Size/Widthと同様に、高さを設定する項目です。

フォント/フォントサイズの設定:

システムのText Stylesを直接適用するか、Customを使用できます(ただし、Customではフォントサイズの設定ができませんでした);そのため、私はSystemを使って各表示ラベルのフォントサイズをカスタマイズしています

実践で学ぶ:Lineのレイアウトを例に

レイアウト部分はiOSほど複雑ではないため、サンプルを使って直接説明します。これで簡単に始められます。Lineのホーム画面のレイアウトを例にします:

WatchKit App/Interface.storyboardでInterface Controller Sceneを見つける:

  1. ページ全体は、iOSアプリ開発で使うUITableViewに相当しますが、Apple Watchアプリでは操作が簡素化され、「WKInterfaceTable」と呼ばれています。
    まずはInterface Controller SceneにTableをドラッグします。

UIKitのUITableViewと同様に、Table本体とCell(Apple WatchではRowと呼ばれます)があります;使用は大幅に簡略化されており、このインターフェース上で直接Cellのデザインとレイアウトを行うことができます!

  1. レイアウト構造を分析し、Rowの表示スタイルを設計する:

左側に角丸のフルサイズImageを配置し、その上にLabelを重ねます。右側は上下2つのブロックに均等に分割し、上部と下部にそれぞれLabelを配置するレイアウトです。

2–1: 左右の2つのブロック構造を作る

2つのGroupを1つのGroupにドラッグし、それぞれのSizeパラメータを設定します:

左側の緑色部分:

Layout設定Overlap,内部の子Viewで未読メッセージLabelのレイヤー重ね表示を行う

Layout設定でOverlapを行い、その中の子Viewに未読メッセージのLabelをレイヤー重ねて表示する。

固定幅と高さ40の正方形

幅と高さを40に固定した正方形を作成する

右側の赤い部分:

Layout設定Vertical,裡面子View要做上下兩個顯示

LayoutをVerticalに設定し、その中の子Viewを上下に2つ表示する

幅の設定は外側を参照し、割合は100%、左側の緑色部分40を差し引く

幅の設定は外側を参照し、割合は100%から左側の緑色部分40を引く

左右コンテナ内のレイアウト:

左側部分:Imageをドラッグし、次にLabelを包むGroupをドラッグして右下に揃える(Groupに背景色を設定し、間隔と角丸を設定)

右側部分:2つのLabelを配置し、1つは左上揃え、もう1つは左下揃えに設定してください。

Rowに名前を付ける(UIKitのUITableViewでCellにidentifierを設定するのと同様):

選択したRow->Identifier->カスタム名を入力

Rowを選択し、Identifierにカスタム名を入力する

Rowの表示スタイルは一種類だけではありませんね?

とても簡単で、Rowを1つドラッグしてTableに入れ(実際に表示するRowのスタイルはプログラムで制御)、Identifierを入力して名前を付けるだけです。

ここではデータがない場合の表示用にもう一つRowを追加しています

ここにデータがない場合の表示用のRowをもう一つ追加します。

レイアウトに関する情報

watchKitのhiddenはスペースを取らないため、インタラクティブな用途に使えます(ログインしていればTableを表示し、ログインしていなければ案内用のLabelを表示)。

レイアウトはここまでで一区切りです。個人のデザインに合わせて変更してください。操作は簡単なので、何度か繰り返し配置や整列パラメータを調整して慣れていきましょう!

プログラム制御部分:

Rowの続きとして、Rowを参照操作するためのClassを作成する必要があります:

class ContactRow:NSObject {
}

class ContactRow:NSObject {
    var id:String?
    @IBOutlet var unReadGroup: WKInterfaceGroup!  // 未読グループ
    @IBOutlet var unReadLabel: WKInterfaceLabel!  // 未読ラベル
    @IBOutlet weak var imageView: WKInterfaceImage!  // 画像ビュー
    @IBOutlet weak var nameLabel: WKInterfaceLabel!  // 名前ラベル
    @IBOutlet weak var timeLabel: WKInterfaceLabel!  // 時間ラベル
}

Outletを接続し、変数を保存する

Table部分も同様にOutletをControllerに接続します:

class InterfaceController: WKInterfaceController {

    @IBOutlet weak var Table: WKInterfaceTable!
    override func awake(withContext context: Any?) {
        super.awake(withContext: context)
        
        // インターフェースオブジェクトの設定をここで行う
    }
    
    override func willActivate() {
        // このメソッドはウォッチのビューコントローラーがユーザーに表示される直前に呼ばれる
        super.willActivate()
    }
    
    struct ContactStruct {
        var name:String
        var image:String
        var time:String
    }
    
    func loadData() {
        // APIコールバックを取得...
        //postData {
        let data:[ContactStruct] = [] // APIから返されたデータ...
        
        self.Table.setNumberOfRows(data.count, withRowType: "ContactRow")
        // 複数のROWを表示する場合は以下を使う:
            //self.Table.setRowTypes(["ContactRow","ContactRow2","ContactRow3"])
        //
        for item in data.enumerated() {
            if let row = self.Table.rowController(at: item.offset) as? ContactRow {
                row.nameLabel.setText(item.element.name)
                // ラベルや画像に値を割り当てる......
            }
        }
        
        //}
    }
    
    override func didDeactivate() {
        // このメソッドはウォッチのビューコントローラーが表示されなくなった時に呼ばれる
        super.didDeactivate()
        loadData()
    }
    
    // Rowが選択された時の処理:
    override func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) {
        guard let row = table.rowController(at: rowIndex) as? ContactRow,let id = row.id else {
            return
        }
        self.pushController(withName: "showDetail", context: id)
    }
}

Tableの操作は大幅に簡略化されており、delegateやdatasourceはありません。データの設定方法は、setNumberOfRowsやsetRowTypesを呼び出して行数とタイプを指定し、その後rowController(at:)を使って各行のデータ内容を設定するだけです!

TableのRow選択イベントも override func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) を実装するだけで操作可能です!(Tableにはこのイベントだけがあります)

ページ切り替え方法は?

まずはInterface ControllerにIdentifierを設定

まずはInterface ControllerにIdentifierを設定します。

watchKitには2種類のページ遷移モードがあります:

  1. iOS UIKit の push に似ています
    self.pushController(withName: Interface Controller Identifier , context: Any? )

push方式は左上に戻るボタンが表示される

push方式は左上に戻るボタンがあります

前のページに戻る(iOS UIKitと同様):self.pop()

ルート画面に戻る:self.popToRootController()

新しいページを開く:self.presentController()

  1. タブ表示方法 WKInterfaceController.reloadRootControllers(withNames: [ Interface Controller Identifier ], contexts: [ Any? ])

あるいはStoryboard上で、最初のページのInterface ControllerをControl+Clickでドラッグして、次のページに「next page」を選択してもよいです。

タブ表示は左右にスワイプしてページを切り替え可能

タブ表示は左右にスワイプしてページを切り替えられます

2つのページ遷移方法は混用できません。

ページ遷移パラメータ?

iOSのようにカスタムdelegateやsegueを使ってパラメータを渡す必要はなく、watchKitのページ遷移でパラメータを渡す方法は、上記のメソッドの contexts にパラメータを入れるだけです。

接続パラメータは InterfaceController の awake(withContext context: Any?) で受け取る

例えば、AページからBページにid:Intを渡して遷移する場合:

Aページ:

self.pushController(withName: "showDetail", context: 100)

Bページ:

override func awake(withContext context: Any?) {
        super.awake(withContext: context)
        guard let id = context as? Int else {
           print("パラメータエラー!")
           self.popToRootController()
           return
        }
        // ここでインターフェースオブジェクトを設定します。
}

プログラムによるコンポーネント制御部分

iOS UIKitと比べてかなり簡略化されており、iOS開発経験があればすぐに慣れるでしょう!
例えば、labelはsetText()に変わります。
p.s. しかもgetTextメソッドがなく、extensionで変数を作るか外部変数に保存するしかありません。

iPhoneとの同期/データ転送

もしiOSの関連Extensionを開発したことがあるなら、無意識にApp GroupsでUserDefaultsを共有する方法を使うでしょう。私も最初はその方法で試してみて、ずっとデータが渡らずに悩みましたが、調べてみるとwatchOS 2以降はこの方法がサポートされていないことが分かりました…。

新しいWatchConnectivity方式を使ってiPhoneとApple Watch間で通信(ソケットのような概念)を行うには、iOS側とwatchOS側の両方で実装が必要です。以下はシングルトンパターンでの実装例です:

スマートフォン側:

import WatchConnectivity

class WatchSessionManager: NSObject, WCSessionDelegate {
    @available(iOS 9.3, *)
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
        // iPhone側のセッションが有効化完了
    }
    
    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
        // iPhone側がWatchから送られたUserInfoを受信
    }
    
    func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Void) {
        // iPhone側がWatchからのメッセージを受信
    }
    
    // 他にdidReceiveMessageData、didReceiveFileもあり、どれを使うかはデータ受信の要件次第
    
    func sendUserInfo() {
        guard let validSession = self.validSession,validSession.isReachable else {
            return
        }
        
        if userDefaultsTransfer?.isTransferring == true {
            userDefaultsTransfer?.cancel()
        }
        
        var list:[String:Any] = [:]
        // UserDefaultsをlistに入れる....
        
        self.userDefaultsTransfer = validSession.transferUserInfo(list)
    }
    
    func sessionReachabilityDidChange(_ session: WCSession) {
        // Watchアプリの接続状態が変わった時(Watchでアプリ起動時/終了時)
        sendUserInfo()
        // 状態変化時に、Watchアプリ起動ならUserDefaultsを同期
    }
    
    func session(_ session: WCSession, didFinish userInfoTransfer: WCSessionUserInfoTransfer, error: Error?) {
        // UserDefaults同期(transferUserInfo)完了
    }
    
    func sessionDidBecomeInactive(_ session: WCSession) {
        
    }
    
    func sessionDidDeactivate(_ session: WCSession) {
        
    }
    
    static let sharedManager = WatchSessionManager()
    private override init() {
        super.init()
    }
    
    private let session: WCSession? = WCSession.isSupported() ? WCSession.default : nil
    private var validSession: WCSession? {
        if let session = session, session.isPaired && session.isWatchAppInstalled {
            return session
        }
        // 有効で接続中、かつWatchアプリがインストールされているセッションを返す
        return nil
    }
    
    func startSession() {
        session?.delegate = self
        session?.activate()
    }
}

WatchConnectivity iPhone側のコード

そして、iOSのAppDelegate.swiftのapplication(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?)内にWatchSessionManager.sharedManager.startSession()を追加し、起動後にセッションを接続します。

時計側:

import WatchConnectivity

class WatchSessionManager: NSObject, WCSessionDelegate {
    func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
    }
    
    func sessionReachabilityDidChange(_ session: WCSession) {
        guard session.isReachable else {
            return
        }
        
    }
    
    func session(_ session: WCSession, didFinish userInfoTransfer: WCSessionUserInfoTransfer, error: Error?) {
        
    }
    
    func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
        DispatchQueue.main.async {
            //UserDefaults:
            //print(userInfo)
        }
    }
    
    static let sharedManager = WatchSessionManager()
    private override init() {
        super.init()
    }
    
    private let session: WCSession? = WCSession.isSupported() ? WCSession.default : nil
    
    func startSession() {
        session?.delegate = self
        session?.activate()
    }
}

WatchConnectivity ウォッチ側のコード

そして、WatchOS Extension/ExtensionDelegate.swiftのapplicationDidFinishLaunching()内に WatchSessionManager.sharedManager.startSession() を追加して、ウォッチアプリ起動後にセッションを接続します。

WatchConnectivity データ転送方法

データ送信には:sendMessage, sendMessageData, transferUserInfo, transferFile
データ受信には:didReceiveMessageData, didReceive, didReceiveMessage
両端の送受信メソッドは同じです。

時計から携帯へのデータ送信は問題なく行えますが、携帯から時計へのデータ送信は時計のアプリが開いている場合に限られます。

watchOSプッシュ通知の処理

プロジェクトディレクトリ下のPushNotificationPayload.apnsがここで役立ちます。これはシミュレーター上でプッシュ通知をテストするためのもので、シミュレーターにWatch Appターゲットをデプロイし、インストールしてアプリを起動すると、このファイルの内容を使ったプッシュ通知を受け取れます。開発者がプッシュ通知機能をより簡単にテストできるようになります。

PushNotificationPayload.apnsを変更/有効化/無効化するには、ターゲットを選択してからEdit Schemeを選んでください

PushNotificationPayload.apnsを変更/有効化/無効化する場合は、Targetを選択してからEdit Schemeを選んでください。

watchOS プッシュ通知処理:

iOSと同様にUNUserNotificationCenterDelegateを実装しますが、watchOSでも同じメソッドを実装します。watchOS ExtensionのExtensionDelegate.swift内で行います。

import WatchKit
import UserNotifications
import WatchConnectivity

class ExtensionDelegate: NSObject, WKExtensionDelegate, UNUserNotificationCenterDelegate {

    func applicationDidFinishLaunching() {
        
        WatchSessionManager.sharedManager.startSession() //前述したWatchConnectivityの接続開始
      
        UNUserNotificationCenter.current().delegate = self //UNUserNotificationCenterのデリゲート設定
        // アプリケーションの最終初期化を行う
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.sound, .alert])
        // iOS同様、この方法でアプリが前景にある時でも通知を表示可能
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        // 通知がタップされた時
        guard let info = response.notification.request.content.userInfo["aps"] as? NSDictionary,let alert = info["alert"] as? Dictionary<String,String>,let data = info["data"] as? Dictionary<String,String> else {
            completionHandler()
            return
        }
        
        // response.actionIdentifierでタップイベントのIdentifierを取得可能
        // デフォルトのタップイベント:UNNotificationDefaultActionIdentifier
        
        if alert["type"] == "new_ask") {
            WKExtension.shared().rootInterfaceController?.pushController(withName: "showDetail", context: 100)
            // 現在のroot interface controllerを取得しpushする
        } else {
           // その他の処理....
           //WKExtension.shared().rootInterfaceController?.presentController(withName: "", context: nil)
            
        }
        
        completionHandler()
    }
}

ExtensionDelegate.swift

watchOSの通知表示は、三種類に分かれます:

  1. static:デフォルトのプッシュ通知の表示方法

iPhoneの通知と連携しており、iOS側でUNUserNotificationCenter.setNotificationCategoriesを実装して通知下部にボタンを追加しています。Apple Watchでもデフォルトで同様に表示されます

スマホのプッシュ通知と連携して、iOS側ではUNUserNotificationCenter.setNotificationCategoriesを実装し、通知の下にボタンを追加しています。Apple Watchでもデフォルトで同様に表示されます。

  1. dynamic:プッシュ通知の表示スタイルを動的に処理(内容の再構成、画像の表示)

  2. interactive:watchOS 5以降でサポート。dynamicに加えて、ボタン操作もサポートしています。

Interface.storyboardのStatic Notification Interface Controller Sceneで通知処理方法を設定可能

Interface.storyboardのStatic Notification Interface Controller Sceneで通知の処理方法を設定できます。

staticは特に説明は不要で、デフォルトの表示方法を使います。ここではまずdynamicについて紹介します。「Has Dynamic Interface」にチェックを入れると「Dynamic Interface」が表示され、ここでカスタムの通知表示方法をデザインできます(Buttonは使用できません):

私のカスタム通知表示デザイン

私のカスタム通知表示デザイン

import WatchKit
import Foundation
import UserNotifications

class NotificationController: WKUserNotificationInterfaceController {

    @IBOutlet var imageView: WKInterfaceImage!
    @IBOutlet var titleLabel: WKInterfaceLabel!
    @IBOutlet var contentLabel: WKInterfaceLabel!
    
    override init() {
        // ここで変数を初期化します。
        super.init()
        self.setTitle("結婚吧") // 右上のタイトルを設定
        // インターフェイスオブジェクトを設定します。
    }

    override func willActivate() {
        // このメソッドはウォッチのビューコントローラーがユーザーに表示される直前に呼ばれます
        super.willActivate()
    }

    override func didDeactivate() {
        // このメソッドはウォッチのビューコントローラーが表示されなくなった時に呼ばれます
        super.didDeactivate()
    }
    
    override func didReceive(_ notification: UNNotification) {
        
        if #available(watchOSApplicationExtension 5.0, *) {
            self.notificationActions = []
            // iOSで実装したUNUserNotificationCenter.setNotificationCategoriesで通知下に追加されたボタンをクリア
        }
        
        guard let info = notification.request.content.userInfo["aps"] as? NSDictionary,let alert = info["alert"] as? Dictionary<String,String> else {
            return
        }
        // 通知情報
        
        self.titleLabel.setText(alert["title"])
        self.contentLabel.setText(alert["body"])
        
        if #available(watchOSApplicationExtension 5.0, *) {
            if alert["type"] == "new_msg" {
              // 新しいメッセージ通知の場合、通知下に返信ボタンを追加
              self.notificationActions = [UNNotificationAction(identifier: "replyAction",title: "回覆", options: [.foreground])]
            } else {
              // その他の場合は閲覧ボタンを追加
              self.notificationActions = [UNNotificationAction(identifier: "openAction",title: "查看", options: [.foreground])]
            }
        }
        
        
        // 通知を表示する必要があるときに呼ばれます。
        // 動的通知インターフェイスを使う場合に実装してください。
        // 動的通知インターフェイスをできるだけ早く設定してください。
        
    }
}

プログラム部分も同様にアウトレットをコントローラーに接続し、機能を実装します。

次にinteractiveについてですが、dynamicと同様ですが、Buttonを追加でき、dynamicと同じClassで制御できます。
私はinteractiveを使っていません。なぜならボタンはプログラムでself.notificationActionsに追加しているからです。違いは以下の通りです:

左はinteractiveを使用、右はself.notificationActionsを使用

左側はinteractiveを使用し、右側はself.notificationActionsを使用しています。

両方の方法ともwatchOS 5以上が必要です。

self.notificationActionsでボタンを追加した場合、ボタンのイベント処理はExtensionDelegate内の userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) で行い、identifierで動作を識別します。

メニュー機能?

コンポーネントライブラリからMenuをドラッグし、さらにメニュー項目Menu Itemをドラッグ、そしてIBActionをコードに接続

コンポーネントライブラリからMenuをドラッグし、次にメニュー項目のMenu Itemをドラッグして、IBActionをコードに接続します。

ページを長押しすると以下が表示されます:

コンテンツ入力?

内蔵のpresentTextInputControllerメソッドを使うだけでOK!

@IBAction func replyBtnClick() {
    guard let target = target else {
        return
    }
    
    self.presentTextInputController(withSuggestions: ["後で返信します","ありがとうございます","ご連絡お待ちしております","はい","OK!"], allowedInputMode: WKTextInputMode.plain) { (results) in
        
        guard let results = results else {
            return
        }
        // 入力値がある場合
        
        let txts = results.filter({ (txt) -> Bool in
            if let txt = txt as? String, txt != "" {
                return true
            } else {
                return false
            }
        }).map({ (txt) -> String in
            return txt as? String ?? ""
        })
        // 入力の事前処理
        
        
        txts.forEach({ (txt) in
            print(txt)
        })
    }
}

まとめ

これを読んでいただきありがとうございます!お疲れ様です!

ここまでで記事は一区切りです。UIレイアウト、プログラム、プッシュ通知、インターフェースの応用について大まかに触れましたが、iOS開発経験があれば非常に習得が早いです。ほとんど同じで、多くのメソッドが簡略化されていて使いやすくなっています。しかし、できることは確かに少なくなっています(例えば、現在Tableでの「もっと読み込む」機能の実装方法はまだわかっていません)。今できることは限られていますが、将来的に公式がもっと多くのAPIを開発者に開放してくれることを願っています❤️❤️❤️

MurMur:

Apple Watch Appのターゲットをウォッチにデプロイするのは本当に遅い — Narcos

Apple Watch App Target をウォッチにデプロイするのは本当に遅い — Narcos

PostZMediumToMarkdown によって Medium から変換されました。

GitHub で編集
この記事を改善
本記事は Medium で初公開
オリジナルを読む
この記事をシェア
リンクをコピー · SNS でシェア
ZhgChgLi
著者

ZhgChgLi

An iOS, web, and automation developer from Taiwan 🇹🇼 who also loves sharing, traveling, and writing.

コメント