3

I am using ObjectMapper for a while and I find it cumbersome to write map function for classes with lots of property by using the way described in documents:

func mapping(map: Map) {
    username    <- map["username"]
    age         <- map["age"]
    weight      <- map["weight"]
    array       <- map["array"]
    dictionary  <- map["dictionary"]
    bestFriend  <- map["bestFriend"]
    friends     <- map["friends"]
}

I wonder if it is possible to use reflection to write map function like below assuming my JSON data and my class have axactly same property names:

func mapping(map: Map) {
    let names = Mirror(reflecting: self).children.flatMap { $0.label }
    for name in names {
        self.value(forKey: name) <- map[name]
    }
}

UPDATE:

Based on Sweeper's answer I have updated my code:

func mapping(map: Map) {
    for child in Mirror(reflecting: self).children.compactMap({$0}) {
        child <- map[child.label]
    }
}

I guess this should work.

UPDATE 2:

Thanks to Sweeper, I have find out my initial guess was wrong and Child is just a typealias for a touple:

public typealias Child = (label: String?, value: Any)

So my second attempt is also won't work.

Olcay Ertaş
  • 5,987
  • 8
  • 76
  • 112

1 Answers1

1

The <- operator is declared like this:

public func <- <T: RawRepresentable>(left: inout T, right: Map) {
    left <- (right, EnumTransform())
}

As you can see, the left parameter is declared inout. This means that you must use a mutable variable there, not the return value of some method.

So you do need to write all the properties.

I found this plugin that generates the mapping for you: https://github.com/liyanhuadev/ObjectMapper-Plugin

In Swift 4, Codable is introduced and yet automatically works things out for you:

struct Foo: Codable {
    var username: String
    var age: Int
    var weight: Double

    // everything is done for you already! You don't need to write anything else
}
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • 1
    @OlcayErtaş I doubt that works either... Does it? `child` is just a copy of the actual property. Mutating it doesn't affect the actual property. – Sweeper May 15 '18 at 07:23
  • So in Swift reflection, there is no way to get actual property by name rigth? – Olcay Ertaş May 15 '18 at 07:32
  • 1
    @OlcayErtaş You can get the property's value by name, but to change the value of the property is hard. I found this: https://stackoverflow.com/questions/31589405/using-reflection-to-set-object-properties-without-using-setvalue-forkey You can probably make that work with Object Mapper, but it's _really_ fragile and ugly. You kind of lose the whole point of using Object Mapper. – Sweeper May 15 '18 at 07:35