2025年に入って、ウィジェットメインで使用するアプリを3つリリースしました。
本記事の最後にアプリを紹介しています。
本記事では、Widgetの実装で得られた知見について纏めてみました。
1. Widgetでできないこと
Widgetの開発を行っていると、開発する前に「知っておきたかった。」ことや「え〜、それできないんだ。」という発見が結構ありました。
そういった経験を紹介できればと思います。
1.1 Widgetは15分に1度の更新のみ
-
現状だと、Widgetは15分に1度の更新のみということです。
リアルタイム性のあるものだと勝手に想像していました。
どのくらいの頻度でWidgetを更新するかはgetTimelineの中で指定します。getTimelineのコード例
func getTimeline(in context: Context, completion: @escaping (Timeline<MarketEntry>) -> Void) { // entriesを生成する処理 // タイムラインを更新 let nextUpdateDate = Calendar.current.date(byAdding: .minute, value: 15, to: Date())! let timeline = Timeline(entries: entries, policy: .after(nextUpdateDate)) completion(timeline) }
1.2 WidgetはUIPasteboardにアクセスできない(コピペが使用できない)
-
Widgetでテキストをコピーできたら便利だなと考え、実装してみました。
WidgetでUIPasteboardにアクセスできないのであれば、「はい、そうですか。」で終わりなのですが、シミュレーターで動作確認するとUIPasteboardにアクセスすることができるため、無事コピーできると勘違いしてしまうのです。
そして実機で確認すると全く動作しません。
どうにかコピーできないかと試行錯誤してみますが、動作はしません。調べてみると同じようなことを言っている人がいます。
https://stackoverflow.com/questions/78670089/cant-use-uipasteboard-on-widget-swift-on-real-device
- この件について、解消方法が存在するのであれば教えていただきたいです。
また、この罠に引っ掛かる人が減れば幸いです。
1.3 WidgetではUserDefaults.standard
からのデータを参照できない
-
アプリ側で
UserDefaults.standard
使用して保存した変数をWidget側で参照したいというケースがあると思います。
Widgetでは、App Groupsで設定したIDのUserDefaults
を使用する必要があります。
App GroupsのUserDefaults
を用意したら、使用方法はUserDefaults.standard
と同じです。
App GroupsはSigning & Capabilitiesから設定できます。let sharedDefaults = UserDefaults(suiteName: "group.com.xxxx.xxxxx")
1.4 UIApplication.shared
はWidgetでは使用できない
- 前提として、Widgetで
UIApplication.shared
を使用することはないです。
使用すると、以下のエラーが発生します。'shared' is unavailable in application extensions for iOS: Use view controller based solutions where appropriate instead.
-
そもそも、なぜこのエラーが発生したのか?
次のコードのように、アプリ側とWidget側で処理を共通化したUtilというファイルを使っていました。
つまり、UtilファイルのTargetにはアプリとWidgeExtensionを指定している状態ということになります。
UIApplication.sharedはWidgetでは使用できないので、当然ビルドエラーとなり上記のエラーが発生します。 - 解消方法
- アプリ側とWidget側で共通化処理のファイルを別々で用意する
- アプリ側とWidget側で両方とも使用する場合は、コメントアウト等で運用方法を記載する
struct Util { static let windowScene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene }
1.5 WidgetでScrollView
は使用できない
2. その他
2.1 インタラクティブなWidgetを作成する
-
Widgetでは、ボタンを使用することができます。
AppIntentに準拠したStructを作成して、Buttonで使用する
struct EqualIntent: AppIntent { static var title: LocalizedStringResource = "Equal" static var description = IntentDescription("Equal the display") func perform() async throws -> some IntentResult { CalculatorState.tappedEqual() return .result() } } // ボタンの呼び出し元 Button(intent: EqualIntent()) { Text("=") .font(.system(size: 24, weight: .bold)) .frame(maxWidth: .infinity, maxHeight: .infinity) .foregroundColor(.white) } .background(RoundedRectangle(cornerRadius: buttonNumberRadius).foregroundStyle(.orange))

2.2 WidgetのButtonのBackgroundを消す方法
-
WidgetでButtonを使用すると、自動で青色のBackgroundが付きます。
ButtonStyleを付与する必要がある。普通にButtonにModifierを付与するのでは、解消できません。ButtonStyleのコード例
struct ClearBackgroundStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label .background(Color.clear) } }
まとめ
もっと重要なことがあった気がしたのですが、忘れちゃいました(笑)
今後は発見し次第、どこかに纏めておこうと思います。
以下、リリースしたアプリです。
海外の株式市場にズブズブに浸かっている人は、MarketTimeZenを使ってみて欲しいです。
1. Reawake
iPhoneからMacBookの電池残量を確認できるアプリ。
(リリースから修正していないので、動作が怪しいかも…)
2. MarketTimeZen
タイムゾーンに合わせて株式市場がいつ開くのかをロック画面やウィジェットから確認できる。
海外に行った際に「市場もう始まってたのか!」であったり、
開場する時を楽しみに待っていたが、「今日アメリカは祝日で閉場してたのかい…」という経験を解消するために開発しました。
3. Ex Rate Calc - ウィジェットで為替電卓 -
シンプルな為替の電卓です。
例えばアメリカに行って何かを買うときにドルをそのまま入力して円換算できたら便利かもな?と思い開発しました。