Let's say that I have a class Employee whose picture property can be observed.
internal class Employee: CustomDebugStringConvertible, Identifiable, ObservableObject {
internal let name: String
internal let role: Role
@Published internal var picture: UIImage?
}
With a class that stores an array of employees. This array might be mutated later so it's a @Published property:
internal class EmployeeStorage: ObservableObject {
@Published internal private(set) var employees: [Employee]
}
Now I have a list of views that uses the employee storage:
struct EmployeeList: View {
@ObservedObject var employeeStorage = EmployeeStorage.sharedInstance
var body: some View {
// Uses employeeStorage.employee property to draw itself
}
}
And the list is used in a content view:
struct ContentView: View {
@ObservedObject var employeeStorage = EmployeeStorage.sharedInstance
var body: some View {
// Uses an EmployeeList value
}
}
Let's say that now the EmployeeStorage object changes mutates employee array: this effectively updates the UI and I can see that the list is updated with the new collection of employees. Problem: what if I want to achieve the same effect when an employee's picture property changes? I thought that this was enough, but in my case (I have a more complex example), the UI is not updated in case that an employee's image changes. How to do it?
There are a couple of problems in your code.
Employee
Model
ObservableObject
.UIImage
!? Simply use Image
and possibly a URL if you're fetching data from a web Api asynchronously.// Employee Model
struct Employee: Codable, Identifiable {
let name: String
let role: Role
let imageUrl: String
let picture: Image
}
EmployeeStorage
EmployeeStorage
as a singleton is problematic. Apple suggests that you better use an @EnvironmentObject
property to pass data shared between views and their subviews. It's super simple, yet robust!That being said, you can modify your code as follows:
// Employees Store
final class EmployeeStorage: ObservableObject {
@Published var employees: [Employee]
}
// Content View
struct ContentView: View {
var employeeStorage = EmployeeStorage()
var body: some View {
...
EmployeeList()
.environmentObject(employeeStorage) // here you pass the storage to the list
}
}
// Employee List
struct EmployeeList: View {
@EnvironmentObject var employeeStorage: EmployeeStorage
var body: some View {
NavigationView {
List {
ForEach(employeeStorage.employees) { employee in
NavigationLink(
destination: EmployeeDetail()
.environmentObject(employeeStorage)
) {
EmployeeRow(employee: employee)
}
}.navigationBarTitle(Text("Employees"))
}
}
}
}
For more information, you can check this out. It's been done pretty much what you're going to achieve.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With