watchOS2の新機能 complication詳細解説


watchOS2の新しい機能であるcomplicationについてまとめました。

なお、ここで書いている内容はデベロッパープログラムに登録していなくても閲覧可能なドキュメント、WWDCのセッションを元に書いています。また、画像はすべてセッションのPDFのものを使用しています。


## complication

complicationはwatchOS2で追加された機能で、Apple Watchのフェイスにアプリのデータを表示できるようにする機能です。

![スクリーンショット 2015-07-14 19.36.33.png](https://qiita-image-store.s3.amazonaws.com/0/45525/cb32de62-800a-2057-bd06-8036a739665f.png "スクリーンショット 2015-07-14 19.36.33.png")


watchOS2からTime Travelという機能が追加され、時系列ごとの情報の表示もできます。また、complicationの表示領域をタップするとWatch appへと遷移するようになっています。
### 参考資料

[watchOS 2 Transition Guide](https://developer.apple.com/library/prerelease/watchos/documentation/General/Conceptual/AppleWatch2TransitionGuide/DesigningaComplication.html#//apple_ref/doc/uid/TP40015234-CH11-SW1)と[Creating Complications with ClockKit](https://developer.apple.com/videos/wwdc/2015/?id=209)を参考にしました。

## Complication Families

complicationの表示のタイプは大きく分けると5つになります。

### モジュラーで表示されるModular SmallとModular Large

#### Modular large
![スクリーンショット 2015-07-14 19.44.33.png](https://qiita-image-store.s3.amazonaws.com/0/45525/6ae84286-aa49-97d5-df2b-729ec2a4a909.png "スクリーンショット 2015-07-14 19.44.33.png")
Modular largeはcomplicationの中でも最大の表示領域を持っています。開発者側からするとこれを使ってほしいですね 笑

#### Modular small
![スクリーンショット 2015-07-14 19.44.49.png](https://qiita-image-store.s3.amazonaws.com/0/45525/e1379999-0356-6752-76e8-71a757e00ebd.png "スクリーンショット 2015-07-14 19.44.49.png")
モジュラーにはModular smallも用意されています。こちらはあまり情報の表示ができません


### ユーティリティで表示できるUtilitarian smallとUtilitarian large

#### Utilitarian small
![スクリーンショット 2015-07-14 19.49.46.png](https://qiita-image-store.s3.amazonaws.com/0/45525/dc9a9ac4-e36e-436e-d4a5-fdad0852d7f4.png "スクリーンショット 2015-07-14 19.49.46.png")
ユーティリティで表示できるUtilitarian smallは少し長めの文字を表示できるようになっています（長めといってもApple Watchの画面なのでそこまで多くは表示できません）。

#### Utilitarian large
![スクリーンショット 2015-07-14 19.51.57.png](https://qiita-image-store.s3.amazonaws.com/0/45525/0dcba7da-9320-1056-6832-c59137a0bdba.png "スクリーンショット 2015-07-14 19.51.57.png")

Utilitarian largeはUtilitarian smallを横に長くしたものです。



### 丸い時計盤で表示されるCircular small

#### Circular Small
![スクリーンショット 2015-07-14 19.47.46.png](https://qiita-image-store.s3.amazonaws.com/0/45525/0b68ebb2-a977-178a-6f18-cc41538b7b32.png "スクリーンショット 2015-07-14 19.47.46.png")

最後にCircular smallです。丸い時計盤の四隅に表示されます。非常に小さな情報しか表示できません。


### 注意
なお、*Complication Familiesすべてをサポートする必要はありません。* 自分のアプリに必要な形式だけをサポートするようにすればOKです。

## Complication Template

Complication Familyそれぞれにいくつかのテンプレートが用意されています。

![スクリーンショット 2015-07-14 19.55.38.png](https://qiita-image-store.s3.amazonaws.com/0/45525/2c8e8487-ddb4-dbfd-f855-ea05e54fb3d3.png "スクリーンショット 2015-07-14 19.55.38.png")

Utilitarian largeについては一種類しかありませんが、ほかのcomplicationは複数のレイアウトが用意されています。

これらのレイアウトのためにCLKComplicationTemplateクラスを継承したクラスがたくさん用意されています。いまのところ[22クラスありました](https://developer.apple.com/library/prerelease/watchos/documentation/ClockKit/Reference/CLKComplicationTemplate_class/index.html)。


### `CLKTextProvider`, `CLKImageProvider`

CLKComplicationTemplateを継承したクラスに画像とかテキストデータを入れる場合はCLKTextProviderとかCLKImageProviderに入れる必要があります。

![スクリーンショット 2015-07-14 20.01.52.png](https://qiita-image-store.s3.amazonaws.com/0/45525/78a2f5c5-47df-b3a6-3e35-cbdee295ea87.png "スクリーンショット 2015-07-14 20.01.52.png")


`CLKCompliationTemplate`を継承したクラスを作成し、そのクラスが持っているプロパティに必要なデータを入れることでcompliationの表示を行います。

この際、画像やテキストを入れることになりますが`NSString`や`UIImage`を直接使うのではなく、`CLKTextProvider`, `CLKImageProvider`に変換する必要があります。

例えば、Modular largeのテンプレートを作成する際は以下のようになります。

```swift
    let modularLage = CLKComplicationTemplateModularLargeStandardBody()
    modularLage.headerTextProvider = CLKSimpleTextProvider(text: "Long Text")
    modularLage.body1TextProvider = CLKSimpleTextProvider(text: "Body Text 1")
    modularLage.body2TextProvider = CLKSimpleTextProvider(text: "Body Text 2")
    modularLage.headerImageProvider = CLKImageProvider(backgroundImage: UIImage(named: "image"), backgroundColor: nil)
```

## `CLKComplicationDataSource`

complicationでデータを表示するには`CLKComplicationDataSource`プロトコルに適合したクラスを作ってデータを返す必要があります。このクラスは、watchOS2のターゲットを作成する際に、complicationに対応するかのチェックを入れれば、自動的に作成されます。

```ComplicationController.swift
class ComplicationController: NSObject, CLKComplicationDataSource {
  // 中略
}
```

このクラスの役割は、データをcomplicationで表示するのに適した形に変更することです。

### `getSupportedTimeTravelDirectionsForComplication:withHandler`

このメソッドの役割はTimeTravelで未来のデータをサポートするか、過去のデータをサポートするかなどを決めることです。

Time Travelで過去のデータの表示、未来のデータの表示をサポートする場合は以下のように書きます。

```swift
    func getSupportedTimeTravelDirectionsForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimeTravelDirections) -> Void) {
        handler([CLKComplicationTimeTravelDirections.Forward, CLKComplicationTimeTravelDirections.Backward])
    }
```

逆にTime Travelをサポートしないようにするには以下のように書きます。

```swift
    func getSupportedTimeTravelDirectionsForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationTimeTravelDirections) -> Void) {
        handler(CLKComplicationTimeTravelDirections.None)
    }
```


### getTimelineStartDateForComplication:withHandler:とgetTimelineEndDateForComplication:withHandler:でTimeTravel

この2つのメソッドはTimeTravelで表示サポートする時間帯を決めるために使用します。

以下のコードは現在の時間から前後12時間をTime Travelでサポートする際のコードです。

```swift
    func getTimelineStartDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
        let time = NSDate(timeIntervalSinceNow: NSTimeInterval(-60*60*12))
        handler(time)
    }
    
    func getTimelineEndDateForComplication(complication: CLKComplication, withHandler handler: (NSDate?) -> Void) {
        let time = NSDate(timeIntervalSinceNow: NSTimeInterval(60*60*12))
        handler(time)
    }
```

デジタルクラウンで範囲外の時間になった場合はcomplicationの表示が暗転します。


### `getPrivacyBehaviorForComplication:withHandler:`

プライバシー的に表示したくないデータの場合がある場合に表示します。

プライバシーを考慮しなくても良いデータであれば以下のように。
```swift
    func getPrivacyBehaviorForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationPrivacyBehavior) -> Void) {
        handler(CLKComplicationPrivacyBehavior.ShowOnLockScreen)
    }
```

プライバシーを考慮すべきデータであれば以下のように設定します。

```swift
    func getPrivacyBehaviorForComplication(complication: CLKComplication, withHandler handler: (CLKComplicationPrivacyBehavior) -> Void) {
        handler(CLKComplicationPrivacyBehavior.HideOnLockScreen)
    }
```

### `getCurrentTimelineEntryForComplication:withHandler`, `getTimelineEntriesForComplication:beforeDate:limit:withHandler:`,`getTimelineEntriesForComplication:afterDate:limit:withHandler:`

これらのメソッドを使用して、時間ごとの表示するデータを返します。complicationに表示するデータはこの3つのメソッドを使って用意します。

現在の時間データを返す場合のサンプルコードです。

```swift
    func getCurrentTimelineEntryForComplication(complication: CLKComplication, withHandler handler: ((CLKComplicationTimelineEntry?) -> Void)) {
        
        var entry : CLKComplicationTimelineEntry?
        let now = NSDate()
        
        switch complication.family {
            
        case .ModularSmall:
            
            let textTemplate = CLKComplicationTemplateModularSmallSimpleText()
            textTemplate.textProvider = CLKSimpleTextProvider(text: "Long Text")
            
            entry = CLKComplicationTimelineEntry(date: now, complicationTemplate: textTemplate)
            handler(entry)
        }
        
        handler(nil)
    }
```

### `getPlaceholderTemplateForComplication:withHandler`

complication設定時に表示されるデータは`getPlaceholderTemplateForComplication:withHandler`を使って設定します。

### 注意点

[watchOS 2 Transition Guide](https://developer.apple.com/library/prerelease/watchos/documentation/General/Conceptual/AppleWatch2TransitionGuide/DesigningaComplication.html#//apple_ref/doc/uid/TP40015234-CH11-SW1)には以下のようなことが書かれていました。

* データソースのメソッドは最小限にとどめておくこと
* 通信してデータを取ってくるとか計算とかデータの転送が遅れるようなことはしない 
* データの取得や計算が必要な場合はiOSアプリ側で行ったり、WatchKit extensionの別の場所で行い、それをcomplicationからアクセス可能な場所にキャッシュさせて使用すること
* データソースのメソッドではキャッシュしたデータを取ってきて、ClockKitの要求するフォーマットに設定するだけにする

要するに *データソースは基本的にキャッシュさせたデータを読み込ませるように設計しよう* ということですね。

## まとめ

最初は微妙な機能かなと思っていたのですが、ちゃんと調べたり検証したりしているうちに色々と使いどころのある機能だなと感じました。

ただし表示できる情報の量に制限があり、表示するデータに時間の情報が必要なのでアプリの機能次第なのかなとも感じました。

ただ、Apple Watchで一番使用する機能は *時計* だと思うので、そこにデータを表示できるのはかなり強みだと感じています。