I apologize if I have to deviate from writing RxSwift to SwiftUI. I have been reading a SwiftUI book from RayWenderlich the past few days, and though I have been writing SwiftUI codes since the past year, I still think that I don't have a deep understanding on this language. And what's the best eay to know more about it? Read. I'm a fan of RayWenderlich books, they are so easy to understand, I really recommend buying their books if some topics interest you.

Anyway this topic caught my attention: ObservableObject. But prior to that, I read a tweet this morning that we have to be careful in using ObservableObjects because it can generate massive view updates. They call it Massive ObservableObject (MOOs), or a cow! It can get too noisy, and too messy.
To make an ObservableObject, it should be:
A reference type (make a class conform to ObservableObject)
Be able to specify which property triggers a UI update (@Published attribute). These properties act like a @State property does in a View. Which means it should be a value type (basic data types or structures). As for structures, we should limit it to contain only the required minimum properties.
Here's an example of an ObservableObject:
struct Birthday {
// I used Strings here for easier binding later
var month: String = ""
var day: String = ""
var year: String = ""
var formatted: String {
return "\(month)/\(day)/\(year)"
}
}
class Person: ObservableObject {
@Published var name: String = ""
@Published var age: String = ""
@Published var birthday = Birthday()
}
And we initialize it like this in a View:
@ObservedObject private var person = Person()
Here's a very simple Screen for the sake of demo:
import SwiftUI
struct ContentView: View {
@ObservedObject private var person = Person()
var updateDetailsView: some View {
VStack(spacing: 5) {
HStack {
Text("Update Details:")
.font(.subheadline)
Spacer()
}
.padding(.top, 10)
TextField("Name", text: $person.name)
TextField("Age", text: $person.age)
}
}
var updateBirthdayView: some View {
VStack(spacing: 5) {
HStack {
Text("Birthday:")
.font(.subheadline)
Spacer()
}
.padding(.vertical, 10)
TextField("Month", text: $person.birthday.month)
TextField("Day", text: $person.birthday.day)
TextField("Year", text: $person.birthday.year)
}
}
var body: some View {
VStack(spacing: 20) {
Text(person.name)
.font(.headline)
Text(person.age)
.font(.body)
Text(person.birthday.formatted)
.font(.body)
Divider()
updateDetailsView
updateBirthdayView
Spacer()
}
.padding(.horizontal, 16)
}
}

Value Changes
If the value of these @Published properties change, it will trigger a UI update.
I tested this out with the sample app above, and indeed, every time I type on the TextField, the body gets redrawn.
My misunderstanding on Structs
I also confirmed that the struct's reference stays the same even though I have updated any of its property's value. I initially thought that the reference will be updated every time we change it since it might try to create another instance of the struct since it's a value type. But it didn't. The struct, in this case, was only created once.
But still, when I update the birthday's month, or day, or year, it triggers every time I type in a new value. So a struct that's a publisher, all of its properties will trigger a UI update.
This is the reason why we have to be careful in using ObservableObject especially when there are too many publishers, if we're not careful with our publishers and structs, we might be triggering UI update every time a value has been updated. And sometimes when there are too many UI updates, it can cause UI issues, or might even cause your app to lag (performance issues).
Some tips I got from other devs who replied on that tweet:
Use private every time we declare a variable. And only expose it when needed.
Only use the @Published attribute if we needed to update the UI when the value changes.
Comments