SwiftUI is it possible to update ObservedObject in the current view without redrawing current view?
SwiftUI is designed so that the code in your views is run multiple times so that it knows what to display, and it reruns any time a piece of state – whether it's @State
, @StateObject
, @ObservedObject
, etc. – changes. That's why you're seeing multiple print statements – it's expected behaviour.
Here, your View2
and View3
are referencing the same data source, so of course if that data source changes in one, it's going to change in the other.
So when you have a one-element array and remove that element in one view, your other view is going to be re-evaluated because it's looking at exactly the same array. If you don't want that to happen, then you should not share state between views.
The key with successfully using SwiftUI is to ensure that your views' body code can be run, and rerun, as many times as the system wants to without introducing side-effects (the throwing of an error when calling let text = dataSource.listOfThings[0]
with an empty array is one such side effect). If you try and fight against that, you'll have a devil of a time creating beautiful and performant user interfaces with SwiftUI.
SwiftUI: ObservableObject does not persist its State over being redrawn
Finally, there is a Solution provided by Apple: @StateObject
.
By replacing @ObservedObject
with @StateObject
everything mentioned in my initial post is working.
Unfortunately, this is only available in ios 14+.
This is my Code from Xcode 12 Beta (Published June 23, 2020)
struct ContentView: View {
@State var title = 0
var body: some View {
NavigationView {
VStack {
Button("Test") {
self.title = Int.random(in: 0...1000)
}
TestView1()
TestView2()
}
.navigationTitle("\(self.title)")
}
}
}
struct TestView1: View {
@ObservedObject var model = ViewModel()
var body: some View {
VStack {
Button("Test1: \(self.model.title)") {
self.model.title += 1
}
}
}
}
class ViewModel: ObservableObject {
@Published var title = 0
}
struct TestView2: View {
@StateObject var model = ViewModel()
var body: some View {
VStack {
Button("StateObject: \(self.model.title)") {
self.model.title += 1
}
}
}
}
As you can see, the StateObject
Keeps it value upon the redraw of the Parent View, while the ObservedObject
is being reset.
run when a view redraws in SwiftUI
If I correctly understood your needs then you should do this not in view but in view model, like
class ViewModel: ObservableObject {
var onModelChanged: (_ old: Model, _ new: Model) -> Void
@Published var model = Model() {
didSet {
onModelChanged(oldValue, model)
}
}
init(onModelChanged: @escaping (_ old: Model, _ new: Model) -> Void = {_, _ in}) {
self.onModelChanged = onModelChanged
}
}
so instantiating ViewModel
you can provide a callback to observe values changed in model and have old and new values, like
@StateObject var viewModel = ViewModel() {
print("Old value: \($0.someValue)")
print("New value: \($1.someValue)")
}
ObservedObject resets when I update the view using a property. Hence it leads to loss of data
SwiftUI views are structs, and therefore, immutable. When you update the state and cause the view to redraw, it actually creates a new instance of the view.
In your SongListView
you have
@ObservedObject var model: SongListViewModel = SongListViewModel()
This means that each time your SongListView
is re-drawn (which includes any time that player.isPlaying
is changed), you are initialising model
with a new instance of SongListViewModel
.
You should remove the default value and supply the model via a parameter to the initialiser of SongListView
-
@ObservedObject var model: SongListViewModel
Related Topics
API Violation - Multiple Calls Made to -[Xctestexpectation Fulfill]
How to Select All Text in a Uitextfield Using Swift
How to Make Function That Some Parameters Not Required in When Call It in iOS Swift 3
Running Swift Build in Terminal Leading to "Platform Path" Errors
Swift, Error Exc_Breakpoint (Code=1, Subcode=0X100695474)
How to Open Your App's Settings (Inside the Settings App) with Swift (iOS 11)
Why Are Properties of an Immutable Object Mutable in Swift
How to Detect Text View Begin Editing and End Editing in Swift 3
Behaviour of Protocols with Self
Swift Compiler Error Int Is Not Convertible to Cgfloat
Create a Navigationlink Without Back Button Swiftui
Swiftui - How to Get Coordinate/Position of Clicked Button
Use of Undeclared Type Autoreleasingunsafepointer Xcode 6 Beta 6
Swift 3: Safe Way to Decode Values with Nscoder
Playing Hls (M3U8) in Cocoa Os X Avplayer - Swift