Observation

RSS for tag

Make responsive apps that update the presentation when underlying data changes.

Posts under Observation tag

45 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

@State ViewModel memory leak in iOS 17 (new Observable)
Our app has an architecture based on ViewModels. Currently, we are working on migrating from the ObservableObject protocol to the Observable macro (iOS 17+). The official docs about this are available here: https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro Our ViewModels that were previously annotated with @StateObject now use just @State, as recommended in the official docs. Some of our screens (a screen is a SwiftUI view with a corresponding ViewModel) are presented modally. We expect that after dismissing a SwiftUI view that was presented modally, its corresponding ViewModel, which is owned by this view (via the @State modifier), will be deinitialized. However, it seems there is a memory leak, as the ViewModel is not deinitialized after a modal view is dismissed. Here's a simple code where ModalView is presented modally (through the .sheet modifier), and ModalViewModel, which is a @State of ModalView, is never deinitialized. import SwiftUI import Observation @Observable final class ModalViewModel { init() { print("Simple ViewModel Inited") } deinit { print("Simple ViewModel Deinited") // never called } } struct ModalView: View { @State var viewModel: ModalViewModel = ModalViewModel() let closeButtonClosure: () -> Void var body: some View { ZStack { Color.yellow .ignoresSafeArea() Button("Close") { closeButtonClosure() } } } } struct ContentView: View { @State var presentSheet: Bool = false var body: some View { Button("Present sheet modally") { self.presentSheet = true } .sheet(isPresented: $presentSheet) { ModalView { self.presentSheet = false } } } } #Preview { ContentView() } Is this a bug in the iOS 17 beta version or intended behavior? Is it possible to build a relationship between the View and ViewModel in a way where the ViewModel will be deinitialized after the View is dismissed? Thank you in advance for the help.
3
12
3.0k
Jul ’24
@Observable does not conform with Equatable (and Hashable)
Since Xcode 15 beta 5, making a class with the @Observable macro no longer requires all properties to have an initialization value, as seen in the video. Just put an init that collects the properties and everything works correctly. @Observable final class Score: Identifiable { let id: Int var title: String var composer: String var year: Int var length: Int var cover: String var tracks: [String] init(id: Int, title: String, composer: String, year: Int, length: Int, cover: String, tracks: [String]) { self.id = id self.title = title self.composer = composer self.year = year self.length = length self.cover = cover self.tracks = tracks } } But there is a problem: the @Observable macro makes each property to integrate the @ObservationTracked macro that seems not to conform the types to Equatable, and in addition, to Hashable. Obviously, being a feature of each property, it is not useful to conform the class in a forced way with the static func == or with the hash(into:Hasher) function that conforms both protocols. That any class we want to be @Observable does not conform to Hashable, prevents any instance with the new pattern to be usable within a NavigationStack using the data driven navigation bindings and the navigationDestination(for:) modifier. I understand that no one has found a solution to this. If you have found it it would be great if you could share it but mainly I am making this post to invoke the mighty developers at Apple to fix this bug. Thank you very much. P.S. - I also posted a Feedback (FB12535713), but no one replies. At least that I see.
3
4
2.2k
Apr ’24
Binding with ForEach or List doesn't work anymore when using @Observable macro
Hi. The binding in a ForEach or List view doesn't work anymore when using the @Observable macro to create the observable object. For example, the following are the modifications I introduced to the Apple's example called "Migrating from the Observable Object Protocol to the Observable Macro" https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro struct LibraryView: View { @Environment(Library.self) private var library var body: some View { List($library.books) { $book in BookView(book: book) } } } All I did was to add the $ to turn the reference to library.books into a binding but I got the error "Cannot find '$library' in scope" Is this a bug or the procedure to use binding in lists changed? Thanks
3
0
2.1k
Oct ’24
Observation and MainActor
Previously, it was recommended to use the @MainActor annotation for ObservableObject implementation. @MainActor final class MyModel: ObservableObject { let session: URLSession @Published var someText = "" init(session: URLSession) { self.session = session } } We could use this as either a @StateObject or @ObservedObject: struct MyView: View { @StateObject let model = MyModel(session: .shared) } By moving to Observation, I need to the @Observable macro, remove the @Published property wrappers and Switch @StateObject to @State: @MainActor @Observable final class MyModel { let session: URLSession var someText = "" init(session: URLSession) { self.session = session } } But switching from @StateObject to @State triggers me an error due to a call to main-actor isolated initialiser in a synchronous nonisolated context. This was not the case with @StateObject of @ObservedObject. To suppress the warning I could : mark the initializer as nonisolated but it is not actually what I want Mark the View with @MainActor but this sounds odd Both solutions does not sound nice to my eye. Did I miss something here?
9
3
6.5k
Apr ’24
@Observable and didSet?
I'm in the process of migrating to the Observation framework but it seems like it is not compatible with didSet. I cannot find information about if this is just not supported or a new approach needs to be implemented? import Observation @Observable class MySettings { var windowSize: CGSize = .zero var isInFullscreen = false var scalingMode: ScalingMode = .scaled { didSet { ... } } ... } This code triggers this error: Instance member 'scalingMode' cannot be used on type 'MySettings'; did you mean to use a value of this type instead? Anyone knows what needs to be done? Thanks!
9
3
4.0k
Jan ’25