Skip to content

Noobish1/KeyedMapper

Repository files navigation

Build Status codebeat badge codecov CocoaPods

KeyedMapper

⚠️ NOTE: This library has been superseded by the Codable protocol, I would suggest using that instead.

KeyedMapper was inspired heavily by Lyft's Mapper. I wanted something like Mapper but with enums for keys and support for things like NilConvertible and ReverseMappable so I made KeyedMapper.

Requirements

  • Xcode 11
  • Swift 5 (as of 1.13, prior versions support Swift 3)
  • iOS 8+

Usage

Convertible
extension NSTimeZone: Convertible {
    public static func fromMap(_ value: Any) throws -> NSTimeZone {
        guard let name = value as? String else {
            throw MapperError.convertible(value: value, expectedType: String.self)
        }
        
        guard let timeZone = self.init(name: name) else {
            throw MapperError.custom(field: nil, message: "Unsupported timezone \(name)")
        }
        
        return timeZone
    }
}
NilConvertible
enum NilConvertibleEnum {
    case something
    case nothing
}

extension NilConvertibleEnum: NilConvertible {
    static func fromMap(_ value: Any?) throws -> NilConvertibleEnum {
        if let _ = value {
            return .something
        } else {
            return .nothing
        }
    }
}
DefaultConvertible
enum DefaultConvertibleEnum: Int, DefaultConvertible {
    case firstCase = 0
}
Mappable
struct SubObject {
    let property: String
}

extension SubObject: Mappable {
    enum Key: String, JSONKey {
        case property
    }

    init(map: KeyedMapper<SubObject>) throws {
        self.property = try map.from(.property)
    }
}

extension SubObject: ReverseMappable {
    func toKeyedJSON() -> [SubObject.Key : Any?] {
        return [.property : property]
    }
}

struct Object {
    let property: String
    let optionalProperty: String?
    let convertibleProperty: NSTimeZone
    let optionalConvertibleProperty: NSTimeZone?
    let nilConvertibleProperty: NilConvertibleEnum
    let arrayProperty: [String]
    let optionalArrayProperty: [String]?
    let mappableProperty: SubObject
    let optionalMappableProperty: SubObject?
    let defaultConvertibleProperty: DefaultConvertibleEnum
    let optionalDefaultConvertibleProperty: DefaultConvertibleEnum?
    let twoDArrayProperty: [[String]]
    let optionalTwoDArrayProperty: [[String]]?
}

extension Object: Mappable {
    enum Key: String, JSONKey {
        case property
        case optionalProperty
        case convertibleProperty
        case optionalConvertibleProperty
        case nilConvertibleProperty
        case arrayProperty
        case optionalArrayProperty
        case mappableProperty
        case optionalMappableProperty
        case defaultConvertibleProperty
        case optionalDefaultConvertibleProperty
        case twoDArrayProperty
        case optionalTwoDArrayProperty
    }
    
    init(map: KeyedMapper<Object>) throws {
        self.property = try map.from(.property)
        self.optionalProperty = map.optionalFrom(.optionalProperty)
        self.convertibleProperty = try map.from(.convertibleProperty)
        self.optionalConvertibleProperty = map.optionalFrom(.optionalConvertibleProperty)
        self.nilConvertibleProperty = try map.from(.nilConvertibleProperty)
        self.arrayProperty = try map.from(.arrayProperty)
        self.optionalArrayProperty = map.optionalFrom(.optionalArrayProperty)
        self.mappableProperty = try map.from(.mappableProperty)
        self.optionalMappableProperty = map.optionalFrom(.optionalMappableProperty)
        self.defaultConvertibleProperty = try map.from(.defaultConvertibleProperty)
        self.optionalDefaultConvertibleProperty = map.optionalFrom(.optionalDefaultConvertibleProperty)
        self.twoDArrayProperty = try map.from(.twoDArrayProperty)
        self.optionalTwoDArrayProperty = map.optionalFrom(.optionalTwoDArrayProperty)
    }
}

let JSON: NSDictionary = [
    "property" : "propertyValue",
    "convertibleProperty" : NSTimeZone(forSecondsFromGMT: 0).abbreviation as Any,
    "arrayProperty" : ["arrayPropertyValue1", "arrayPropertyValue2"],
    "mappableProperty" : ["property" : "propertyValue"],
    "defaultConvertibleProperty" : DefaultConvertibleEnum.firstCase.rawValue,
    "twoDArrayProperty" : [["twoDArrayPropertyValue1"], ["twoDArrayPropertyValue2"]]
]

let object = try Object.from(JSON)
ReverseMappable
extension Object: ReverseMappable {
    func toKeyedJSON() -> [Object.Key : Any?] {
        return [
            .property : property,
            .optionalProperty : optionalProperty,
            .convertibleProperty : convertibleProperty,
            .optionalConvertibleProperty : optionalConvertibleProperty,
            .nilConvertibleProperty : nilConvertibleProperty,
            .arrayProperty : arrayProperty,
            .optionalArrayProperty : optionalArrayProperty,
            .mappableProperty : mappableProperty.toJSON(),
            .optionalMappableProperty : optionalMappableProperty?.toJSON(),
            .defaultConvertibleProperty : defaultConvertibleProperty,
            .optionalDefaultConvertibleProperty : optionalDefaultConvertibleProperty,
            .twoDArrayProperty : twoDArrayProperty,
            .optionalTwoDArrayProperty : optionalTwoDArrayProperty
        ]
    }
}

let outJSON = object.toJSON()