I have a CRUD app built with flutter. Now I want to use Swift to create a widget to show the data at home screen.
I use json to store the data. In the following code if I replace jsonURL with mock json string, everything works well. So I think the problem comes from accessing the local file.
let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.app.identifier")
let jsonURL = sharedContainerURL?.appendingPathComponent("things.json")
And this is how I create the json file with Dart.
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/$storageName.json');
}
In a nutshell the problem is that the widget cannot access the json file I created in the app storage. Everything works well except the widget.
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
I'm working on an app that has the following structure:
MyApp
MyWidgetExtension
MyWatchKitApp
-- MyWatchKitAppExtension
---- MyWatchKitAppWidgetExtension
Both MyWidgetExtension and MyWatchKitAppWidgetExtension were developed using a shared MyWidgetBundle defined as follows:
@main
struct MyWidgetBundle : WidgetBundle
However, I'm running into an issue when attempting to run this on devices with iOS 14. I get an error stating "App extensions must define either NSExtensionMainStoryboard or NSExtensionPrincipalClass keys in the NSExtension dictionary in their Info.plist."
Interestingly, if I remove MyWatchKitAppWidgetExtension, the app installs just fine. But, if I add NSExtensionPrincipalClass or NSExtensionMainStoryboard, when I try to distribute the app to TestFlight, I receive an error stating "Unexpected key NSExtensionPrincipalClass found in extension Info.plist".
I'm at a loss as to how to resolve this issue. Does anyone have any suggestions or insights?
We have a few widgets with configurations enabled. These have been loading/refreshing just fine on iOS 15.x devices across all configurations.
However, on iOS 16.x devices only a few configurations load while others just show a skeleton view.
Also, we did verify on simulators for all the configurations on iOS 15.x, 17, and widgets load as expected on them expect for 16.x simulators.
I have read a few articles where people have been experiencing simialr issues with widgets on iOS 16.x - https://piunikaweb.com/2023/03/10/ios-16-lock-screen-weather-widget-not-updating-or-blank-issue/
Any help or suggestions to resolve this?
AppIntent of the same type is created both for main and widget targets. If app is launched in background, built-in main target intent is executed, but if app is closed, then app is not launched in background mode to execute built-in intent.
Is it supposed to be like this or is it a bug?
I am trying to refresh my widget through Silent Push Notifications.
I have the background value as well as content-value = 1.
However 99% of the time if the app is in the background or has been fully terminated/exited, the silent push notification won't start the app.
I also tried logging the method that runs when the notification is received and it seems like it only gets called once the app is re-launched manually.
Do I need to do anything to get it to get my widget to refresh when some events happen on the backend? Android is really smooth when it comes to implementing similar logic.
In widgets when the button is tapped, we will get a call back to perform() func in app Intent. After this perform func execution is completed, timelineProvider will be called. Likewise, what is the option to update the live activity value once perform() is called in appIntent? I tried to access the live activity instance from the extension into the app, but it is nil so, I can't able to update my live activity.
Is there any way to trigger the night mode (vibrant red variant) for Standby when using the iOS 17 simulator or is that only testable on actual hardware devices?
This is Siri Intent before iOS 16.
func handle(intent: CustomIntent, completion: @escaping (CustomIntentResponse) -> Void) {
completion(CustomIntentResponse(code: .continueInApp, userActivity: nil))
}
This flow working fine.
This is AppIntent after iOS 16. There are 2 ways to continue in app
openAppWhenRun = true
ForegroundContinuableIntent Protocol only works in SwiftUI
But both are not notifying the main app its just opening app without activity. In SiriIntent we can notifiy Main App with completion(CustomIntentResponse(code: .continueInApp, userActivity: nil))
application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool
else its start with didfinishlaunching and we can get useractivity.
Is there any solution for after iOS 16 In AppIntent we can notify app and get NSUserActivity?
Does Vision Pro works with widgets? If yes, how?
Hi , how I can run my shortcut by tapping on button from widget?
As per WidgetKit TimelineProvider documentation:
"When your app is in the foreground, has an active media session, or is using the standard location service, refreshes don’t count against the widget’s daily limit. For more information about media sessions and location services, see AVAudioSession and Configuring your app to use location services."
That suggests that I should be able to reload my widget from the background with the most up to date details of the active media session.
However, that doesn't appear to be working either when trying to reload all timelines or just a specific one. All other background processing is working fine in the app otherwise, including the media playback.
Media session is started by the following code:
do {
try AVAudioSession.sharedInstance().setCategory(.playback, options: .mixWithOthers)
print("AVAudioSession Category Playback OK")
try AVAudioSession.sharedInstance().setActive(true)
print("AVAudioSession is Active")
print(AVAudioSession.sharedInstance())
} catch let error as NSError {
print(error.localizedDescription)
}
let item = AVPlayerItem(url: URL(string: "https://cdn.smartersoft-group.com/various/pull-bell-short.mp3")!)
self.queuePlayer = AVQueuePlayer(playerItem: item)
self.queuePlayer?.volume = 0.0
self.playerLooper = AVPlayerLooper(player: self.queuePlayer!, templateItem: item)
self.queuePlayer?.playImmediately(atRate: 1.0)
self.queuePlayer?.volume = 2.0
Wondering if I am misunderstanding the documentation and it's not a supported use case or a bug.
FB12782013 has been submitted.
In my watch app the AppIntentRecommendation that is returned by func recommendations() needs to be updated after the app is launched.
Problem:
The information that I need to return a list of AppIntentRecommendation is not available when the app is first installed, so I return a "dummy" AppIntentRecommendation initially.
After the app is launched for the first time and user has signed in, I can update the AppIntentRecommendation in func recommendations() but watchOS does not update the list of widgets presented as complications.
func recommendations() -> [AppIntentRecommendation<ConfigurationAppIntent>] {
// Create an array with all the preconfigured widgets to show.
let defaults = UserDefaults.init(suiteName: "group.myApp")!
guard let names = defaults.dictionary(forKey: "names" as? [String:String] else {
return [AppIntentRecommendation(intent: .Demo, description: "Demo")]
}
var recs = [AppIntentRecommendation<ConfigurationAppIntent>]()
for (idx, name) in names() {
let rec = ConfigurationAppIntent()
rec.order = idx
rec.carName = name
rec.action = nil
recs.append(AppIntentRecommendation(intent: rec, description: "name") )
}
return recs
}
}
Hi guys,
I am migrating my widgets to iOS 17 and because I already manage my layout margins, I just want to disable to new built-in widget content margins.
I did it by using ".contentMarginsDisabled()" on the WidgetConfiguration and it works fine at run time.
WIDGET CODE
struct MyWidget: Widget {
let kind: String = "MyWidget"
var body: some WidgetConfiguration {
return IntentConfiguration(kind: kind, intent: MyWidgetIntent.self, provider: WidgetProvider<MyWidgetIntent>()) { entry in
WidgetView<MyWidgetIntent>(entry: entry)
}
.configurationDisplayName("My Widget")
.supportedFamilies([WidgetFamily.systemMedium])
.contentMarginsDisabled() // <-- HERE
}
}
RESULT
Nevertheless, when previewing the WidgetView in a WidgetPreviewContext I didn't find anyway to disable the content margins (because manipulating directly the view and not a WidgetConfiguration).
PREVIEW CODE
struct MyWidget_Previews: PreviewProvider {
static var previews: some View {
WidgetView<MyWidgetIntent>(entry: WidgetEntry<MyWidgetIntent>())
// .padding(-16) Need to add this negative padding to disable margin
.previewContext(
WidgetPreviewContext(family: .systemMedium))
}
}
Do you know how to disable the content margins for widget preview?
Can I use the button on the interactive widget in iOS 17 to update the widget screen?
After updating to the Xcode beta and build/running my app, I started to get an error on all my widgets "Please adopt containerBackground API" and I found a post that had a solution to accommodate iOS 14-16
import Foundation
import SwiftUI
extension View {
@ViewBuilder
func widgetBackground() -> some View {
let gradient = LinearGradient(gradient: Gradient(colors: [Color("LightBlue"), Color("DarkBlue")]), startPoint: .topLeading, endPoint: .bottomTrailing)
if #available(watchOS 10.0, iOSApplicationExtension 17.0, iOS 17.0, macOSApplicationExtension 14.0, *) {
self.containerBackground(gradient, for: .widget)
} else {
self.background(gradient)
}
}
}
I add this to my ZStack for the widget and it does nothing. I still get the same error to adopt to containerBackground API. What am I doing wrong?
We notice when iPhone 14’s lock screen goes in Always-On mode, the countdown text in Live Activity:
seconds part is displayed as --
minutes part only refresh every minute.
Do you have any recommendations on how to improve it? For example, is there any system event or API to let our Live Activity know when lock screen enters/exit Always-On mode? If they are available, we would show something else to the user instead of xx:-- in Always-On lock screen.
In a Live Activity, we have use case to show some countdown for the user o finish some tasks before a deadline. When the deadline reaches, we want to show a different message. We create a timer in our Live Activity codes, listen to it and want to change the text when the timer fires. But, the timer never fires. We write another simple codes to verify:
We didn’t see the countdown effect. We are wondering if
Timer is disabled in Live Activity, so it never fires
or
State variable is disabled, so even Timer fires and changes State variable’s value, View cannot pick up the new State variable’s value
I have created a widget and added a button in its body like this: Button("Tap me: ", intent: TaskIntent()).
If I compile the TaskIntent in both the app and the widget, when tapping the button, it open the app and that's all, 'perform' is not called at all.
If I compile the TaskIntent only in the widget, when tapping the button, it does nothing, 'perform' is not called either.
Is there any special sauce needed to run the 'perform' of the AppIntent attach to the button?
I tried to add iOS 17 version check inside WidgetBundle but the widget extension keep on crashing .
Getting Thread 1: Swift runtime failure: Unexpectedly found nil while unwrapping an Optional value. After removing the iOS 17 check it works fine.
Is there any way to provide version check inside widgetBundle?
We are migrating ClockKit complications to WidgetKit in our watch app (watchOS 9+). The migration went smoothly, UI part works just fine. However, we've hit the wall with widgets not updating when requested by the watch app. I believe we are missing something very simple and fundamental, but couldn't find what exactly so far. Advice and tips would be very welcome! 🙇♂️
Our implementation details:
Whenever data is changed in the main app, the updated data is submitted to the watch app via WatchConnectivity framework using WCSession.default.transferCurrentComplicationUserInfo(_:). According to documentation, this method should be used to transfer complication-related data, since it will wake the watch app even if it is in the background or not opened at all.
Watch app receives updated data and stores it in UserDefaults shared with Watch Widget Extension hosting WidgetKit complications via App Group.
Watch app then request widget timeline reload via WidgetCenter.shared.reloadAllTimelines(). According to documentation, it reloads the timelines for all configured widgets belonging to the containing app, so it seems the appropriate way to reload WidgetKit complications.
Widget Timeline Provider class in Watch Widget Extension reads updated data from shared UserDefaults and uses it to provide the updated snapshot for widget views to render.
We believe our implementation logic is correct, but it doesn't work, for some reason. Widgets sometimes update when the watch app is opened, but not always. The most definitive way to force widgets to update is to switch to a different watch face, which confirms that the Widget Timeline Provider has access to properly updated data.
P.S. We are aware of the daily reload budget imposed on widgets, so we use widgets reload trigger sparingly. Anyway, according to documentation, reload budget is not effective when in DEBUG mode, but widgets won't reload even in DEBUG mode.
Thank you!