Post

Replies

Boosts

Views

Activity

Reply to Is there a way to update Observable on the main thread but not to read from it?
Thanks Quinn for chiming in. You are right, app architecture is not Apple's responsibility. I guess we just need to know the intended usage of the the API. So to that end, I guess the 'ask' is the following clarifications: Are we suppose to update properties of a @Observable on the main thread? Are we suppose to properties of an object that is a property of Observable on the main thread? Is it safe to read properties of a @Observable on a background thread? Is it safe to read properties of an object that is a property of @Observable on a background thread? Is there a way to enforce check at compile time for setting properties of @Observable (for example by decorating it with @MainActor), but at the same time allow read to be not on the main thread?
Oct ’24
Reply to Is there a way to update Observable on the main thread but not to read from it?
Now I've asked chatgpt if there is a hack we can do to alleviate this problem by making write on the mainthreads but allow read to be from any thread. It came up with this solution: import Foundation class UserModel { private var _name: String private var _age: Int private let queue = DispatchQueue.main // Queue to ensure writes happen on the main thread init(name: String, age: Int) { self._name = name self._age = age } // Custom getter allows read access from any thread var name: String { return _name } var age: Int { return _age } // Custom setter ensures writes happen on the main thread func update(name: String, age: Int) { if Thread.isMainThread { self._name = name self._age = age } else { queue.async { self._name = name self._age = age } } } } @Observable class UserViewModel { private var _userModel: UserModel private let queue = DispatchQueue.main init(userModel: UserModel) { self._userModel = userModel } // Custom getter for the UserModel allows reads from any thread var userModel: UserModel { return _userModel } // Custom setter ensures writes happen on the main thread func updateUserModel(name: String, age: Int) { if Thread.isMainThread { _userModel.update(name: name, age: age) } else { queue.async { self._userModel.update(name: name, age: age) } } } } // NetworkManager can now read from any thread and only needs to jump to the main thread for writes class NetworkManager { func updateUserInfo(viewModel: UserViewModel) { Task { // Read values from UserModel without jumping to the main thread let userName = viewModel.userModel.name let userAge = viewModel.userModel.age print("Making network call with userName: \(userName) and userAge: \(userAge)") // Simulate network delay try await Task.sleep(nanoseconds: 1_000_000_000) // After the network call, update the UserModel on the main thread viewModel.updateUserModel(name: "Jane Doe", age: 31) } } } // Example usage let userModel = UserModel(name: "John Doe", age: 30) let viewModel = UserViewModel(userModel: userModel) let networkManager = NetworkManager() // Calling from a background thread Task { await networkManager.updateUserInfo(viewModel: viewModel) } Essentially wrapping each state with an internal backing and then controlling read and write. Does this approach have any issues? I dont trust chatgpt.
Oct ’24
Reply to How to access SwiftData from a background thread?
@DTS Engineer sorry I encounter another architecture question: I have been studying ModelActor and I'm trying to create one so that I can do SwiftData operations in the background (say after a network call). But according to this video https://www.youtube.com/watch?v=VG4oCnQ0bfw it seems the ModelActor itself needs to be created in the background off the main thread. I want to create one actor to be used throughout the app, and not create a new actor for each background operation. How could I do that? At first, a naive approach might be import Foundation actor ModelActor { // Your ModelActor properties and methods } class ModelActorService { static let shared = ModelActorService() private(set) var modelActor: ModelActor? private init() { // Initialize on a background queue DispatchQueue.global(qos: .background).async { self.modelActor = ModelActor() } } } // ViewModel example class SomeViewModel: ObservableObject { private let modelActor: ModelActor init(modelActor: ModelActor = ModelActorService.shared.modelActor!) { self.modelActor = modelActor } // ViewModel methods using modelActor } but that won't work because the creation of the actor is async and there's no guarantee that it would actually be ready when the viewModel wants to use it. How do I setup a actor facility that is global, created in the background, that can be used by various viewModels for background data operations?
Sep ’24
Reply to How to access SwiftData from a background thread?
@DTS Engineer Thanks for the reply, some followup questions With respect to (2): It seems from the other thread that UI updates could be bugged still. But what is the intended behaviour when it eventually get fixed? Is @Query suppose to update from changes made from other modelContexts? And vice versa? Because if that is not the intended design, and our project requires it, we might be better off going back to core data. With respect to (3): Please comment. Is icloud sync suppose to trigger for changes made both in @Query modelContext and ModelActor's custom modelContext? (both pointed to the same container).
Sep ’24