Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@danilValeev
Last active June 17, 2021 08:16
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save danilValeev/ef29630b61eed510ca135034c444a98a to your computer and use it in GitHub Desktop.
Save danilValeev/ef29630b61eed510ca135034c444a98a to your computer and use it in GitHub Desktop.
Easy mapping Realm's List and RealmOptional with ObjectMapper

This code helps using ObjectMapper with RealmSwift.

RealmSwift uses List<T> collection for "to many" relashionships and RealmOptional<T> for optional primitive types, but ObjectMapper can't map directly to List<T> and RealmOptional<T>.

With this operators you can properly define RealmSwift's relashionships and optional properties and use <- operator to map them.

Example

import Foundation
import RealmSwift
import ObjectMapper

class Chat: Object, Mappable {

    let messages = List<Message>()

    func mapping(map: Map) {
        // use <- operator to map a List
        messages <- map["messages"]
    }

    required convenience init?(map: Map) {
        self.init()
    }
    
}

class Message: Object, Mappable {

    dynamic var text: String? = nil

    let length = RealmOptional<Int>()

    func mapping(map: Map) {
        text <- map["text"]
        // use <- operator to map a RealmOptional
        length <- map["length"]
    }

    required convenience init?(map: Map) {
        self.init()
    }
    
}

Swift 3

import Foundation
import RealmSwift
import ObjectMapper

infix operator <-

/// Object of Realm's List type
public func <- <T: Mappable>(left: List<T>, right: Map) {
    var array: [T]?

    if right.mappingType == .toJSON {
        array = Array(left)
    }

    array <- right

    if right.mappingType == .fromJSON {
        if let theArray = array {
            left.append(objectsIn: theArray)
        }
    }
}

/// Object of Realm's RealmOptional type
public func <- <T>(left: RealmOptional<T>, right: Map) {
    var optional: T?

    if right.mappingType == .toJSON {
        optional = left.value
    }

    optional <- right

    if right.mappingType == .fromJSON {
        if let theOptional = optional {
            left.value = theOptional
        }
    }
}

Swift 2

infix operator <- {}

/// Object of Realm's List type
public func <- <T: Mappable>(left: List<T>, right: Map) {
    var array: [T]?

    if right.mappingType == .ToJSON {
        array = Array(left)
    }

    array <- right

    if right.mappingType == .FromJSON {
        if let theArray = array {
            left.appendContentsOf(theArray)
        }
    }
}

/// Object of Realm's RealmOptional type
public func <- <T>(left: RealmOptional<T>, right: Map) {
    var optional: T?

    if right.mappingType == .ToJSON {
        optional = left.value
    }

    optional <- right

    if right.mappingType == .FromJSON {
        if let theOptional = optional {
            left.value = theOptional
        }
    }
}
@kdawgwilk
Copy link

By using this we have to change our List properties from let (Realm recommends) to var will this have any side effects?

@danilValeev
Copy link
Author

@kdawgwilk The pros of this approach is that you have not to change a List properties from let to var. It is not recommended.

@RajaveluC
Copy link

@danilValeev

I am using the above swift 3.0 and I have added your file to my project, and I still couldn't get around parsing nested objects successfully.

What is wrong here?

Here are my classes.

class User : Object, Mappable {

    dynamic var userId: String!
    dynamic var emailId: String!
    
    override class func primaryKey() -> String? {
        return "userId"
    }
    
    required convenience init?(map: Map) {
        self.init()
    }
    
    func mapping(map: Map) {
        userId <- map["userId"]
        emailId <- map["email"]
    }
}

class ContactGroup : Object, Mappable {
    dynamic var groupId: String!
    var users = List<User> ()
    
    override class func primaryKey() -> String? {
        return "groupId"
    }
    
    required convenience init?(map: Map) {
        self.init()
    }
    
    func mapping(map: Map) {
        groupId <- map["contactGroupId"]
        users <- map["users"]
    }
}

func storeContactGroups (groups : NSArray)  {
        var groupRealms = Array<ContactGroup> ()
        for object in groups {
            let group : ContactGroup! = ContactGroup(JSON: object as! [String : Any])
            groupRealms.append(group)
        }
        
        let realm = try! Realm()
        try! realm.write {
            for group in groupRealms {
                realm.add(group, update: true)
            }
        }
 }

After the storeContactGroups is executed, I still see zero Users in my Realm file under ContactGroup. What am I doing wrong here?

screen shot 2016-12-21 at 2 52 23 pm

@kubatruhlar
Copy link

kubatruhlar commented Feb 15, 2017

Not working with Swift 3, array <- right will produce nil. (list type)

@danilValeev
Copy link
Author

@RajaveluC Have you found an issue? Can you provide json example?

@danilValeev
Copy link
Author

@kubatruhlar Works for me. There are two parts: first is for Swift 3. Can you provide the code that doesn't work?

@madawei2699
Copy link

@kubatruhlar The List(), the Object must confirm Mappable class.

@artem-shmatkov
Copy link

@danilValeev, Thanks, really helpful!

@euroboy
Copy link

euroboy commented Jun 17, 2021

Getting an error: Reference to generic type 'Map' requires arguments in <...> after updating to Realm version 10.8.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment