カテゴリー: iOS

iOS14のWidget機能を使ってみた。

はじめに

こんにちはsuzukiです。WWDCが初めてオンラインで開催されましたね。今回のブログでは、WWDCで発表されたiOS14のWidget機能について、簡単に触れていこうと思います。内容としてはXcodeのインストールからプロジェクトにWidget Extensionを追加し、テキストの表示までを行っていこうと思います。
環境
OS:Catlina 10.15.4
Xcode:12.0 beta (12A6159)

Xcode 12 betaの入れ方

AppleDeveloperでダウンロードできます。
・Xcode 12 beta
・Xcode 12 for macOS Universal Apps beta (Apple Silicon Macで動作するmacOSアプリの開発用)
のふたつがダウンロードできますが、iOSアプリを作成するには上のXcode 12 betaをダウンロードしてください。
ダウンロードファイルの容量が、10Gほどあるので結構時間がかかります。
解凍後のバイナリは20Gぐらいでした。

WidgetExtensionの追加方法

widgetを使用するには、TargetからWidgetExtensionを追加します。
プロジェクトの作成の際に
Xcode 12 betaでLife Cycleの項目が追加されました。SwiftUI AppとUIKit App Delegateのどちらかを選択してください。
WidgetExtensionの追加は、下記のスクショのようにFile>New>Targetと選択します。

検索窓にwidgetと入力しNextをタップする。

Include Configuration Intentのチェックを外す。
※理由は後述しますが、widgetがうまく機能しない場合があります。

無事追加されると下記のようなファイルが追加されます。

今回の内容ではwidget_extension.swiftを変更します

デフォルトのコード説明

追加されたコードを見ると下記のように記述されています。

import WidgetKit
import SwiftUI

struct Provider: TimelineProvider {
    public typealias Entry = SimpleEntry

    public func snapshot(with context: Context, completion: @escaping (SimpleEntry) -> ()) {
        let entry = SimpleEntry(date: Date())
        completion(entry)
    }

    public func timeline(with context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
        var entries: [SimpleEntry] = []

        // Generate a timeline consisting of five entries an hour apart, starting from the current date.
        let currentDate = Date()
        for hourOffset in 0 ..< 5 {
            let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
            let entry = SimpleEntry(date: entryDate)
            entries.append(entry)
        }

        let timeline = Timeline(entries: entries, policy: .atEnd)
        completion(timeline)
    }
}

struct SimpleEntry: TimelineEntry {
    public let date: Date
}

struct PlaceholderView : View {
    var body: some View {
        Text("Placeholder View")
    }
}

struct widget_extensionEntryView : View {
    var entry: Provider.Entry

    var body: some View {
        Text(entry.date, style: .time)
    }
}

@main
struct widget_extension: Widget {
    private let kind: String = "widget_extension"

    public var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider(), placeholder: PlaceholderView()) { entry in
            widget_extensionEntryView(entry: entry)
        }
        .configurationDisplayName("My Widget")
        .description("This is an example widget.")
    }
}

時刻が表示されれば成功です。ひとまず簡単な説明をしていこうと思います。
ほとんどAppleDeveloperで説明されている内容の翻訳になります。
WWDCのWidget関係と発表内容のコードも確認してください。

Configurationについて

プロジェクトに追加した際のInclude Configuration Intentによってデフォルトの内容が変わります。
https://developer.apple.com/forums/thread/650831の記事でもあるのですが、シミュレーターでうごかないことがあるみたいです。簡単に試す場合はInclude Configuration Intentのチェックは外して試すことをおすすめします。
StaticConfiguration:
 Include Configuration Intentチェックなしで追加される。
 ユーザー設定可能なプロパティを持たないwidgetが作成される。
IntentConfiguration:
 Include Configuration Intentチェックなしで追加される。
 ユーザー設定可能なプロパティを持つwidgetが作成される。
 SiriKitカスタムインテントを使用します。

プロバイダー

widgetがいつレンダリングするかをWidgetKitに伝えるタイムラインを生成します。
更新に必要な情報といつ行うかを設定を行います。

snapshot

WidgetKitは、ウィジェットが一時的な状況で表示されるときに、snapshot(for:with:completion :)を呼び出します。
widget galleryで表示される際に、完了ハンドラをできるだけ早く呼び出します。ウィジェットの現在の状態を取得または計算するのに数秒以上かかる場合は、サンプルデータを提供する可能性があります。
(widget galleryがシミュレーターで確認できておりません)

Timeline

タイムラインには、タイムラインエントリオブジェクトの配列と更新ポリシーが含まれています。
・タイムラインエントリの作成
TimelineEntryに準拠するカスタムタイプを宣言します。
デフォルトで作成されたエントリでは最低限必要な、WidgetKitがウィジェットのビューを更新する日付のみ設定されています。ウィジェットがビューをレンダリングするために必要な情報を設定できます。
・タイムラインの更新ポリシー
WidgetKitがプロバイダーに新しいタイムラインを要求する最も早い日付を指定します。

・.atEnd
デフォルトの更新ポリシー指定したタイムラインエントリの配列の最後の日付の後に新しいタイムラインを要求するようにWidgetKitに指示します。その他に下記の項目も設定できますが、after()は想定した動きになりませんでした。

・.after(_ date: Date)
デフォルトの日付より前または後の別の日付を示すことができます。タイムラインを変更する可能性があるタイムラインエントリの終了前の特定の時点があることがわかっている場合は、より早い日付を指定します。逆に、最終日以降、タイムラインが一定期間変更されないことがわかっている場合は、後日を指定します。

・.never
新しいタイムラインをまったく要求しないようにWidgetKitに指示します。その場合、アプリはWidgetCenterを使用してWidgetKitに新しいタイムラインをリクエストするように要求します。

さいごに

最後までありがとうございました。次回は内容を少し変更していこうと思います。
Today Extensionが進化して今回の機能が実装されるのかと思っていたのですが、全く新しい機能でした。
Today Extensionはインターフェースが非推奨になっていたので、更新がありそうです。

おすすめ書籍

suzuki

シェア
執筆者:
suzuki
タグ: SwiftUIWidget

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

3週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

1か月 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前