swiftdictionaryrealm

Use Map<String, SomeClass> in RealmSwift?


Code example:

@objcMembers
class Obj1: Object {
    dynamic var obj2: Obj2?
}

@objcMembers
class Obj2: Object {
    dynamic var obj3: Obj3?
}

@objcMembers
class Obj3: Object {
    dynamic var val: String?
    dynamic var valMap: Map<String, String>?
}

try! realm.write {
            let obj1 = Obj1()
            obj1.obj2 = .init()
            obj1.obj2?.obj3 = .init()
            obj1.obj2?.obj3?.val = "test"
            obj1.obj2?.obj3?.valMap = .init()
            obj1.obj2?.obj3?.valMap?["testKey"] = "testValue"
            realm.add(obj1)
        }

try! RealmManager.shared.realm.write {
            let requestedObj1 = realm.objects(Obj1.self).first!
            ...
}

I check every value inside requestedObj1 and see each data I've stored previously except of Map object and its values - it just becomes nil after assignment.

Possibly there are similar bugs exist but I can't found it - documentation doesn't show a concrete example of how to use Map and in the same time Map is similar to Swift's map for collections and sequences which make searching almost impossible.

Tried to replace dynamic var valMap with @Persisted var, let and etc. and got random errors which doesn't allow me to use it. In the best case everything works but Map remains nil or empty.

In general how to store dictionaries in RealmSwift? According to documentation RealmSwift supports storing Object subclasses inside Map but there is no concrete example:

https://www.mongodb.com/docs/realm-sdks/swift/latest/Classes/Map.html


Solution

  • I would first suggest using Swift at this point as it's quite a bit more streamlined.

    To accomplish this in ObjC, there are two things that need to change; let and = to initialize. Your current Obj3 is this

    @objcMembers
    class Obj3: Object {
        dynamic var val: String?
        dynamic var valMap: Map<String, String>?
    }
    

    so update it to this (pick either property; non-optional or optional)

    @objcMembers
    class Obj3: Object {
        dynamic var val: String?
        let valMap = Map<String, String>() //non-optional value
        let valMapOptionalValue = Map<String, String?>() //optional value
    }
    

    which can be initialized thusly

    let myObject = Obj3()
    myObject.valMap["testKey"] = "testValue"
    myObject.valMapOptionalValue["Hello"] = "World"
    
    try! realm.write {
        realm.add(myObject)
    }
    

    and then one thing to note about a Map Map<String, String>?

    Maps work similar to a Dictionary; an element cannot exist without a key. e.g. having an element with a nil key is 'not possible'. So making a Map optional would fall into that same structure; it cannot exist without a key so the element would never be optional.