WidgetKit

RSS for tag

Show relevant, glanceable content from your app on iOS and iPadOS Home Screen and Lock Screen, macOS Desktop, Apple Watch Smart Stack and Complications, and in StandBy mode on iPhone.

Posts under WidgetKit tag

200 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Can Live Activities be updated via `activity.update` in extensions?
I'm trying to build a live activity with the new iOS 17.0 interactivity, and I want a button on the live activity to update the live activity when clicked (e.g. start a timer). This works fine with a widget, but with live activities it doesn't seem like the activity view gets refreshed instantly. As a workaround, I tried putting the activity.update code in the button's intent's perform method. This doesn't seem to work as the intent gets executed from the widget extension, which seems to always have an empty activities (link) array. Question: Is activity.update only meant to be called from the app (either foregrounded or in a background task), or should it also be possible to be called in the WidgetExtension itself? Ideally I'd be able to avoid using push notifications, which seems overkill for e.g. just at timer. Related thread: Changing the live activity without push notification
3
0
1.3k
Aug ’23
Widgets: Detect StandBy night mode iOS 17
How can I detect the StandBy mode on iOS 17? When my widget is used in StandBy mode, the background is removed thanks to this method: .containerBackground(for: .widget) { myBackgroundImage() } This is very useful when my small widget is displayed on the iPad Lockscreen in landscape mode for example. But to delimit my widget frame on the iPad lockscreen I set up an alternative light background this way: if widgetRenderingMode == .vibrant { Color.white.opacity(0.1).cornerRadius(cornerRadius) } My question: How to add the same alternative background when the widget is used in StandBy mode? In StandBy mode, widgetRenderingMode is equal to .fullColor like on the HomeScreen. But I want to be able to add this background only in standBy mode... Precision: containerBackground(for: .widget){} send it's content to the background of the container so I can't hide my light background behind it as it will always be in front wherever I put it...
4
2
2.2k
Nov ’23
Live Activity not working on iOS 16.6
Hey all, I was testing my app on my iPhone that just updated to 16.6. For some reason, when I request to show an Activity, the request goes through, tells me that my live activity is currently running (w/o any errors) yet won't show on the lock screen. When I tested on the Simulator, which is running 16.4, I am able to see the Live Activity perfectly fine. Has there been some change that would prevent me from seeing the Live Activity now?
2
0
1.3k
Aug ’23
transferCurrentComplicationUserInfo does not wake the extension delegate
When calling WCSession.default.transferCurrentComplicationUserInfo , the watch extension does not wake (init is never called) when the watch application is inactive. It does work fine when the watch application is in the foreground. And the didReceiveUserInfo is being called. WidgetKit developer mode is set, so that remainingComplicationUserInfoTransfers is not an issue. using watchOS 9.5 and SwiftUI / WidgetKit complications.
3
2
1.3k
Oct ’23
Tapping button in Widget does not update App until force quit
I think this is a bug, but figured I'd double check in case I missed something stupid. I have the following model: class Thread { var id: UUID var timestamp: Date var style: String var fit: String var brand: String var size: String @Attribute(.externalStorage) var image: Data? @Relationship(deleteRule: .cascade) var worn: [TimesWorn] var wornCount: Int @Relationship(deleteRule: .cascade) var washed: [TimesWashed] var washedCount: Int @Relationship(deleteRule: .cascade) var fitPics: [FitPic] which I am accessing via the following method in my app intent file: func perform() async throws -> some IntentResult { guard let modelContainer = try? ModelContainer(for:Thread.self) else { return .result() } let descriptor = FetchDescriptor<Thread>(predicate: #Predicate {thread in thread.style == threadStyle }) let threads = try? await modelContainer.mainContext.fetch(descriptor) if let thread = threads?.first { let newWorn = TimesWorn() thread.worn.append(newWorn) thread.wornCount = thread.worn.count } return .result() } TimesWorn is super simple, just a timestamp. Basically running into two issues: when I hit the button in the widget tied to the intent, wornCount will not update in the widget UI so there is no feedback that anything has happened. if I instead use worn.count in my widget text view, it does update immediately. To add to the weirdness any places in the main application where I am using wornCount do not update until I force quit the app and relaunch. Please forgive me if I am doing something dumb here, or am asking this question stupidly. this is my first swift app and my first ever forum post! running Xcode 15 beta 6 + Simulator iOS 17 beta 5
0
0
363
Aug ’23
Conditionally Migrate WatchOS 10 users ONLY to WidgetKit
I'm looking to migrate my users ClockKit complications to WidgetKit in my next app update. I can only do this for WatchOS 10 users because the APIs are too limited for WatchOS 9 (eg. Widget corner round text not available). But I do need to do this for WatchOS 10 users in order to get in the Smart Stack. When I tried to mark my getWidgetConfiguration method in my ComplicationController.swift with: @available(watchOS 10.0, *) it complains and says: Protocol 'CLKComplicationWidgetMigrator' requires 'getWidgetConfiguration(from:completionHandler:)' to be available in watchOS 9.0 and newer I then tried modifying my WidgetKit extension to only support WatchOS 10. This seems to work for a while but at some point WatchOS 9 devices still try the migration and crash with symbolNotFound DYLD issues for the WidgetKit extension which shouldn't even be embedded in the WatchOS 9 builds! (all visible in iPhone Analytics data crashes) So I'm not sure what else to try. I've researched a lot in docs etc... but can find no official way to achieve this.
11
2
2k
Jun ’24
AppIntent timing out after around 25s. Are there solutions or alternatives?
I am launching an app intent from a widget button tap, which, in turn, executes a task in the background that can take up to 40 seconds. This task fails to execute completely most times, as the time out period is around 25 to 30 seconds. Is there any way to work around the app intent time limit? User feedback is not important, as this interacts with an external physical device, so the user knows the state from looking at said device. I was considering other alternatives for when the task fails to execute completely, like prompting the user to continue the task, but this doesn't seem like a valid solution, as there is no way to do this foreground interfacing from a widget. I am completely stumped with this. This was a problem with the old Intents, and it seems like it's still a problem with the new App Intents. I wish Apple would allow developers to control how much the intent would take to timeout.
0
1
483
Aug ’23
showsWidgetContainerBackground crashes on iOS17 beta 6 on a real device
When using @Environment(.showsWidgetContainerBackground) inside any view, the app and widgets crash in a real device with iOS17 beta 6. The same build works perfectly on simulators and a real device running iOS17 beta 5. Using XCode 15 beta 6 to build the app. I am getting this error: Symbol not found: _$s7SwiftUI17EnvironmentValuesV9WidgetKitE05showsE19ContainerBackgroundSbvg I just created a new blank project and only added that line and it crashes. Filled a feedback report. I am the only one? struct ContentView: View { @Environment(\.showsWidgetContainerBackground) var showsBackground: Bool var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } .padding() } }
4
0
704
Aug ’23
Cannot see my widget in Widget Gallery (macOS)
I used a package which contains a XCFramework inside it in my widget target. It’s working fine on iOS, and macOS Widget Simulator. But when I open widget gallery on macOS, I can’t find my widget. I tried to run my widget directly inside /Contents/PlugIns/WidgetTestExtension.appex/Contents/MacOS/WidgetTestExtension, it prints out the error: dyld[4767]: Library not loaded: @rpath/myframework.framework/Versions/A/myframework
0
0
360
Aug ’23
Using cache on WidgetExtension
I am planning on using a cache with NSCache for the WidgetExtension, how would I guarantee that my NSCache lives as long as possible? I plan on initializing the cache when getTimeline is called, if I initialize it there would my NSCache still live for the next time for when the timeline is regenerated, even if the WidgetExtension is terminated?
0
0
456
Aug ’23
Interactive Widget (iOS 17) reload timeline on interaction
Hello everyone 👋 I'm currently implementing the new interactive widget for iOS 17 but there is a behaviour I don't understand. My Goal : The widget I want to create is pretty simple : I have a Store where I can find a list of MyObject, and in the widget I want to display 2 items randomly every X hours. For each item, there is a button to like/unlike the item. The Issue : When I tap the like button, the function func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) of the widget is called again and so, two items are get randomly (so the items I liked disappear). What I want : When two items are got randomly and I tap a like button, I want my item isLiked property to be toggled without refreshing my timeline. My current code : (The code is simplified for the demo, but if you copy/paste all of this code in a new widget, it should compile and run) MyObject final class MyObject { var isLiked: Bool let name: String init(isLiked: Bool, name: String) { self.isLiked = isLiked self.name = name } } MyStore final class MyStore { static let shared: MyStore = .init() private init() { } var myObjects: [MyObject] = [ .init(isLiked: false, name: "Test 1"), .init(isLiked: true, name: "Test 2"), .init(isLiked: false, name: "Test 3"), .init(isLiked: false, name: "Test 4"), .init(isLiked: false, name: "Test 5"), .init(isLiked: true, name: "Test 6"), .init(isLiked: false, name: "Test 7"), .init(isLiked: false, name: "Test 8"), .init(isLiked: false, name: "Test 9"), .init(isLiked: true, name: "Test 10"), .init(isLiked: false, name: "Test 11"), .init(isLiked: false, name: "Test 12"), .init(isLiked: true, name: "Test 13"), .init(isLiked: false, name: "Test 14"), ] func getRandom(_ number: Int) -> [MyObject] { guard !myObjects.isEmpty else { return [] } var random: [MyObject] = [] for _ in 0 ... number - 1 { let randomIndex: Int = Int.random(in: 0...myObjects.count - 1) random.append(myObjects[randomIndex]) } return random } } My action intent import AppIntents struct AddOrRemoveFromFavoriteAppIntent: AppIntent { static let title: LocalizedStringResource = "My title" static let description: IntentDescription = .init("My description") @Parameter(title: "name") var name: String init() { } init(name: String) { self.name = name } func perform() async throws -> some IntentResult { MyStore.shared.myObjects.first(where: { $0.name == name })?.isLiked.toggle() return .result() } } My widget struct MyWidget: Widget { let kind: String = "MyWidget" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider()) { entry in MyWidgetEntryView(entry: entry) .containerBackground(.fill.tertiary, for: .widget) } .configurationDisplayName("My Widget") .description("This is an example widget.") } } My widget view struct MyWidgetEntryView : View { var entry: Provider.Entry var body: some View { if entry.objects.count >= 2 { HStack { HStack { Text(entry.objects[0].name) Button(intent: AddOrRemoveFromFavoriteAppIntent(name: entry.objects[0].name)) { Text(entry.objects[0].isLiked ? "Unlike" : "Like") } } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.red) HStack { Text(entry.objects[1].name) Button(intent: AddOrRemoveFromFavoriteAppIntent(name: entry.objects[1].name)) { Text(entry.objects[1].isLiked ? "Unlike" : "Like") } } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.green) } } else { Text("No data") } } } My widget model struct SimpleEntry: TimelineEntry { let date: Date let objects: [MyObject] } My widget provider struct Provider: TimelineProvider { func placeholder(in context: Context) -> SimpleEntry { SimpleEntry(date: Date(), objects: []) } func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { let entry = SimpleEntry(date: Date(), objects: []) completion(entry) } func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { var entries: [SimpleEntry] = [] let currentDate = Date() for hourOffset in 0 ..< 5 { let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! let entry = SimpleEntry(date: entryDate, objects: MyStore.shared.getRandom(2)) entries.append(entry) } let timeline = Timeline(entries: entries, policy: .atEnd) completion(timeline) } } What am I missing ? Are widgets not designed for this use case ? Thanks 🙏 Alexandre
3
0
1.9k
Aug ’23
What is the correct way to reload widget timeline in iOS 17 for interactive widgets manually?
I want a button in my interactive widget, which on tapping will get some result from API and then reload the widget timeline on returning .result(). But since this operation can take time I want that my button state should get disabled so that no more interaction is allowed and this should happen instantly. What I want to know is that, is there a way we can reload the timeline on tapping the button (via perform method of app intent) before the result is returned, which will show my results instantly and disable my button, and once the network call finishes, the result() block can hit back, further continuing to reload the timeline. I am also wondering if WidgetCenter.shared.reloadTimline(ofKind: ) is going to work if my app is not running in the background, and if my code will successfully hit this method.
0
0
445
Aug ’23
Cannot Start Audio Playback from Interactive Widget (iOS 17)
I want to be able to start/stop audio from my interactive widget. But when I try to start playback with my AppIntent, I get the error, ATAudioSessionClientImpl.mm:281 activation failed. status = 561015905 This indicates the app isn't set up properly for background audio, but it has Audio, AirPlay, and Picture in Picture checked in Background Modes. Is this an intentional limitation of 3rd party widgets? The Apple Music widget is able to start and stop audio without the app being in the foreground. Can third party apps do it too?
2
1
2.0k
Oct ’23
A widget using Button(intent:) { } will crash on macOS 13, even after restricting the button to macOS 14 using if #available(macOS 14, *) { }
I have a widget with a button that prints "Hello" to the console when tapped. This button is shown only on iOS 17.x and macOS 14.x, and is supposed to be hidden on lower versions. Platform versions tested: iOS 16.6, iOS 17 beta 7 (21A5319a), macOS 13.4.1 Xcode version used: 15.0 beta 7 (15A5229h). Expected behaviour: The button is hidden on iOS 16.x and macOS 13.x, and shown only on iOS 17 and macOS 14. Observed behaviour: The button is hidden on iOS 16.6. However, on macOS 13.4.1, the widget crashes with this error: dyld[5266]: Symbol not found: _$s7SwiftUI6ButtonV012_AppIntents_aB0E6intent5labelACyxGqd___xyXEtc0dE00D6IntentRd__lufC Referenced from: <3EB03A2B-BF9A-31EC-88B9-B4CF015DD35D> /Users/user/Library/Developer/Xcode/DerivedData/WidgetIntentTest-hfjndspaaxqvyuhgdzlznjikilce/Build/Products/Debug/WidgetIntentTest.app/Contents/PlugIns/WidgetExtension.appex/Contents/MacOS/WidgetExtension Expected in: <0A9F8825-DAF1-39A5-953C-C5ECACDC1B0B> /System/Library/Frameworks/_AppIntents_SwiftUI.framework/Versions/A/_AppIntents_SwiftUI Here's the code: The widget code with the button: import WidgetKit import SwiftUI struct WidgetsEntryView: View { var entry: Provider.Entry @available(iOS 17, macOS 14, *) var button: some View { Button(intent: SayHelloIntent()) { Text("Say hello") } } var body: some View { VStack { Text("Hello there") #if os(iOS) if #available(iOS 17, *) { button } #elseif os(macOS) if #available(macOS 14, *) { button } #endif } //ignore this if on iOS 16 or macOS 13 .widgetBackground(backgroundView: Color.clear) } } The rest of the widget code struct Provider: TimelineProvider { func placeholder(in context: Context) -> SimpleEntry { SimpleEntry(date: Date()) } func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { completion(SimpleEntry(date: Date())) } func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) { let timeline = Timeline(entries: [SimpleEntry(date: Date())], policy: .atEnd) completion(timeline) } } struct SimpleEntry: TimelineEntry { let date: Date } struct Widgets: Widget { let kind: String = "Widgets" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: Provider()) { entry in WidgetsEntryView(entry: entry) } .configurationDisplayName("My Widget") .description("This is an example widget.") } } @main struct WidgetIntentTestBundle: WidgetBundle { var body: some Widget { Widgets() } } //ignore this if on iOS 16 or macOS 13 extension View { func widgetBackground(backgroundView: some View) -> some View { #if os(iOS) if #available(iOSApplicationExtension 17.0, *) { return containerBackground(for: .widget) { backgroundView } } else { return background(backgroundView) } #elseif os(macOS) return background(backgroundView) #endif } } App Intent import AppIntents import WidgetKit struct SayHelloIntent: AppIntent { static var title: LocalizedStringResource = "Say Hello" static var description = IntentDescription("prints hello to the console.") func perform() async throws -> some IntentResult { print("Hello") return .result() } } The app's ContentView struct ContentView: View { var body: some View { VStack { Text("Hello World") } .padding() } } Is anyone else experiencing this issue? The Button initialiser I'm using is marked as available on macOS 13, so I don't see a reason why the widget would crash with that error (which I understand is because it can't find the Button code in SwiftUI on macOS 13. For what it's worth the widget doesn't crash and works as expected when built using Mac catalyst 16. Any help is welcome!
2
0
1.1k
Oct ’23
watchOS 10, widgets, widget doesn't appear in the gallery
I am working on a new standalone Apple Watch app. However, I am unable to see my widget in the Smart Stack. My goal is to have a singular complication for the .accessoryRectangular family. The latest beta I tried is Xcode Beta 8. I can't see the widget either on the Simulator or real hardware. I have done these steps. Added Watch App target with DT set to 10.0. Bundle ID .watchkitapp Added widget extension target with DT set to watchOS 10. Bundle ID .watchkitapp.widget Added AppIntentTimelineProvider for the widgetExtension target, all based on the example code Backyard Birds: Building an app with SwiftData and widgets Added the Widget to the same target @main struct widget: Widget { let kind: String = "widget" var body: some WidgetConfiguration { AppIntentConfiguration(kind: kind, intent: ConfigurationAppIntent.self, provider: Provider()) { entry in widgetEntryView(entry: entry) } } } struct widgetEntryView : View { @Environment(\.widgetFamily) private var family var entry: Provider.Entry var body: some View { switch family { case .accessoryRectangular: RectangularView(entry: entry) default: Text("Not Supported") } } } What am I missing? In the past I would add the complications controller for the WatchKit, but I was hoping we don't need to do it. I will research more into complications and WidgetKit for watchOS 10 and will keep this updated for people who struggling like me.
2
0
927
Aug ’23