SurfTideΔとSurfTideWatchに、AppleWatchのコンプリケーションを実装したときのメモです。自分用の備忘録なので、それほど親切には書いていませんが、ネットの情報の不足分を補完する感じです。
タイド情報(潮汐)は常に変化するので、以下のような条件でシンプルに実装しています。
- 一定時間で更新する必要がある
- 毎回タイドを計算するのは冗長なので当日と前後1日分のデータはキャッシュする
- 複数の種類のコンプリケーションを準備する
で、まずはXCODEプロジェクトのWatchExtensionに、コンプリケーションを利用するよう設定します。
というのも、自分の実装では「ComplicationController.swift」関連の設定が無かったり(古いプロジェクト)自分で削除していたりしたので、まずはそれを正しく構成しなければなりませんでした。
まずは「Info.plist」にコンプリケーション関連の設定を追加。
必要なKeyは以下2つです。「CLKComplicationPrincipalClass」は「ComplicationController.swift」のパスを指定します。「CLKComplicationSupportedFamilies」は使うタイプのComplicationsだけ設定します。
<key>CLKComplicationPrincipalClass</key> <string>$(PRODUCT_MODULE_NAME).ComplicationController</string> <key>CLKComplicationSupportedFamilies</key> <array> <string>CLKComplicationFamilyModularSmall</string> <string>CLKComplicationFamilyUtilitarianSmall</string> <string>CLKComplicationFamilyUtilitarianSmallFlat</string> <string>CLKComplicationFamilyUtilitarianLarge</string> <string>CLKComplicationFamilyCircularSmall</string> <string>CLKComplicationFamilyExtraLarge</string> <string>CLKComplicationFamilyGraphicCorner</string> <string>CLKComplicationFamilyGraphicBezel</string> <string>CLKComplicationFamilyGraphicCircular</string> </array>
次に「ComplicationController.swift」を用意します。
これはネットの情報は錯綜(海外情報も)してて、古いバージョンやらなんやらで、結局何が必要なのかイマイチでした。
で、自分で実装したのは以下2のメソッドだけです。
ちなみに、最新のXCODEが作ってくれるテンプレートもイマイチ??で使いませんでした。そもそもWatchOS7でしか利用できないようでしたしね。
- getLocalizableSampleTemplate(設定するときのプレ表示用)
- getCurrentTimelineEntry(実際の表示用)
端折ってますがこんな感じです。実際は「getTemplate」の中で、「Info.plist」で設定した分のコンプリケーションテンプレート(CLKComplicationTemplate)を作ってます。
また、キャッシュするデータもこのクラスのメンバ変数として保存します。自分のアプリの場合、場所が変わった場合や、日付が変わった場合をトリガーにしてキャッシュを書き換えています。
class ComplicationController: NSObject, CLKComplicationDataSource { ・ func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) { setTideData(date: Date()) if let template = getTemplate(complication.family) { handler(template) return } handler(nil) } ・ func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) { let date = Date() setTideData(date: date) if let template = getTemplate(complication.family) { let entry = CLKComplicationTimelineEntry( date: date,complicationTemplate: template) handler(entry) return } handler(nil) } ・ }
次に、各テンプレートが使うアイコンリソースですが、これも必要な分だけ実装します。足りない分は別途ImageSetで追加してもOK。
ちなみに、背景は全て透過で作成します。これを「ComplicationController.swift」の中で各テンプレートに設定します。
最後に、一定時間で更新する処理を「ExtensionDelegate.swift」に記述して完了です。
追加するのは、「applicationDidEnterBackground」メソッドと、handleメッソッドの中の「case let backgroundTask as WKApplicationRefreshBackgroundTask:」にバックグラウンド処理のスケジュール(scheduleBackgroundRefreshTasks)を入れます。
端折ってますがこんな感じです。
func applicationDidEnterBackground() { // コンプリケーション更新用スケジュール scheduleBackgroundRefreshTasks() } func handle(_ backgroundTasks: Set) { // Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one. for task in backgroundTasks { // Use a switch statement to check the task type switch task { case let backgroundTask as WKApplicationRefreshBackgroundTask: // Schedule the next background update. self.scheduleBackgroundRefreshTasks() // Be sure to complete the background task once you're done. // ↓scheduleBackgroundRefreshTasks を有効にする場合は true に変更 backgroundTask.setTaskCompletedWithSnapshot(true) ・ ・ func scheduleBackgroundRefreshTasks() { let watchExtension = WKExtension.shared() let targetDate = Date().addingTimeInterval(15.0 * 60.0) watchExtension.scheduleBackgroundRefresh(withPreferredDate: targetDate, userInfo: nil) { (error) in // Check for errors. if let error = error { print("*** An Background refresh error occurred: \(error.localizedDescription) ***") return } // コンプリケーションデータを更新 let server = CLKComplicationServer.sharedInstance() for complication in server.activeComplications ?? [] { server.reloadTimeline(for: complication) } } }
これはどの情報をみても15分に1度みたいだったので、そのタイミングがコンプリケーションのミニマム更新時間みたいです。もちろん、他のアクションから強制更新はできますが、1日に更新できる回数は決まっているみたいです。
Comments