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?
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