Check object is managed, and get unmanaged from managed object and write it

Hi there,

I’m writing a project using RealmSwift,

Now I kind of know some basic rules of using Realm, and here’s the quesiton:

how can I check if an Object is mamanged already? no seeing some similar APIs;

secondly,

imagine I have Object called “RealmOrder”, which contains an Object called “RealmItem”

now I have an initializer, which looks like this:

    init(orderID: String, item: String) {
        super.init()
        orderId = orderID
        rlmItem = RealmItem.getRealmItem(item)
    }

RealmItem.getRealmItem(item) is defined as below:

    static func getRealmItem(_ name: String) -> RealmItem {
        let realm = try! Realm()
        let item = realm.object(ofType: RealmItem.self, forPrimaryKey: name)
        return item == nil ? RealmItem(name: name) : item
    }

so basically if RealmItem already has data, it will return the managed object, if not it will create a new one.

Now problem is if I want to write this RealmOrder object into realm, I will get trouble like

‘Object is already managed by another Realm. Use create instead to copy it into this Realm.’

So I want to ask, how to solve this and what’s the best practise?

Just a note that iInitializing Realm objects should be this

convenience init(orderID: String, item: String) { //note convenience
    self.init()  //note self.
    orderId = orderID
    rlmItem = RealmItem.getRealmItem(item)
}

may I ask why?

I kind of declared init() to be private, so for outsiders, only use getRealmItem() to get the item, now allowing them to create a new one

From the Realm Documentation:

Custom initializers for Object subclasses: When creating your model Object subclasses, you may sometimes want to add your own custom initialization methods for added convenience.

Due to some present limitations with Swift introspection, these methods cannot be designated initializers for the class. Instead, they need to be marked as convenience initializers using the Swift keyword of the same name:

MyModel: Object {
@objc dynamic var myValue = “”

convenience init(myValue: String) {
    self.init() //Please note this says 'self' and not 'super'
    self.myValue = myValue
}

}

To return to the first question, you should be able to check item.realm – if it is nil then the item should be an unmanaged Object.

1 Like

Hi Jay,

but when I use super, it seems everything is fine on my side. May I ask what would be the side effect?

Because I didn’t get any compiler errros nor warnings, and the run time seems functional, so I don’t understand why can’t I use super here.

I assume init() merely takes no arguments, so it would kind of behave like super.init().

anyway, I followed your suggestion, just want to ask the reason behind the scene. Thank you.

Hi Andrew,

Thank you. I found tha Java seems have the isUnmanaged API.

Second question,

‘Object is already managed by another Realm. Use create instead to copy it into this Realm.’

I don’t find too much useful information on this, like how do I copy the Object type and send it to Realm?

I found two APIs;

realm.create(T.Type)
realm.create(T.Type, value: Any, update: Realm.UpdatePolicy)

but don’t know how to ‘create’ or ‘copy’ a managed object into another Realm.

I’ve only done it it in Swift, but I added a convenience init to create a new Object instance by copying over the attributes. If the object contains embedded objects then you need similar convenience inits for those too:

extension User {
    convenience init(_ user: User) {
        self.init()
        partition = user.partition
        userName = user.userName
        userPreferences = UserPreferences(user.userPreferences)
        lastSeenAt = user.lastSeenAt
        conversations.append(objectsIn: user.conversations.map { Conversation($0) })
        presence = user.presence
    }
}

extension UserPreferences {
    convenience init(_ userPreferences: UserPreferences?) {
        self.init()
        if let userPreferences = userPreferences {
            displayName = userPreferences.displayName
            avatarImage = Photo(userPreferences.avatarImage)
        }
    }
}

Will this work with nested Object types as well?

I have seen something similar on stack overflow, but a few people mentions it doesn’t work with nested objects, though I don’t try yet.

also, what you mean by " If the object contains embedded objects then you need similar convenience inits for those too:"

thank you.

A Realm Object class can contain primitive such as strings and ints, but it can also contain other Realm objectc. In my example, the User class includes the userPreferences attribute which is UserPreferences object. Because UserPreferences is embedded within an Object rather than being a top-level Realm Object, it inherits from EmbeddedObject rather than Object

1 Like

re: self.init

Remember that Realm is backed by ObjC code and not Swift. For example Realm (objects) do not directly support computed properties (to be managed/handled by Realm), which is very common in Swift objects.

So when you create a Realm Object class UserClass: Object you are creating a UserClass of type Object, which is itself an NSObject (I am being loose here)

A designated initializer must ensure that the properties introduced by its class are initialized before it delegates up to a superclass initializer. Properties cannot be left in an intermediate state which super.init could cause (depending on how the vars are defined within the object)

The memory for an object is fully initialized once the initial state of all of its stored properties is known. To satisfy that requirement, a designated initializer must make sure that all of its own properties are initialized (self.init) before it hands off up the chain.

Without the initial value, the compiler won’t synthesize initializers for the class - which would be a version of init that calls through to the base class.

Side effects? objects that don’t behave as expected, ‘lose’ data and and generally mis-behave - and those issues are very hard to track down. Been there, done that.

1 Like

Hi Jay, thanks for reply so fruitful!

I was under the assumption that a Object instance init() is merely calling 'super.init()` if my other variables are either optional or already had a default value. Looks like I’m wrong.

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.