Widget changes not syncing to main app
Hello! Thanks for this demo project. I've noticed that when you modify a task through the widget, the change is not reflected in the main app until the app is restarted. I've seen the same behavior in my own apps using Interactive Widgets with SwiftData. Is this just expected behavior for the SwiftData beta currently?
Hi. Do you fetch your data when the app is in the foreground like monitoring the ScenePhase and fetch data when its value changes to .foreground? I haven’t encountered this issue before in iOS 17 Beta 6.
I'm not doing anything special in the app besides the @query command in the view to pull from the ModelContainer. When I used your app, I had the same behavior though. Where when I completed a task, the main app didnt reflect the changes when brought into the foreground unless I killed and reopened the entire app
Oh I reproduce this issue and seems that fetching and saving data manually won't work. However today Apple released the new Beta 8, I haven't have time to try it yet because I am on my trip, can you share the result after upgrading Xcode 15 beta and try?
Beta 8 produces the same result, unfortunately. Sent from my iPhoneOn Aug 31, 2023, at 6:46 AM, JuniperPhoton @.***> wrote: Oh I reproduce this issue and seems that fetching and saving data manually won't work. However today Apple released the new Beta 8, I haven't have time to try it yet because I am on my trip, can you share the result after upgrading Xcode 15 beta and try?
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: @.***>
I think we should fire a feedback to Apple.
I tried Core Data and the issue exists and it seems that the changes won't be written to the disk until the app is killed. Also, I can only try on the simulator for now, but as far as I know the simulator has different behaviors regarding to saving UserDefaults to the disk.
I also tried changing the value in UserDefaults and the value is updated as soon as I tap the widget to bring the app to the foreground.
Good idea. I've never filed feedback with them before. Is that something you'd like to do? I'd be happy to figure it out otherwise
I would. But I find out that the last feedbacks I submitted are still in the “open” state. But it’s still better to file a feedback though.
By the way, did you test on real device or just use the simulator like me?
Okay I’ll file it. I’ve tried on a simulator and physical device with the same results. Sent from my iPhoneOn Aug 31, 2023, at 8:14 PM, JuniperPhoton @.***> wrote: I would. But I find out that the last feedbacks I submitted are still in the “open” state. But it’s still better to fire a feedback though. By the way, did you test on real device or just use the simulator like me?
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: @.***>
I have tested on a simulator and physical device with the same result. Feedback filed.. FB13107596
Thanks. I will also report this issue later.
I have tried a method to avoid this issue.
When fetching data: use a dedicated ModelContext to fetch data. By default the @Query Marco uses MainContext to fetch data.
let context = ModelContext(sharedModelContainer)
let items = (try? context.fetch(FetchDescriptor<Item>())) ?? []
self.items = items
And it's ok to update the data in an arbitrary context in Widget extension.
The reason I tried this method is that my app MyerList has adopted this interactive widget feature before I make this demo and article and it works as expected. Though it uses Core Data instead of SwiftData, but I always uses the background context to update data and update data using a subscription-notification style.
After all, I think it's still the issue of SwiftData (even in Beta 8). And I will still keep my eye on this issue.
In my own app this has also been driving me bananas. Searched on GitHub for what others are doing and found this repo. On release version of Xcode 15, the issue remains unfortunately :(
(posting for others who are googling, because there's not much out there).
See video:
https://github.com/JuniperPhoton/Widget-Intermediate-Animation/assets/1131967/1e549294-fd1a-471a-9f84-dc2faa97c19f
I'm very curious what Sindre is doing, he has build basically this same app (apparently in SwiftData)
Here he has this behaviour working:
https://github.com/JuniperPhoton/Widget-Intermediate-Animation/assets/1131967/fdc9adf3-cc93-4cf9-80d1-72804c0b7bac
There is a workaround posted before(see my previous comment). It's fine by me for now because I won't use SwiftData for production because supporting only iOS 17 will have very few users.
Indeed that workaround works - thanks for fast reply. It's a shame to abandon @Query - it seems kinda fundamental..!
Unfortunately, fetching Models from a background context means they can't be used in the UI :(
By default the @Query Marco uses MainContext to fetch data.
I don't think this can be changed - that might actually be a workaround if it could. A shame.
And it's ok to update the data in an arbitrary context in Widget extension.
Correct, it doesn't seem to matter which context you use in the Widget. Also saving (or not) doesn't make a difference.
Correction:
Unfortunately, fetching Models from a background context means they can't be used in the UI :(
Actually, this works well enough. The fetched models can still be used in the UI without issue.
Actually you can use modelContext modifier to set the context to be used in @Query. Even if you are creating your own ModelContext there is no need to abandon the @Query Marco if it's off this case.
But the key of this workaround is to create a ModelContext each time you fetch items. If you reuse a ModelContext created before you still can't get the right results.
Thanks so much, was banging my head on that throughout the betas & was amazed it released with that bug. Now I can progress! 🙌
This workaround got my app working as well. Thanks @JuniperPhoton
Can I get a code sample on how to use this workaround?
I put .modelContext(ModelContext(sharedModelContainer)) on the content view
and the following in onChange(of: scenePhase):
guard let modelContainer = try? ModelContainer(for: Item.self) else { return }
let descriptor = FetchDescriptor<Item>()
let fetchedContributions = try? ModelContext(modelContainer).fetch(descriptor)
for item in items {
if fetchedContributions?.first(where: { $0.id == item.id })?.completedDate == nil && item.completedDate != nil {
toggleComplete(item: item)
} else if fetchedContributions?.first(where: { $0.id == item.id })?.completedDate != nil && item.completedDate == nil {
toggleComplete(item: item)
}
}
but it seems to have multiple ModelContext as the items sometimes do not save.
thanks :)
Thanks for the idea of a seperate context, that works for me when fetching data, but how would i then prevent another cloudkit instance from beeing created if i use cloudkkit?
CoreData: error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate resetAfterError:andKeepContainer:](585): <NSCloudKitMirroringDelegate: 0x600003b42ca0> - resetting internal state after error: Error Domain=NSCocoaErrorDomain Code=134410 "CloudKit setup failed because there is another instance of this persistent store actively syncing with CloudKit in this process."
I did something nasty but it seems to work fine :) In a nutshell – yes, SwiftData won't pull the changes from Widget when the app enters foreground but it will do so if you try to change the corresponding item in the app. So what I did is following:
@Environment(\.scenePhase) var scenePhase
var body: some View {
...
.onChange(of: scenePhase) { _, newValue in
if case .active = newValue {
items.forEach { $0.title = $0.title }
}
}
}
Setting the same title won't change a thing but it will force SwiftData to pull the changes and apply them. That being said I've only a bunch of items at any given time, not sure how it's gonna work with hundreds or more but at least it should help those who have only a few at a given screen
@ramzesenok your solution worked for me! I'm considering SwiftData to manage app state that is shared with AUv3 app extension. In my demo using 2 apps that share the same app group, I was only seeing additions and deletions, but not property changes. The forEach hack did the trick to update the view. Wow.
Apple just announced SwiftData History, which can be used to resolve this issue. Here's the video from WWDC: https://www.youtube.com/watch?v=K2FzpebEL_4&list=PL5ZA0WjgLYi1c3otN7tXl5K5H6MOhIR__&index=22
Apple just announced SwiftData History, which can be used to resolve this issue. Here's the video from WWDC: https://www.youtube.com/watch?v=K2FzpebEL_4&list=PL5ZA0WjgLYi1c3otN7tXl5K5H6MOhIR__&index=22
Can you explain how it can help?
Apple just announced SwiftData History, which can be used to resolve this issue. Here's the video from WWDC: https://www.youtube.com/watch?v=K2FzpebEL_4&list=PL5ZA0WjgLYi1c3otN7tXl5K5H6MOhIR__&index=22
Can you explain how it can help?
I have yet to test, but around the 5:18 mark in the video, the speaker gives an example of how SwiftData History can be used to fetch changes from a widget in order to update the app UI.
I can try to do some testing this week
Apple just announced SwiftData History, which can be used to resolve this issue. Here's the video from WWDC: https://www.youtube.com/watch?v=K2FzpebEL_4&list=PL5ZA0WjgLYi1c3otN7tXl5K5H6MOhIR__&index=22
Can you explain how it can help?
I have yet to test, but around the 5:18 mark in the video, the speaker gives an example of how SwiftData History can be used to fetch changes from a widget in order to update the app UI.
I can try to do some testing this week
Okay, I will also try.
Apple just announced SwiftData History, which can be used to resolve this issue. Here's the video from WWDC: https://www.youtube.com/watch?v=K2FzpebEL_4&list=PL5ZA0WjgLYi1c3otN7tXl5K5H6MOhIR__&index=22
Can you explain how it can help?
I have yet to test, but around the 5:18 mark in the video, the speaker gives an example of how SwiftData History can be used to fetch changes from a widget in order to update the app UI. I can try to do some testing this week
Okay, I will also try.
@rizwan95 I think I got SwiftData History implemented correctly. I have my app/widget updating in both directions in a sample app. Here is the link to the repo: https://github.com/codefruitdev/SwiftDataWidget
Apple just announced SwiftData History, which can be used to resolve this issue. Here's the video from WWDC: https://www.youtube.com/watch?v=K2FzpebEL_4&list=PL5ZA0WjgLYi1c3otN7tXl5K5H6MOhIR__&index=22
Can you explain how it can help?
I have yet to test, but around the 5:18 mark in the video, the speaker gives an example of how SwiftData History can be used to fetch changes from a widget in order to update the app UI.
I can try to do some testing this week
Okay, I will also try.
@rizwan95 I think I got SwiftData History implemented correctly. I have my app/widget updating in both directions in a sample app. Here is the link to the repo: https://github.com/codefruitdev/SwiftDataWidget
Woah! Thank you very much!
I did something nasty but it seems to work fine :) In a nutshell – yes, SwiftData won't pull the changes from Widget when the app enters foreground but it will do so if you try to change the corresponding item in the app. So what I did is following:
@Environment(\.scenePhase) var scenePhase var body: some View { ... .onChange(of: scenePhase) { _, newValue in if case .active = newValue { items.forEach { $0.title = $0.title } } } }Setting the same title won't change a thing but it will force SwiftData to pull the changes and apply them. That being said I've only a bunch of items at any given time, not sure how it's gonna work with hundreds or more but at least it should help those who have only a few at a given screen
It worked for me, thanks!!