In our app we use a UserService
that is a ObservableObject
and passed as environment. A synced realm is opened and the app user (a RealmObject) is obtained using flexible sync.
When updating the users properties, such as his username, the view does not get redrawn. This is against my expectations since UserService
contains a @Published
property where the user (that is being edited) is stored. On the database it clearly shows the property being edited, however the view does not get redrawn, only when restarting the app the new properties are shown.
What would be the best way to have a UserService
objects taking care of all user related logic (storing a user object (reference to it), containing functions to update, …) and use this to display the active data of this user throughout the views?
Here is a MRE (the login logic is left out to reduce complexity):
import SwiftUI
import RealmSwift
class UserService2: ObservableObject {
var realm: Realm
@Published var ownUser: User
init(realm: Realm, ownUser: User) {
self.realm = realm
self.ownUser = ownUser
}
func changeName(newName: String) {
do {
try self.realm.write {
self.ownUser.userName = newName
}
} catch {
print("error")
}
}
}
struct TestView: View {
@EnvironmentObject var userService: UserService2
var body: some View {
VStack {
Text(userService.ownUser.userName ?? "no name")
Button {
userService.changeName(newName: Date().description)
} label: {
Text("change name")
}
}
}
}
struct ContentView: View {
var realm: Realm? = nil
init() {
let flexSyncConfig = app.currentUser!.flexibleSyncConfiguration(initialSubscriptions: { subs in
subs.append(
QuerySubscription<User>(name: "ownUserQuery") {
$0._id == "123"
})
})
do {
let realm = try Realm(configuration: flexSyncConfig)
self.realm = realm
} catch {
print("sth went wrong")
}
}
var body: some View {
if let realm = realm, let ownUser = realm.objects(User.self).where( { $0._id == "123" } ).first {
TestView()
.environmentObject(UserService2(realm: realm, ownUser: ownUser))
} else {
ProgressView()
}
}
}
The User
Object looks like this
import Foundation
import RealmSwift
class User: Object, ObjectKeyIdentifiable {
@Persisted(primaryKey: true) var _id = UUID().uuidString
@Persisted var userName: String?
convenience init(_id: String? = nil, userName: String? = nil) {
self.init()
if let _id = _id {
self._id = _id
}
self.userName = userName
}
}
P.S. I assume I could observe changes on the object using realm and somehow force a view refresh, but I would find it much more clean using the already existing way to watch for changes and redraw views when needed using @Published
…
P.P.S. This user object is created on the server using a trigger when someone authenticates. However, I assume this is not really relevant to this problem.