Hi all, it seems that the new Controls API introduced at WWDC 2024 does not support custom images stored on disk?
I’m trying to create a configurable ControlWidget whose icon will change based on the user’s selection, but it only seems to be able to render system images and image assets.
I store the necessary image files to disk on the first launch. I checked that the files are indeed saved correctly, as my Home Screen widgets are able to fetch and display the images.
Has anyone tried out the new APIs and is able to confirm/deny what I’ve experienced?
WidgetKit
RSS for tagShow 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
Hi, I'm trying to implement the iOS18 ControlWidget features. When i turn on a ControlWidgetToggle that action with a LiveActivityIntent, the toggle will turn off automatic. The toggle state is read from my sharemanger, but it state won't refresh even i use the ControlCenter.shared.reloadAllControls(). Here is my code.
struct TimerLiveActivityControl: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(kind: Self.kind) {
ControlWidgetToggle(isOn: ShareManager.shared.isLiveActivityTimerOn, action: LiveActivityTimerIntent()) { isTurnedOn in
Image(systemName: isTurnedOn ? "fan.fill":"fan")
Text(isTurnedOn ? "Turned off" : "Turned On")
} label: {
Text("Live Activity Timer")
}
}
}
}
struct LiveActivityTimerIntent: LiveActivityIntent, SetValueIntent {
static var title: LocalizedStringResource { "Live Activity timer" }
@Parameter(title: "is Turned On")
var value: Bool
@MainActor
func perform() async throws -> some IntentResult {
TimerLiveActivityTimer.shared.duration = 10
TimerLiveActivityTimer.shared.startTimer()
return .result()
}
}
func startTimer() {
self.startActivity()
self.isLiveActivityTimerOn = true
Timer.scheduledTimer(withTimeInterval: TimeInterval(self.duration), repeats: false) { timer in
self.isLiveActivityTimerOn = false
ControlCenter.shared.reloadAllControls()
self.endActivity()
}
//The ControlWidgetToggle can't refresh even i refresh it all time
Timer.scheduledTimer(withTimeInterval: TimeInterval(0.1), repeats: true) { timer in
ControlCenter.shared.reloadAllControls()
}
}
However the code can work if I use a single SetValueIntent.
My Xcode version is 16.0 beta, use iOS 18.0 simulator.
Hi,
I have an app that provides a SmartStack Widget.
The content in that widget is dependent on a state inside the application so from time to time the app needs to update the widget state.
I tried:
WidgetCenter.shared.invalidateConfigurationRecommendations()
WidgetCenter.shared.reloadAllTimelines()
However this does not have any effect.
I only see updates on reboots (device with watchOS 11 B5)
Is there any other way or am I doing something wrong?
Hi everyone,
I'm working on an app that uses the Screen Time API, and I'm encountering an issue with Live Activities.
The app runs a timer (e.g., 10 minutes), after which the intervalDidEnd method in DeviceActivityMonitor is triggered. To improve the user experience, I've implemented Live Activities to display the remaining time.
However, I'm having trouble stopping the Live Activity when the timer expires and intervalDidEnd is called. It seems that the Screen Time
extensions cannot detect or interact with active Live Activities, even though
both share the same App Group.
My Question:
Since the DeviceActivityMonitor extension does not seem to be able to detect Live Activities, does anyone know if there’s a way to end a Live Activity when the timer expires without relying on a server? Using Apple’s Push Notification service feels excessive given the lightweight nature of the app, which doesn’t use server-side components.
Any insights or suggestions would be greatly appreciated!
Thanks!
I am trying to start a live activity via push token with the below headers and payload. I am using the 'fetch-http2' npm module to execute the APNS request in a deno/typescript environment, and am authenticating via token/p8. The device receives the alert portion of the payload, but the live activity does not appear on the device. I get a 200 OK response from APNS with the unique ID, and dashboard shows notification was successfully sent to the device.
The odd thing, when backgrounding the app dismisses to the Dynamic Island with the animation as if there were a live activity happening, but there is not. When I check Activity<MyAttributes>.activities on app launch, it's empty. I don't see any errors in Xcode when I have the app running/foregrounded when sending the request. Quitting the app restores normal behavior.
TL;DR:
I have regular APNS alert push notification requests working without issue from the same environment. However when attempting to start a live activity via APNS, the alert is received but the live activity does not appear, despite the app/system seemingly thinking there is one. What could I be missing, or what else could I try?
I have checked that:
My generated JWT and device token are valid according to CloudKit dashboard (again, standard push alerts are working as expected)
I can successfully start a live activity locally/from foreground via Activity.request
I have added Supports Live Activities and Supports Live Activities Frequent Updates to my app's info.plist, and also have the required capabilities enabled (remote push, background processing, background fetch)
I am using the current device push-to-start token (obtained from device via Activity.pushToStartTokenUpdates) for the device token in the APNS request (NOT the update token)
My ActivityAttributes and ActivityAttributes.ContentState values and types are correct
"headers": {
"authorization": "bearer {jwt}",
"apns-push-type": "liveactivity",
"apns-topic": "{bundleId}.push-type.liveactivity"
}
"aps": {
"attributes-type": "LiveActivityAttributes",
"attributes": {
"title": "Test Event"
},
"content-state": {
"status": 1
},
"event": "start",
"alert": {
"title": "Alert Title",
"body": "Live Activity has started."
},
"sound": "default",
"timestamp": Math.round(Date.now() / 1000)
}
Third-party WidgetKit complications on watchOS 11 beta 5 are not appearing in the list of available complications. They have also disappeared from watch faces where they were installed. The exact same complications were working fine on earlier betas. This is happening on device, but not in simulator.
This issue may be related to FB14684253, which was fixed with the release of Xcode beta 5. However, Xcode beta 5 does not fix the issue on Apple Watch.
As a sanity check, I also tried with the Backyard Birds sample project, and the complications for that app aren't appearing on device either.
Filed as FB14689021.
I have a LockedCameraCapture extension working well, however there is one situation I cannot find a solution to. If the user has not yet provided camera access permission then the main app will be launched rather than the LockedCameraCapture extension. I cannot find a mechanism by which my main app can detect that this was the reason for the launch and thereby request permission.
When the button is pressed from the control center without permission the app is run and the CameraCaptureIntent is called so I can prompt the user from there. However, as best I can tell the CameraCaptureIntent is not called when launched from a locked Lock Screen, the app is simply opened.
My app has a variety of functions, most of which do not involve the camera so I cannot just always prompt the user for camera access on open. Is there any mechanism by which my main app can detect it was launched for this reason so it could ask for permission? Thank you!
ControlWidgetButton custom image not displayed in iOS 18 beta 5 while it works fine in beta 4 with the same code
My app’s WidgetKit widgets are all crashing on iOS 18 beta 5. They were working just fine on earlier betas. This is happening across both Home and Lock Screen widgets. It's an EXC_BAD_ACCESS crash that seems to be happening deep within WidgetKit.
I've seen other developers posting about this on social media, so it's not just me. Wanted to get this flagged ASAP as it's very late in the beta cycle now...
Filed as FB14684253.
On watchOS 11 when starting a workout session a widget appears automatically on the smart stack showing the pause or resume button.
It´s a great feature, but my problem is that the duration is not showed correctly because the prepare phase of the workout and the pause / resume events are not taken into account calculating the duration.
In my project I don´t use the workout builder. I have made a sample project with workout builder and there the duration is shown correctly.
It would be great if this automatically appearing widget would also show the time correctly in case the workout builder is not used (prepare phase and pause resume events considered, otherwise it looks like a bug).
Is there any way to opt out of this automatically appearing widget or could this be fixed? Any comments from an Apple engineer on this?
Hey folks, I am creating one Widget AppIntent and I want to have as parameters X amount of scripts (let’s call them items) and each item has it’s own color associated (in this screenshot as String just to simplify).
First image is the code I have now which allows the user to select several items, second image is the only way I found to allow associating one item to one color. Is there a smarter way of doing it?
there is a error when widget run in iphone
SendProcessControlEvent:toPid: encountered an error: Error Domain=com.apple.dt.deviceprocesscontrolservice Code=8 "Failed to show Widget 'com.ghm.SmarfidCard.Widget' error: Error Domain=FBSOpenApplicationServiceErrorDomain Code=1 "The request to open "com.apple.springboard" failed." UserInfo={NSLocalizedFailureReason=The request was denied by service delegate (SBMainWorkspace)., BSErrorCodeDescription=RequestDenied, NSUnderlyingError=0x9d6c10a80 {Error Domain=SBAvocadoDebuggingControllerErrorDomain Code=1 "Failed to get descriptors for extensionBundleID (com.ghm.SmarfidCard.Widget)" UserInfo={NSLocalizedDescription=Failed to get descriptors for extensionBundleID (com.ghm.SmarfidCard.Widget)}}, FBSOpenApplicationRequestID=0xa573, NSLocalizedDescription=The request to open "com.apple.springboard" failed.}." UserInfo={NSLocalizedDescription=Failed to show Widget 'com.ghm.SmarfidCard.Widget' error: Error Domain=FBSOpenApplicationServiceErrorDomain Code=1 "The request to open "com.apple.springboard" failed." UserInfo={NSLocalizedFailureReason=The request was denied by service delegate (SBMainWorkspace)., BSErrorCodeDescription=RequestDenied, NSUnderlyingError=0x9d6c10a80 {Error Domain=SBAvocadoDebuggingControllerErrorDomain Code=1 "Failed to get descriptors for extensionBundleID (com.ghm.SmarfidCard.Widget)" UserInfo={NSLocalizedDescription=Failed to get descriptors for extensionBundleID (com.ghm.SmarfidCard.Widget)}}, FBSOpenApplicationRequestID=0xa573, NSLocalizedDescription=The request to open "com.apple.springboard" failed.}., NSUnderlyingError=0x9d6c0dbb0 {Error Domain=FBSOpenApplicationServiceErrorDomain Code=1 "The request to open "com.apple.springboard" failed." UserInfo={NSLocalizedFailureReason=The request was denied by service delegate (SBMainWorkspace)., BSErrorCodeDescription=RequestDenied, NSUnderlyingError=0x9d6c10a80 {Error Domain=SBAvocadoDebuggingControllerErrorDomain Code=1 "Failed to get descriptors for extensionBundleID (com.ghm.SmarfidCard.Widget)" UserInfo={NSLocalizedDescription=Failed to get descriptors for extensionBundleID (com.ghm.SmarfidCard.Widget)}}, FBSOpenApplicationRequestID=0xa573, NSLocalizedDescription=The request to open "com.apple.springboard" failed.}}}
Domain: DTXMessage
Code: 1
User Info: {
DVTErrorCreationDateKey = "2024-08-06 09:58:53 +0000";
}
I want to move a widget created with WidgetKit from an app extension to another app extension.
If I do this, what will happen to the widget that users have already placed on their home screens?
Will the widget disappear from their home screens, or will the migration be handled correctly?
I'm working on a SwiftUI interactive widget using AppIntent. However, I want to prevent my AppIntents from appearing in the Shortcuts app. Currently, all my AppIntents are showing up in the Shortcuts app, but I only want them to be used within my widget.
Is there a way to restrict the visibility of AppIntents so they don't appear in the Shortcuts app?
Here is a simplified version of my AppIntent:
import AppIntents
struct MyWidgetIntent: AppIntent {
static var title: LocalizedStringResource = "My Widget Intent"
func perform() async throws -> some IntentResult {
// Intent logic here
}
}
I've looked into the documentation but haven't found a clear way to achieve this.
I am working on integrating widget support into an existing app and have a question regarding the best practices for reloading a widget. For context, the existing app allows the user to add a playlist widget which will show the title, track count, and thumbnail associated with that playlist.
Within the app, playlists can of course be edited which will change the data that should be reflected on the widget. In addition, edits may happen over and over, in quick succession (ex. deleting multiple tracks, reordering tracks, etc).
My question is as follows: should the widget be reloaded every time an edit is made, or is there a way to mark a widget as needing to be reloaded?
Considering the use case where a user is editing a playlist and makes 10-20 changes in quick succession, it feels wasteful to reload a widget 10-20 times for each small change. However, the app doesn't necessarily know when the user is going to stop editing or terminate the app, so it's important that the widget is reloaded rather proactively.
In addition to the above question, is there any way to only reload a specific, single widget, rather than all widgets or all widgets of a certain kind?
Considering the use case where a user may have 3-4 playlist widgets on their homescreen, it also feels wasteful to reload all of them when only one of the playlists may have been edited. Yet, from my understanding of WidgetCenter, the only two functions for reloading a widget are reloadAllTimelines and reloadTimelines(ofKind:) which seem to only allow broad reloading.
I'm creating a configurable widget using AppIntentConfiguration in my SwiftUI app and wanted to read configuration's payload when the user taps on the widget and launches the app.
Having read the WidgetKit's documentation I noticed I can just call userActivity.widgetConfigurationIntent(of: MyWidgetConfigurationAppIntent.self) inside .onContinueUserActivity() modifier, to get the intent's instance.
This function works and returns the instance when user taps on the widget and the app is already running in the background, but returns nil when the app launches for the first time.
Am I doing something wrong here, is this a desired behaviour? Is using Deep Links a more suited solution for this use case? I'm really not liking the idea of serialising instances of Measurement and UnitMass/UnitTemperature into URLs.
Here's a sample code to illustrate:
@main
struct WidgetIntentTestApp: App {
@State private var favouriteEmoji: String?
private let intentActivityName = String(describing: ConfigurationAppIntent.self)
var body: some Scene {
WindowGroup {
ContentView(favouriteEmoji: favouriteEmoji)
.onContinueUserActivity(intentActivityName) { userActivity in
guard let intent = userActivity.widgetConfigurationIntent(of: ConfigurationAppIntent.self) else {
/// Intent is `nil` when the user activity `launches the app for the first time`.
/// I would have expected this to work given the user activity's `.name` clearly matches the Intent's name
fatalError("Expected intent but received `nil` - this should not have happened.")
}
favouriteEmoji = intent.favoriteEmoji
}
}
}
}
Good afternoon! I am working on an app which requires the app to be opened in response to a push notification from a background state. As it stands:
The app is running in the background
The app has a static widget on the homepage
The app has a dynamic widget with a live activity which is being updated from the backend
The dynamic widget is firing an event which the static widget is listening for
The static widget is programatically calling an AppIntent which tries to open the parent app
Is this possible? Is there a better approach which would work?
We are looking for a way to open the app from the background with the users permission. Appreciate any guidance on the issue
I'm just starting to learn iOS app development and I'm currently using SwiftData. The main app is working fine, and now I've added a Widget to use data from the main app. I've already enabled App Groups and want to reference the shared model entities from the main app, but I'm getting an error saying "can't find type in scope." What additional configurations do I need to make in order to properly reference the same entities? I've seen many example codes that have a "Shared" folder, but even after creating one and moving the files into it, I'm still unable to use them properly. I must be missing some configuration, right?
Also, there's another issue: when debugging the Widget, I keep getting the following error. What could be causing this?
SendProcessControlEvent:toPid: encountered an error: Error Domain=com.apple.dt.deviceprocesscontrolservice Code=8 "Failed to show Widget 'XXXX-Widget' error: Error Domain=FBSOpenApplicationServiceErrorDomain Code=1 "The request to open "com.apple.springboard" failed."
Hello,
I am playing with controls in control center.
I notice that we can just add Toggles and Buttons.
Is it possible to build a more complex UI, like the music control (with play, pause .. buttons, and artwork, labels) ?
Thank you
Frederic
Has anyone been able to create a Control Center widget that opens a snippet view? There are stock Control Center widgets that do this, but I haven't been able to get it to work.
Here's what I tried:
struct SnippetButton: ControlWidget {
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(
kind: "***.***.snippetWidget"
) {
ControlWidgetButton(action: SnippetIntent()) {
Label("Show Snippet", systemImage: "map.fill")
}
}
.displayName(LocalizedStringResource("Show Snippet"))
.description("Show a snippet.")
}
}
struct SnippetIntent: ControlConfigurationIntent {
static var title: LocalizedStringResource = "Show a snippet"
static var description = IntentDescription("Show a snippet with some text.")
@MainActor
func perform() async throws -> some IntentResult & ProvidesDialog & ShowsSnippetView {
return .result(dialog: IntentDialog("Hello!"), view: SnippetView())
}
}
struct SnippetView: View {
var body: some View {
Text("Hello!")
}
}