Realm-cocoa: RLMException: Invalid value for property

Created on 7 Jan 2017  路  2Comments  路  Source: realm/realm-cocoa

Today I wanted to copy a local Realm Object to a synchronized realm. For that I did the following:

            let realmOld = try! Realm(configuration: configOld);

            if (!realmOld.isEmpty) {
                print("old realm isnt empty");

                let user = realmOld.objects(USER.self).filter("email = '\(email)'").first;

                try! realmOld.write {
                    user?.creation.value = HelperTime.getCurrentMillis();
                    user?.migrated.value = true;

                    print ("\(user!)");
                }

                MY_REALM_Manager.getSharedInstance().addUserCopy(user: user!);

                realmOld.deleteAll();
            } else {
                print("old realm is empty");
            }

The addUserCopy does the following:

    func addUserCopy(user: USER) {
        try! self.realm.write {
            self.realm.create(USER.self, value: user);
        }
    }

Now if I want to do it I get the following errror:

Terminating app due to uncaught exception 'RLMException', reason: 'Invalid value '' for property 'gender''

So I checked all my gender fields, there where three Gender Fields at all:

    gender = 2;
    gender = 1;
    gender = 0;

So all gender fields of my three existing users are initialized correctly, they have a valid value for this field decleration:

let gender = RealmOptional<Int>();

The default value of the gender is -1.

So why is realm complaining about an invalid value?

Most helpful comment

Fixed! The big problem here was a getter method which is reallyl interesting. In my User class I have a getter which converts the Int value for a gender to a String:

func getGender() -> String {
    switch self.gender.value!
    case Gender.male.rawValue: 
        return "male";
    case Gender.female.rawValue:
        return "female";
    default:
        return "";
}

So for some special reason which I dont know the realm is using this getter method during the copy process and is so complaing that he cant use the string "male" and set it on the gender which is of type Int.

I simply commented out that getter and everything worked fine.

All 2 comments

The problem I described above is a bit simplified. Maybe the problem occures due to the List where my users are stored in so here is the more complex thing I do. First, The Object definition:

 class REALM_USER : Object {
    let creation = RealmOptional<Int64>();
    let migrated = RealmOptional<Bool>();
    dynamic var firstname: String!;
    dynamic var lastname: String!;
    dynamic var email: String!;
    var users = List<USER>();

    required init() {
        super.init();

        self.creation.value = HelperTime.getCurrentMillis();
        self.migrated.value = false;
        self.firstname = "";
        self.lastname = "";
        self.email = "";
        self.users.removeAll();
    }

    required init(realm: RLMRealm, schema: RLMObjectSchema) {
        super.init(realm: realm, schema: schema);
    }

    required init(value: Any, schema: RLMSchema) {
        super.init(value: value, schema: schema);
    }
}

And here the USER:

class USER : Object {
    dynamic var firstname: String!;
    dynamic var lastname: String!;
    dynamic var dateOfBirth: NSDate!;
    let gender = RealmOptional<Int>();
    dynamic var email: String!;
    dynamic var phone: String!;
    dynamic var mobile: String!;
    dynamic var fax: String;
    dynamic var address: ADDRESS!;

    required init() {
        super.init();

        self.firstname = "";
        self.lastname = "";
        self.dateOfBirth = nil;
        self.gender.value = -1;
        self.email = "";
        self.phone = "";
        self.mobile = "";
        self.fax = "";
        self.address = ADDRESS();
    }

    required init(realm: RLMRealm, schema: RLMObjectSchema) {
        super.init(realm: realm, schema: schema);
    }

    required init(value: Any, schema: RLMSchema) {
        super.init(value: value, schema: schema);
    }
}

So now I got one REALM_USER with three USERS and I want to deep copy this object from a local Realm to a synchronized realm. The objects are all containing correct informations, as I mentioned also the gender property is initialized correctly with valid values.

Now I do:

let realmOld = try! Realm(configuration: configOld);

if (!realmOld.isEmpty) {
    print("old realm isnt empty");

    let realm_user = realmOld.objects(REALM_USER.self).filter("email = '\(email)'").first;

    try! realmOld.write {
        realm_user?.creation.value = 0;
        realm_user?.migrated.value = true;
    }

    MY_REALM_Manager.getSharedInstance().addRealmUserCopy(realm_user: realm_user!);

    realmOld.deleteAll();
 } else {
    print("old realm is empty");
}

So as already mentioned the app crashes. When I now delete all users and copy only the REALM_USERS with an empty list of List<USER>:

let realmOld = try! Realm(configuration: configOld);

if (!realmOld.isEmpty) {
    print("old realm isnt empty");

    let realm_user = realmOld.objects(REALM_USER.self).filter("email = '\(email)'").first;

    try! realmOld.write {
        realm_user?.creation.value = 0;
        realm_user?.migrated.value = true;
        realm_user?.users.removeAll(); // ADDED THIS LINE
    }

    MY_REALM_Manager.getSharedInstance().addRealmUserCopy(realm_user: realm_user!);

    realmOld.deleteAll();
 } else {
    print("old realm is empty");
}

The copying of the REALM_USER works fine. So that leads to the question if there is some error with deep copying a Realm List? Here again how I deep copy the object:

func addRealmUserCopy(realm_user: REALM_USER) {
    try! self.realm.write {
        self.realm.create(REALM_USER.self, value: realm_user);
    }
}

I tried to get a workaround setup but that also failed. I tried to store the USER objects in another List, delete them from the original object and tried to add them later:

let realmOld = try! Realm(configuration: configOld);

if (!realmOld.isEmpty) {
    print("old realm isnt empty");

    let realm_user = realmOld.objects(REALM_USER.self).filter("email = '\(email)'").first;
    let users = List<USER>(); // ADDED THIS LINE

    for user in (realm_user?.users)! { // ADDED THIS LINE
        users.append(user); // ADDED THIS LINE
    } // ADDED THIS LINE

    try! realmOld.write {
        realm_user?.creation.value = 0;
        realm_user?.migrated.value = true;
        realm_user?.users.removeAll(); // ADDED THIS LINE
    }

    MY_REALM_Manager.getSharedInstance().addRealmUserCopy(realm_user: realm_user!);
    MY_REALM_Manager.getSharedInstance().addUsers(realm_user: realm_user!, users: users);  // ADDED THIS LINE   

    realmOld.deleteAll();
} else {
    print("old realm is empty");
}

Where addUsers does:

func addUsers(_ realm_user: REALM_USER, _ users: List<USER>) {
    try! self.realm.write {
        realm_user.users.append(objectsIn: users);
    }
}

But this ended up with the following error:

Terminating app due to uncaught exception 'RLMException', reason: 'Cannot modify managed RLMArray outside of a write transaction'

Which I also dont understand cause you can obviously see that I use try! self.realm.write.

Fixed! The big problem here was a getter method which is reallyl interesting. In my User class I have a getter which converts the Int value for a gender to a String:

func getGender() -> String {
    switch self.gender.value!
    case Gender.male.rawValue: 
        return "male";
    case Gender.female.rawValue:
        return "female";
    default:
        return "";
}

So for some special reason which I dont know the realm is using this getter method during the copy process and is so complaing that he cant use the string "male" and set it on the gender which is of type Int.

I simply commented out that getter and everything worked fine.

Was this page helpful?
0 / 5 - 0 ratings