Widget Extensionを作る

Published by @SoNiceInfo at 7/5/2020


Widgetを作成するとアプリコンテンツをiOSのホーム画面やmacOSの通知センターに表示することができるようになります。

WWDC20で発表されたWidget Extension作成に必要な知識と作成方法を紹介する記事のThumbnail画像

Widget作成に必要な知識

Widgetの大きさ

Widgetの大きさはsupportedFamiliesで定義され、.systemSmall(2x2サイズ), .systemMedium(4x2サイズ), .systemLarge(4x4サイズ)の3種類があります。

StaticConfigurationとIntentConfiguration

Widgetには大まかに2種類あります。StaticConfigurationIntentConfigurationです。

  • StaticConfiguration: ユーザがWidget上で設定を必要としないものに利用。例えば新着ニュースを表示するWidget。
  • IntentConfiguration: ユーザがWidget上で設定を必要とするものに利用。例えば地域を選択して天気を表示するWidget。

TimelineEntryとTimeline

Widgetはリアルタイムのアップデートをサポートしません。 表示するコンテンツとともに適切な更新間隔を設定してWidgetに提供する必要があります。これを実現するのがTimelineEntryとTimelineの考え方です。
TimelineEntryオブジェクトは以下のようにコンテンツとDate型のdateを持ちます。(今回のコンテンツはInt型のなにかです。) dateにはコンテンツを表示したい日付を格納します。

struct SimpleEntry: TimelineEntry {
    var date: Date
    var int: Int
}

TimelineはTimelineEntryオブジェクトの配列です。
Timelineの更新は最後のTimelineEntryを表示したとき(.atEnd), 指定した時間が経過した後(.after), アプリからWidgetCenterを利用して更新通知されたら更新する(.never)の3種類があります。 TimelineはTimelineProviderに準拠したProviderによってwidgetsに渡されます。

Snapshot

SnapshotはWidgetのプレビュー機能やWidget Galleryで即座にWidgetを表示するために使われます。 SnapshotはひとつのTimelineEntryを受け取ります。 Widgetのコンテンツがサーバーから提供されるまではモック的な値を入れておきます。

Provider

Providerは上記TimelineやSnapshotをWidgetに提供するためのオブジェクトです。
StaticConfigurationの場合、TimelineProviderに準拠したProviderを用意します。
IntentConfigurationの場合、IntentTimelineProviderに準拠したProviderを用意します。

struct Provider: TimelineProvider {
    public typealias Entry = SimpleEntry

    public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date(), int: Int.random(in: 1..<100))
        completion(entry)
    }

    public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {

        // Generate a timeline consisting of five entries an hour apart, starting from the current date.
        let date = Date()
        let refreshDate = Calendar.current.date(byAdding: .minute, value: 1, to: date)!
        print(refreshDate)

        let timeline = Timeline(entries: [SimpleEntry(date: date, int: Int.random(in: 1..<100)), SimpleEntry(date: refreshDate, int: Int.random(in: 1..<100))], policy: .after(refreshDate))
        completion(timeline)
    }
}

Placeholder

PlaceholderはWidgetの大体の見た目を表示します。.redacted(reason: .placeholder)とすることで簡単に実装できます。

kind

kindはそのWidgetを一意に識別するための文字列です。com.example.widgetのようにBundle indentifierのような文字列がいいでしょう。

Widget Extensionを追加する

Widgetは技術的にはアプリに付属するWidget Extensionのことを指します。
File → New → Target...と進みダイアログが表示されたらWidget Extensionを選択します。
IntentConfigurationを使う場合にはInclude Configuration Intentにチェックを入れましょう。
適切なProduct Nameを入力してFinishします。

Activate "PRODUCT_NAME" schemeと聞かれるのでActivateを選択します。
新しいフォルダが作成されPRODUCT_NAME.swiftが作成されています。 これでWidget Extensionを作成する準備は完了です。

Buildする

左上の「Set the active scheme」で作成したWidgetを選択してBuildします。
Xcode 12 betaとiOS SimulatorではIntentConfigurationのWidgetは動きません。修正されるのを待ちましょう。

参考

Creating a Widget Extension | Apple Developer Documentation
Widgets - System Capabilities - iOS - Human Interface Guidelines - Apple Developer


    アプリをリリースしました!

    ToDoアプリ

    iCloudを利用したデバイス間データ共有、ダークモードに対応しています。

    リリースしたToDoアプリのスクリーンショット

    IPアドレス履歴保存アプリ

    取得したIPアドレスを確認、位置情報とともに保存できます。

    リリースしたIPアドレス保存アプリのスクリーンショット