Superb library! It helped me so much til now!
The only problem I have right now is mapping List
In my JSON a single object looks like this
"arretsPhysiques": [
{
"codeArret": "31DC",
"coordonnees": {
"altitude": 0,
"latitude": 46.20166851756986,
"longitude": 6.161853747996533
},
"ligneDestinations": {
"ligneDestination": [
{
"destination": "Jar.-Botanique",
"destinationMajuscule": "JAR. BOTANIQUE",
"ligne": 1
},
{
"destination": "Gare Cornavin",
"destinationMajuscule": "GARE CORNAVIN",
"ligne": 9
}
]
},
"mnemol": "31DC00",
"nomArret": "31-d茅c"
}
...
]
And model like so:
class Stop: Object, Mappable {
dynamic var physicalStopCode = ""
dynamic var stopCode = ""
dynamic var stopName = ""
dynamic var coordinates = Coordinates()
dynamic var connections = List<Connection>()
dynamic var distance: Double = 0.0
override class func primaryKey() -> String? {
return "physicalStopCode"
}
override static func ignoredProperties() -> [String] {
return ["distance"]
}
required convenience init?(_ map: Map) {
self.init()
mapping(map)
}
func mapping(map: Map) {
physicalStopCode <- map["codeArret"]
stopCode <- map["mnemol"]
stopName <- map["nomArret"]
coordinates <- map["coordonnees"]
connections <- map["ligneDestinations"]
}
}
I'm able to map every single property but ligneDestination
which is strangely nested inside ligneDestinations
Mapped object returns empty List<Connection>
. I also tried connections <- map["ligneDestinations.ligneDestination"]
to get this nested array of object but with no luck.
So does ObjectMapper supports List type? Or any suggestions? Thanks! :)
I don't think objectmapper supports realm's List type. I am using objectmapper on a realm project, too. My workaround is to map the json to an ignored array property(by realm), then write a loop to add all the elements in the array to the realm list
Yes that's right, ObjectMapper does not support Realm's List type. What @cezheng suggested sounds like a good solution. It would look something like:
func mapping(map: Map) {
var destinations = Array<Connection>()
destinations <- map["ligneDestinations"]
for conn in destinations {
connections.append(conn)
}
}
Finally I went with TransformOf
to make this work. Thanks for your help
Good idea! :+1:
Glad to help
@staticdreams could you provide your implementation of TransformOf
for the Realm.List
type?
@schickling , sure! It looks like this.
let transform = TransformOf<List<Connection>, [AnyObject]>(fromJSON: { (value: [AnyObject]?) -> List<Connection>? in
let connections = List<Connection>()
if let line:[AnyObject] = value {
for item in line {
var connection = Connection()
if let destination = item.valueForKey("destinationName") as? String, let destinationCode = item.valueForKey("destinationCode") as? String {
connection.destinationName = destination
connection.destinationCode = destinationCode
if let line = item.valueForKey("lineCode") as? String {
connection.lineCode = line
}
if let line = item.valueForKey("lineCode") as? Int {
connection.lineCode = String(line)
}
}
connections.append(connection)
}
return connections
}
return nil
}, toJSON: { (value: List<Connection>?) -> [AnyObject]? in
return nil
})
Thanks!
@tristanhimmelman would this be interesting to add as an "official" Transform since Realm is picking up more and more traction?
An official transform for the Realm List type is a great idea... the above example however, I think is too specific for most use cases.
Any ideas for a more general implementation?
I might be late to the party, but I solved this by creating a generic Transform class for ObjectMapper, find the code here: https://gist.github.com/Jerrot/fe233a94c5427a4ec29b
This avoids the need of ignoring properties, just transform them directly like this:
products <- (map["products"], ArrayTransform<ProductModel>())
@jpsim @Jerrot The problem I ran into last night is that Realm cannot detect when a List
property has been set. This is why they say in their docs to always mark them as let
and use the append
function which calls down to the lists underlying realm. In the transform example, you are creating a new List
which does NOT have a Realm
associated with it and therefore those objects will not actually be saved correctly. I struggled with this for hours last night and finally got all my unit tests to pass after doing something like:
// Lists
public var items: List<BookingItem> = List() {
willSet(newValue) { items.removeAll() }
didSet { items.forEach(oldValue.append) }
}
public var fees: List<BookingFee> = List() {
willSet(newValue) { fees.removeAll() }
didSet { fees.forEach(oldValue.append) }
}
public var users: List<BookingUser> = List() {
willSet(newValue) { users.removeAll() }
didSet { users.forEach(oldValue.append) }
}
override public func mapping(map: Map) {
super.mapping(map)
items <- (map["items"], ListTransform<BookingItem>())
fees <- (map["fees"], ListTransform<BookingFee>())
users <- (map["users"], ListTransform<BookingUser>())
}
This is obviously not ideal (but believe it or not it actually works)... can anyone think of a better way of dealing with this?
Okay I slightly lied, for some reason if map["xyz"] is nil, it resets the property to an empty list. No clue how it's doing that... Only seems to happen lists
Okay I figured it out... ObjectMapper will set the property to itself. So instead I made an extension:
// MARK: - List
extension List {
public func replace(list: List, delete: Bool = false) {
if list === self { return }
removeAll()
appendContentsOf(list)
}
}
And then I can just use:
public var images: List<Image> = List() {
willSet { images.replace(newValue) }
}
Can Anyone Please help me to Map and Realm with following Classes. Main problem is I need List of Int but Realm doesn't support List of Int type so kind of stuck....
class OfficeInformationData: Object, Mappable{
dynamic var id: String?
dynamic var officeName: String?
dynamic var titlePost: String?
dynamic var location: LocationData?
dynamic var jobType = 0
var officeTiming: OfficeTimingData?
dynamic var timestamp: Int = 0
dynamic var officeiLoop:Bool = false
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
officeName <- map["officeName"]
titlePost <- map["titlePost"]
location <- map["location"]
jobType <- map["jobType"]
officeTiming <- map["officeTimings"]
timestamp <- map["timestamp"]
officeiLoop <- map["officeiLoop"]
}
}
class OfficeTimingData: Mappable{
var fromHour:String?
var toHour:String?
var days: [Int]?
required init?(_ map: Map) {
}
func mapping(map: Map) {
fromHour <- map["fromHour"]
toHour <- map["toHour"]
days <- map["days"]
}
}
I've created an operator which properly fixes this issue. Please, check https://gist.github.com/danilValeev/ef29630b61eed510ca135034c444a98a
_UPDATE (thanks @gregkerzhner for the note):_
With this operator you don't need any additional code or transforms.
list <- map["name"]
Here is Swift 3 version. To see the version for Swift 2 and an example of usage please check the gist.
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)
}
}
}
@danilValeev thanks for this. Just to clarify, because I wasted a bit of time on this, when using the operator above, you do _not_ also use a list transform. Here is the full usage example
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.appendContentsOf(theArray)
}
}
}
class MinionArmy: Object, Mappable {
//make sure the list is an immutable let constant per new Realm guidelines
let minions = List<Minion>()
func mapping(map: Map) {
//no transformation here, since the custom operator above takes care of the transforming to list
minions <- map["minions"]
}
}
@gregkerzhner thanks for the example - I used a combination of this and @danilValeev's operator to make this work in Swift 3
Mappable can be replaced by BaseMappable (to make it also works with StaticMappable)
@jeffsorr could you share your Swift 3 version?
Here's the pattern I use in Swift 3:
import Foundation
import RealmSwift
import ObjectMapper
/// Maps object of Realm's List type
private 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)
}
}
}
class MinionArmy: Object, Mappable
{
//make sure the list is an immutable let constant per new Realm guidelines
let minions = List<Minion>()
required convenience init?(map: Map) //required by Mappable protocol
{
// This function can be used to validate JSON prior to mapping. Return nil to cancel mapping
self.init()
}
func mapping(map: Map) //mapping of JSON values to object variables
{
//no transformation here, since the custom operator above takes care of the transforming to list
minions <- map["minions"]
}
}
@jeffsorr Maybe +-
operator would be better since you are appending not assigning.
I've created the pod, maybe it would help https://github.com/NikKovIos/ObjectMapper-RealmSwift
In Reaml 3.0 version in Swift, it seems there's no need for this extension. Just change the list from let
to var
Most helpful comment
Here's the pattern I use in Swift 3: