iOS14から、ウィジェットをホーム画面に配置することが可能になりました。
ウィジェットとはホーム画面に簡単な情報をミニサイズで表示する機能で、アプリを開くことなく情報をチェックすることができます。
こんな便利な機能を使わないのはもったいないので、今回試してみました。
開発環境
Xcode 13.2.1
iOS:15.2
Xcodeで新規プロジェクトを作成する
Create a new Xcode Projectを選択します。

今回はSingle View Applicationのプロジェクトを作成し、Product Nameに任意の名前を指定(ここでは「researchWidget」)します。


Widget Extensionを追加する
File -> New -> Target -> Widget Extensionを選択します。

追加するtargetのProduct Nameに任意の名前を入力し(本件では「widget」を指定)、最後にFinishボタンを押します。

これで、Widget機能に関係するフォルダが作成されます。
作成後、デフォルトのウィジェットが追加されているかと思います。

追加したWidgetターゲットを選択し、ビルド&実行してみましょう。

時間を表示するウィジェットが表示されるかと思います。

Widgetファイルのコードの内容の確認
import WidgetKit
import SwiftUI
import Intents
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), configuration: ConfigurationIntent())
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), configuration: configuration)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in 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, configuration: configuration)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
ProviderはWidgetの管理コアであり、TimelineProviderまたはIntentTimelineProviderプロトコルに準拠しています。
- placeholder:デフォルトのビューを表示するために使用します。例えば、データ取得できない場合は、デフォルトのインターフェイスを表示します。
- getSnapshot:プレビュービューを表示するために使用します。例えば、ユーザーがWidget Galleryでサンプルデータを指定して表示します。
- getTimeline:実際のウィジェット表示に使用します。
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
}
struct widgetEntryView : View {
var entry: Provider.Entry
var body: some View {
Text(entry.date, style: .time)
}
}
widgetEntryView: WidgetのUI作成に使用します。
@main
struct widget: Widget {
let kind: String = "widget"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
widgetEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
struct widget_Previews: PreviewProvider {
static var previews: some View {
widgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
widget: Widgetの表示名と説明の設定に使用します。ウィジェットを編集するときに表示します。

WidgetのUIをカスタマイズする
SwiftUIで簡単にwidgetEntryViewの内容を実装してみました。
struct widgetEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack(alignment: .leading, spacing: 0) {
Text("Widget")
.font(.title)
.bold()
HStack(alignment: .lastTextBaseline){
Text("試してみた")
.font(.system(size: 14))
.bold()
Spacer()
Spacer()
VStack(alignment: .trailing){
Image("sakura")
.resizable()
.frame(width: 40, height: 40, alignment: .center)
}
}
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .leading)
.padding()
.background(
Image("bg")
.resizable()
.scaledToFill()
)
.widgetURL(URL(string: "example://widget_deeplink"))
}
}

Widgetのタップイベント
Widgetをタップしたら、デフォルトは対応のアプリへ遷移するのですが、deep linkも使えます。
.widgetURL
修飾子でURLがアプリでハンドリングできるようになります。
)
.widgetURL(URL(string: "example://widget_deeplink"))
Swiftで開発したアプリの場合、SceneDelegateのopenURLContextsでURLを受け取ることが可能です。
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
if let url = URLContexts.first?.url {
print(url)
}
}
}
さいごに
今回のWidget機能を試してみて、タップすることでアプリへ素早く遷移できるのが非常に便利だと感じました。
ぜひ皆さんも試してください。