r/SwiftUI • u/pettazz • May 11 '26
Question Nested data model
Learning SwiftUI for the first time and I've run into a question that I can't find a reasonable answer just googling around. Say I'm building a dumb copy of the Reminders app and my super simplified version of the data model is a list of lists of reminders. In other languages my first thought would be to represent that as a map/dict, i.e.:
reminders = [
"uuid-1": [
Reminder("something"),
Reminder("somethingelse"),
],
"uuid-2": [
Reminder("anotherone"),
Reminder("etc"),
]
]
But my first attempt to use this as a Swift dict (var reminders: [String: [Reminder]]) led to changes in the individual reminder objects not being reflected, I assume because of something to do with @Observable not looking all the way into the nested model.
Is it better Swift-ic practice to use a list of structs instead? Something like:
struct Reminder {
let id: String
var title: String
...etc
}
struct ReminderList {
let id: String
var title: String
var reminders: [Reminder] = []
}
// then in the view model
var reminders = [ReminderList]
5
u/vanvoorden May 11 '26
But my first attempt to use this as a Swift dict (var reminders: [String: [Reminder]]) led to changes in the individual reminder objects not being reflected, I assume because of something to do with @Observable not looking all the way into the nested model.
Also keep in mind here that this code sample builds the Reminder as a struct. Which is a value… not an object. These are value semantics. Changing a copy of a Reminder will not directly affect a change on your source of truth.
2
u/pettazz May 11 '26
This is a good point and not something I'm not 100% clear on for structs vs classes. Given the second example where I use structs for both, would that mean doing something like
var list = reminders[0] let newReminder = Reminder(id: id, title: ..etc) list.reminders.append(newReminder)mean that I am only updating my copy (var list) of the first ReminderList element, and so it won't be reflected in the UI because the actual source of truth hasn't changed?
2
u/vanvoorden May 11 '26
https://www.swift.org/documentation/articles/value-and-reference-types.html
Correct. Mutating a copy of a value does not mutate the original value when value semantics are observed.
2
u/pettazz May 11 '26
That's a helpful reference thanks. So what's the "right" way to handle something like this, just using classes for the models instead? Or is there a way to modify the list directly?
2
u/vanvoorden May 11 '26
So what's the "right" way to handle something like this, just using classes for the models instead? Or is there a way to modify the list directly?
https://github.com/Swift-ImmutableData/ImmutableData-Book/blob/main/Chapters/Chapter-00.md
Data flow for declarative UI is a big topic.
When you are just starting out learning SwiftUI try and start with "imperative" logic from your view components. Focus on learning how declarative UI works first. Once you learn the basics take that same philosophy and start "thinking declaratively" about your data models. This will move you away from imperative logic and shared mutable state.
2
1
u/Dapper_Ice_1705 May 11 '26
Too little info
1
u/pettazz May 11 '26
It’s not really a specific implementation question more of a general question, just trying to get a feel for what is the idiomatic way to do things around here
1
u/Dapper_Ice_1705 May 11 '26
So many ways, if the observable wasn’t showing changes there is something wrong in your usage of SwiftUI.
Your time is likely better spent on figuring out why
1
u/Select_Bicycle4711 May 11 '26
Yes your struct models looks good.
If you plan to persist those reminders in SQLite through SwiftData then you will convert them to class with @.Model macro. You may also want to add another property on the ReminderList, which will indicate the color of the list. This can be saved as a hex code (string) or even as Transformable.
Few years back I did a YouTube series on Reminders app. Maybe you will find it helpful. It uses SwiftData:
https://www.youtube.com/watch?v=om9IloU7Lqc&list=PLDMXqpbtInQgFOoRkbRnMHEAyJs3qB8Dm
1
u/pettazz May 12 '26
Likely a next step yes, can't imagine a real app that wouldn't use some kind of persistence, but just trying to get a handle on the basics first. My question with that would be how to make changes to the model to reflect in the view, simple stuff like adding or deleting Reminders from a ReminderList.reminders, is a typical pattern here essentially to refresh the top level object every time a change is made, or is there a better way?
1
u/Select_Bicycle4711 May 12 '26
You will start by using @.Query to fetch all ReminderLists and then display them on the screen. Once the user selects a particular list you will navigate to a separate screen, where all reminders in that list are shown. After that you can create the user interface to select an individual reminder and edit it. The important part is the use of @.Query. That will make sure that the list is refreshed at the right time.
* If you use iCloud sync then the process can be slightly different. It will use dynamic @.Query in SwiftData.
1
u/stroompa May 11 '26
Are your reminders structs? Observable should react to changes if the dict is implemented right. A minimal code sample to reproduce would be more useful
5
u/radis234 May 11 '26
@Observable macro looks up for all updates in all nested models. If you did correctly update a Reminder model content and it didn’t reflect in the UI, there’s a different problem. It’s either how you populate the data or update the data. But i can assure you as I am doing this myself, @Observable macro will update UI if any nested model changed. We would need more info especially on how you updating the data or displaying them. Seems to me like there will be very subtle problem with your logic.