Error in Declaring Non-Optional Map Property in Realm Swift Object

65 Views Asked by At

I have this realm object with a map property:

class UserModel: Object {
       @Persisted var uploadMap = Map<String, UploadingModel>()
}

where UploadingModel is another realm object.

once I ran the app I got this error:

*** Terminating app due to uncaught exception 'RLMException',
reason: 'Map<String, UploadingModel> property 
'uploadMap' must be marked as optional.'

In the official documentation: realm-swift there is an example that initialize the map like the above without marking it as optional. Also, I use other realm types like List<UploadingModel>() and no error is thrown.

How can I avoid mark it as optional and why the error is thrown?

1

There are 1 best solutions below

2
Jay On BEST ANSWER

Super good question and the documentation is a little thin in this area.

Here's why

TL;DR

If the value part of a Map is a Realm Object, it can be nil so it must be optional

Long explanation via an example:

Start with a Dog Class and then a Class that holds all dog locations as a Map

class Dog: Object {
    @Persisted var name: String
}

class AllDogLocations: Object {
   @Persisted var dogLocation = Map<String, Dog?>() //note optional dog here
}

and then say there two dogs are persisted in Realm, Spot and Scraps and they are added to a dog locations object which is also persisted. Spot is in Ohio and Scraps is in Florida

let spotDog = Dog()
spotDog.name = "Spot"

let scrapsDog = Dog()
scrapsDog.name = "Scraps"

let all = AllDogLocations()
all.dogLocation["Ohio"] = spotDog
all.dogLocation["Florida"] = scrapsDog

try! realm.write {
    realm.add(all)
}

Image then, elsewhere in the app that Spot is deleted from Realm

let spotDog = realm.objects(Dog.self).where { $0.name == "Spot" }.first!
try! realm.write {
    realm.delete(spotDog)
}

What then happens to the AllDogsLocations? Here's what it then looks like

AllDogLocations {
    dogLocation = Map<string, Dog> <0x600003dd1810> (
    [Florida]: Dog {
            name = Scraps;
        },
    [Ohio]: <null>
    );
}

Note Spot, which used to be in Ohio, the value is nil because the object no longer exists. It's key in the map however, is left intact.

However, what would happen if the Value (Dog) was required to not be nil and then that dog was deleted from Realm

Map<String, Dog>()

Whoops - there's your problem! It must be optional because it COULD be deleted elsewhere in the app!

Note that in the case of <String, String>, setting the String to nil removes the map entry entirely as that value is required (non-optional)

There is some debate on the current behavior; what should happen if the Object is set to nil. Let it be nil? Remove it? See Effect of Deleting an Object in a Map?

And if you are using Realm Sync this Git post is very important Map: Remove Keys When Object Values Become nil #8379