Can't have notification to work on new objects


#1

I am creating a realm object in swift 4 and am trying to observe it for changes as per the latest documentation.

Realm object:

import Foundation
import RealmSwift

class Thing: Object{
    @objc dynamic var id = UUID().uuidString
    @objc dynamic var descriptionText: String = "xyxyx"
    override static func primaryKey() -> String? {
        return "id"
}

Remote Realm

extension Realm {
    static func sharedDataRealm(callback: @escaping (Realm?, Swift.Error?) -> Void) {
        SyncUser.logIn(with: Constants.syncCredentials, server: Constants.remoteAuthServer) { (remoteUser, error) in
            if let user = remoteUser {
                let sharedConfig = user.configuration(realmURL: Constants.remoteSharedRealmServer, fullSynchronization: true)
                Realm.asyncOpen(configuration: sharedConfig, callback: callback)
            } else {
                callback(nil, error)
            }
        }
    }
}

Database layer

class Database {
    static let shared: Database = Database()
    private init(){}
    
    func saveThing(thing: Thing){
        Realm.sharedDataRealm(callback: { (remoteRealm, error) in
            if let realm = remoteRealm {
                do { try realm.write { realm.add(thing, update: true)}
                } catch {
                    print("error \(error)")
                    return
                }
            }
        })
    }

func updateText(onThing: Thing){
        Realm.sharedDataRealm(callback: { (remoteRealm, error) in
            if let realm = remoteRealm {
                do { try realm.write { onThing.descriptionText = "\(arc4random())"}
                } catch {
                    print("*** error \(error)")
                    return
                }
            }
        })
    }
}

Problematic observer code:

let thing = Thing()
        Database.shared.saveThing(thing: thing)
        _ = thing.observe { (change) in
            switch change {
            case .change(let properties):
                for property in properties {
                    print("property \(property.name) has new value \(property.newValue as! String)")
                }
            case .error(let error):
                print(" an error occured: \(error)")
            case . deleted:
                print(" thing was deleted")
            }
        }

Other observer code (with delay added…)

override func viewDidLoad() {
    super.viewDidLoad()
    var thing = Thing()
    print ("*** created \(thing.descriptionText)")
    Database.shared.saveThing(thing: thing)
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(5000)) {
        _ = thing.observe { (change) in
            switch change {
            case .change(let properties):
                for property in properties {
                    print("*** property \(property.name) has new value \(property.newValue as! String)")
                }
            case .error(let error):
                print("*** an error occured: \(error)")
            case . deleted:
                print("*** situation was deleted")
            }
        }
    }
    Database.shared.updateText(onThing: thing)
}

The “problematic observer code” crashes with “reason: ‘Only objects which are managed by a Realm support change notifications’” at the “thing.observe(…)” line. The “Other observer code” does not crash, because I introduce a delay from the moment I added the object to the Realm and the moment that I start to observe it. However the change on the “thing” object still does not trigger any notification and the print of the new property is not run.

  1. Is there a better way to start observing the new object after its creation without hard coding delays?
    2. Why there is no notification when the updateText() function is executed, even after the observer gets attached?..
    Thank you

#2

I am not seeing that you are using .subscribe which has caught us multiple times.

    let realm = RealmService //get a ref to the ROS
    
    let thingResults = realm.objects(Thing.self)
    let subscription = thingResults.subscribe() //data not available until this runs
    
    for aThing in thingResults {
        print(aThing.descriptionText)
    }

#3

Yeah, also you can only subscribe on realm objects. )hats an another issue that fixes :slight_smile:


#4

Hi guys and thank you for your help.
@jay - your code does not reproduce my issue. I am trying to save/create the Realm object and then in the next instruction - subscribe to it. Your code is just retrieving a previously created object (which is managed already), so it does not help for my specific issue… And idea on my use case? Basically there should be some sort of a “Realm.addAndObserve()” method, in case one needs to start observing a newly managed realm object.
@freeubi and @jay: I don’t understand why I should subscribe to an object, when the observer is supposed to be notified as well. I quote this code from the Realm official blog at https://realm.io/blog/realm-guest-post-the-things-ive-learned-using-realm-by-Gabor-Varadi/, you can see that there is no subscribe, observe code should react when Realm notifies it:

let realm = try! Realm()
let results = try! Realm().objects(Cat.self)
var notificationToken: NotificationToken?
override func viewDidLoad() {
super.viewDidLoad()
// …
self.notificationToken = results.observe {
(changes: RealmCollectionChange) in // … (handle changes)
}
// call notificationToken?.invalidate() when needed
} ```


#5

You made a realm object but missed to add a realm.
So, if it’s a not realm managed object, then the realm, who is the notification sender cant send you a notification because it doesn’t know about the object.
This means that you need to retrieve an object from realm, then it should work the subscription.


#6

@freeubi I am not sure I understand… I am saving the “thing” object to the Realm (which will add it to the Realm, hence make it a “Realm managed” object) right before calling the observe function. How could I “miss to add a realm” then?

Database.shared.saveThing(thing: thing) //this calls realm.add() function as can be seen in my code
_ = thing.observe { (change) in


#7

I went round and round with this and the documentation is about as clear as mud (IMO).

After working with support for several weeks the bottom line is, when working with ROS, creating a subscribe registers the query with the server to which you will then receive observing events.

My example above was not to duplicate your issue - it’s how support said to do it. Since that time, it’s worked fine as long as we do both observe and subscribe.

See Subscribe to data section in the docs

https://docs.realm.io/platform/using-synced-realms/syncing-data

If you’ll notice this example from the docs, there’s a subscribe and then observe

let results = realm.objects(Person.self).filter("age > 18")
let subscription = results.subscribe()
let subscriptionToken = subscription.observe(\.state) { state in

let resultsToken = results.observe() { changes in

#8

Thanks a lot @jay. I agree that documentation is not clear for Realm Cloud and examples that I could find online are most of the times not relevant for the latest version of Realm. I will give it a try as you suggest, but after I “lost” almost a month with Realm Cloud, expecting it to work the same way as Realm database, I can say that it is not mature enough. The promise of Realm is to simplify things, so far I did not feel it happening, I had to dig deeper and deeper into the guts of their data layer instead of having it working transparently and seamlessly for me. Now I am going to try AWS AppSync instead, as much as I tried to avoid it until now, I managed to achieve so much functionality with AppSync and Amplify, with a much cleaner code, in such a short duration of time that it looks much more promising than Realm Cloud at the moment. Sorry for the rant… :slight_smile:


#9

@zene I agree with those points. It has been an ongoing challenge to make it work reliably and consistently. It’s hard to believe they collect money during development cycles for a product that should still be in beta (IMO). Many other companies have a free tier for developers (Firebase, Mongo, Couchbase) to get started - even AWS.

It would really help if the documentation was not lagging so far behind the released product. Providing tested and current code in the documentation would be a huge improvement.

There are simply a number of items that appear to have been overlooked in the ROS - fault recovery, consistent documentation, and record or object locking during write cycles really need to be addressed.

We’re taking a look at AWS as well - the big plus here for Realm is macOS Support which is a bit weak in AWS. (well, it’s not really supported at all).