0

I have an app that uses SwiftData which is set to be synced with iCloud.

When I run the app in the simulator and on an actual device simultaneously and then amend the customer name on the simulator the change is not reflected on the device if the records are within a ForEach loop within a List. But if just within a List then it does sync and update automatically.

This updates automatically:

List(customers) { cust in
      NavigationLink(cust.name, value: cust)
}

but this doesn't:

List {
    ForEach(customers) { customer in
      NavigationLink(customer.name, value: customer)
}

I realize that notifications only work one way from the simulator (which is fine for my test) but I am getting back data from iCloud when I make the change so it appears as if iCloud is sending the notification but the UI isn't reflecting this within a ForEach loop.

Inserts and deletions do work when used with both methods above.

So, why does it update correctly within a List but not within a Foreach loop?

2
  • 1
    Interesting finding... I didn't know that the List variant was working correctly as I've never tried it. I was always struggling to get the ForEach variant to update automatically. The solution I've found was to "add a dependency" from the NavigationLink to the customers query result, by adding: .onChange(of: customers) {} Perhaps this workaround is working for you too.
    – pd95
    Commented Nov 30, 2024 at 16:38
  • Yes, that workaround has worked. The ForEach now updates when changed on another device. Thanks for your help.
    – Mgwd
    Commented Dec 2, 2024 at 20:00

1 Answer 1

2

According to my understanding this is a bug in SwiftData/SwiftUI, but as mentioned in my comment, there is a workaround:

It seems that the ForEach view (in contrast to the List) is not re-rendering its body whenever the @Query result is refreshed. It seems that ForEach does not detect any relevant change and therefore is not rendering again. To enforce re-rendering of the ForEach content you can add a onChange modifier to your "Row view" to track changes to the Query. You do not have to handle anything in the modifiers closure. Just the presence of the modifier ensures that SwiftUI is rendering the rows again.

List {
    ForEach(customers) { customer in
      NavigationLink(customer.name, value: customer)
          .onChange(of: customers) {}
    }
}
1
  • Thank you!!!!!! Wonderful solution to a disgraceful issue.
    – MattP
    Commented Dec 12, 2024 at 16:06

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.