@Observable
/ onChanged()
@Published
in ObservableObject
/
.onReceive()
Both perform the same on the surface, but this is causing a performance issue in my app.
I have a lot of confusion here.
-
Does
onChange(of:perform:)
destroy and create new value=view?-
init()
of child view is called when:- A property of
observableModel
is changed @State isHovered
is changed
- A property of
-
Looks like reusing? the view to redraw with new values when:
- A property of
publishedModel
is changed (received)- This doesn’t call
init()
of child view.
- This doesn’t call
How to use onReceive() like this if
@Observable
macro can replaceObservableObject
protocol? - A property of
-
-
Difference between model declaration using @Observable vs ObservableObject
If you have a list of caveats where this migration can cause problems, please share.
I was not aware that calling onChange()
initializes a view every time (even if it performs nothing!), which was/is causing performance issues in my app. Also, the biggest problem for me is: if a view has .onChange() modifier as a part of view, the all sibling views are going to be regenerated with init()
.
To avoid this, using onReceive()
with @Published
in ObservableObject
works, but I’d love to know how to do that with a model with @Observable
,
Or if there is a way to avoid recreation of view while using onChange()
that would be great.
Main View
struct ContentView: View {
@State private var isHovered: Bool = false
var publishedModel = PublishedModel()
var observableModel = ObservableModel()
var body: some View {
let _ = Self._printChanges()
HStack {
LeftView($isHovered)
RightView()
// .onReceive(publisher.$array) { value in
// print("publisher.array:", publisher.array)
// }
.onChange(of: observableModel.array) {} // This demon...
}
.padding()
.background(.white)
.onHover {
isHovered = $0
publishedModel.value = "\(isHovered)"
publishedModel.array.append(publishedModel.array.count)
observableModel.value = "\(isHovered)"
observableModel.array.append(observableModel.array.count)
}
}
}
Models
import Observation
import SwiftUI
class PublishedModel: ObservableObject {
@Published var value: String
@Published var array: [Int]
init() {
value = "Hi!"
array = [0, 1, 2, 3, 4]
}
}
@Observable
class ObservableModel {
var value: String
var array: [Int]
init() {
value = "Hi!"
array = [0, 1, 2, 3, 4]
}
}
Children View
struct LeftView: View {
@Binding var isHovered: Bool
init(_ isHovered: Binding<Bool>) {
_isHovered = isHovered
print(Date.now, "LeftView.init()")
}
var body: some View {
let _ = Self._printChanges()
Text("LeftView\n\(Date.now)")
.padding()
.background(.red)
.opacity(isHovered ? 1 : 0)
}
}
struct RightView: View {
init() {
print(Date.now, "RightView.init()")
}
var body: some View {
let _ = Self._printChanges()
Text("RightView\n\(Date.now)")
.padding()
.background(.blue)
}
}