Skip to content

Commit

Permalink
Merge pull request #459 from Hearst-DD/mappable
Browse files Browse the repository at this point in the history
Mappable
  • Loading branch information
tristanhimmelman committed May 12, 2016
2 parents 006a9bd + d83fe93 commit cf7c4ba
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 57 deletions.
8 changes: 8 additions & 0 deletions ObjectMapper.xcodeproj/project.pbxproj
Expand Up @@ -36,6 +36,9 @@
6A412A181BAC830B001C3F67 /* ClassClusterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A412A161BAC770C001C3F67 /* ClassClusterTests.swift */; };
6A412A241BB0DA26001C3F67 /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A412A231BB0DA26001C3F67 /* PerformanceTests.swift */; };
6A412A251BB0DA26001C3F67 /* PerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A412A231BB0DA26001C3F67 /* PerformanceTests.swift */; };
6A442CA11CE251F100AB4F1F /* MapContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A442CA01CE251F100AB4F1F /* MapContextTests.swift */; };
6A442CA21CE251F100AB4F1F /* MapContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A442CA01CE251F100AB4F1F /* MapContextTests.swift */; };
6A442CA31CE251F100AB4F1F /* MapContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A442CA01CE251F100AB4F1F /* MapContextTests.swift */; };
6A51372C1AADDE2700B82516 /* DateFormatterTransform.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A51372B1AADDE2700B82516 /* DateFormatterTransform.swift */; };
6A51372F1AADE12C00B82516 /* CustomTransformTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A51372E1AADE12C00B82516 /* CustomTransformTests.swift */; };
6A6AEB961A93874F002573D3 /* BasicTypesTestsFromJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A6AEB951A93874F002573D3 /* BasicTypesTestsFromJSON.swift */; };
Expand Down Expand Up @@ -180,6 +183,7 @@
6A3774331A31427F00CC0AB5 /* BasicTypesTestsToJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = BasicTypesTestsToJSON.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
6A412A161BAC770C001C3F67 /* ClassClusterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClassClusterTests.swift; sourceTree = "<group>"; };
6A412A231BB0DA26001C3F67 /* PerformanceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PerformanceTests.swift; sourceTree = "<group>"; };
6A442CA01CE251F100AB4F1F /* MapContextTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapContextTests.swift; sourceTree = "<group>"; };
6A51372B1AADDE2700B82516 /* DateFormatterTransform.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateFormatterTransform.swift; sourceTree = "<group>"; };
6A51372E1AADE12C00B82516 /* CustomTransformTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = CustomTransformTests.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
6A6AEB951A93874F002573D3 /* BasicTypesTestsFromJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicTypesTestsFromJSON.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -338,6 +342,7 @@
3BAD2C0F1BDDC0B000E6B203 /* MappableExtensionsTests.swift */,
6A0BF1FE1C0B53470083D1AF /* ToObjectTests.swift */,
891804CC1C122AF000E5C3EE /* MappableTypesWithTransformsTests.swift */,
6A442CA01CE251F100AB4F1F /* MapContextTests.swift */,
84D4F8561CC3B71B008B0FB6 /* NSDecimalNumberTransformTests.swift */,
6AAC8F8319F03C2900E7A677 /* Supporting Files */,
);
Expand Down Expand Up @@ -681,6 +686,7 @@
files = (
6AA1F66F1BE9921C006EF513 /* MappableExtensionsTests.swift in Sources */,
891804CF1C122AF000E5C3EE /* MappableTypesWithTransformsTests.swift in Sources */,
6A442CA31CE251F100AB4F1F /* MapContextTests.swift in Sources */,
6AA1F66B1BE94687006EF513 /* ClassClusterTests.swift in Sources */,
6AA1F66C1BE94687006EF513 /* PerformanceTests.swift in Sources */,
6AC692411BE3FD45004C119A /* BasicTypes.swift in Sources */,
Expand Down Expand Up @@ -746,6 +752,7 @@
files = (
3BAD2C101BDDC0B000E6B203 /* MappableExtensionsTests.swift in Sources */,
891804CD1C122AF000E5C3EE /* MappableTypesWithTransformsTests.swift in Sources */,
6A442CA11CE251F100AB4F1F /* MapContextTests.swift in Sources */,
6A6AEB961A93874F002573D3 /* BasicTypesTestsFromJSON.swift in Sources */,
6A0BF1FF1C0B53470083D1AF /* ToObjectTests.swift in Sources */,
CD44374D1AAE9C1100A271BA /* NestedKeysTests.swift in Sources */,
Expand Down Expand Up @@ -789,6 +796,7 @@
files = (
3BAD2C111BDDC0B000E6B203 /* MappableExtensionsTests.swift in Sources */,
891804CE1C122AF000E5C3EE /* MappableTypesWithTransformsTests.swift in Sources */,
6A442CA21CE251F100AB4F1F /* MapContextTests.swift in Sources */,
6AA1F66D1BE9468D006EF513 /* NestedArrayTests.swift in Sources */,
6A412A181BAC830B001C3F67 /* ClassClusterTests.swift in Sources */,
CD1603261AC02480000CD69A /* BasicTypesTestsFromJSON.swift in Sources */,
Expand Down
48 changes: 24 additions & 24 deletions ObjectMapper/Core/FromJSON.swift
Expand Up @@ -48,40 +48,40 @@ internal final class FromJSON {
/// Mappable object
class func object<N: Mappable>(inout field: N, map: Map) {
if map.toObject {
Mapper().map(map.currentValue, toObject: field)
} else if let value: N = Mapper().map(map.currentValue) {
Mapper(context: map.context).map(map.currentValue, toObject: field)
} else if let value: N = Mapper(context: map.context).map(map.currentValue) {
field = value
}
}

/// Optional Mappable Object
class func optionalObject<N: Mappable>(inout field: N?, map: Map) {
if let field = field where map.toObject && map.currentValue != nil {
Mapper().map(map.currentValue, toObject: field)
Mapper(context: map.context).map(map.currentValue, toObject: field)
} else {
field = Mapper().map(map.currentValue)
field = Mapper(context: map.context).map(map.currentValue)
}
}

/// Implicitly unwrapped Optional Mappable Object
class func optionalObject<N: Mappable>(inout field: N!, map: Map) {
if let field = field where map.toObject && map.currentValue != nil {
Mapper().map(map.currentValue, toObject: field)
Mapper(context: map.context).map(map.currentValue, toObject: field)
} else {
field = Mapper().map(map.currentValue)
field = Mapper(context: map.context).map(map.currentValue)
}
}

/// mappable object array
class func objectArray<N: Mappable>(inout field: Array<N>, map: Map) {
if let objects = Mapper<N>().mapArray(map.currentValue) {
if let objects = Mapper<N>(context: map.context).mapArray(map.currentValue) {
field = objects
}
}

/// optional mappable object array
class func optionalObjectArray<N: Mappable>(inout field: Array<N>?, map: Map) {
if let objects: Array<N> = Mapper().mapArray(map.currentValue) {
if let objects: Array<N> = Mapper(context: map.context).mapArray(map.currentValue) {
field = objects
} else {
field = nil
Expand All @@ -90,7 +90,7 @@ internal final class FromJSON {

/// Implicitly unwrapped optional mappable object array
class func optionalObjectArray<N: Mappable>(inout field: Array<N>!, map: Map) {
if let objects: Array<N> = Mapper().mapArray(map.currentValue) {
if let objects: Array<N> = Mapper(context: map.context).mapArray(map.currentValue) {
field = objects
} else {
field = nil
Expand All @@ -99,27 +99,27 @@ internal final class FromJSON {

/// mappable object array
class func twoDimensionalObjectArray<N: Mappable>(inout field: Array<Array<N>>, map: Map) {
if let objects = Mapper<N>().mapArrayOfArrays(map.currentValue) {
if let objects = Mapper<N>(context: map.context).mapArrayOfArrays(map.currentValue) {
field = objects
}
}

/// optional mappable 2 dimentional object array
class func optionalTwoDimensionalObjectArray<N: Mappable>(inout field: Array<Array<N>>?, map: Map) {
field = Mapper().mapArrayOfArrays(map.currentValue)
field = Mapper(context: map.context).mapArrayOfArrays(map.currentValue)
}

/// Implicitly unwrapped optional 2 dimentional mappable object array
class func optionalTwoDimensionalObjectArray<N: Mappable>(inout field: Array<Array<N>>!, map: Map) {
field = Mapper().mapArrayOfArrays(map.currentValue)
field = Mapper(context: map.context).mapArrayOfArrays(map.currentValue)
}

/// Dctionary containing Mappable objects
class func objectDictionary<N: Mappable>(inout field: Dictionary<String, N>, map: Map) {
if map.toObject {
Mapper<N>().mapDictionary(map.currentValue, toDictionary: field)
Mapper<N>(context: map.context).mapDictionary(map.currentValue, toDictionary: field)
} else {
if let objects = Mapper<N>().mapDictionary(map.currentValue) {
if let objects = Mapper<N>(context: map.context).mapDictionary(map.currentValue) {
field = objects
}
}
Expand All @@ -128,53 +128,53 @@ internal final class FromJSON {
/// Optional dictionary containing Mappable objects
class func optionalObjectDictionary<N: Mappable>(inout field: Dictionary<String, N>?, map: Map) {
if let field = field where map.toObject && map.currentValue != nil {
Mapper().mapDictionary(map.currentValue, toDictionary: field)
Mapper(context: map.context).mapDictionary(map.currentValue, toDictionary: field)
} else {
field = Mapper().mapDictionary(map.currentValue)
field = Mapper(context: map.context).mapDictionary(map.currentValue)
}
}

/// Implicitly unwrapped Dictionary containing Mappable objects
class func optionalObjectDictionary<N: Mappable>(inout field: Dictionary<String, N>!, map: Map) {
if let field = field where map.toObject && map.currentValue != nil {
Mapper().mapDictionary(map.currentValue, toDictionary: field)
Mapper(context: map.context).mapDictionary(map.currentValue, toDictionary: field)
} else {
field = Mapper().mapDictionary(map.currentValue)
field = Mapper(context: map.context).mapDictionary(map.currentValue)
}
}

/// Dictionary containing Array of Mappable objects
class func objectDictionaryOfArrays<N: Mappable>(inout field: Dictionary<String, [N]>, map: Map) {
if let objects = Mapper<N>().mapDictionaryOfArrays(map.currentValue) {
if let objects = Mapper<N>(context: map.context).mapDictionaryOfArrays(map.currentValue) {
field = objects
}
}

/// Optional Dictionary containing Array of Mappable objects
class func optionalObjectDictionaryOfArrays<N: Mappable>(inout field: Dictionary<String, [N]>?, map: Map) {
field = Mapper<N>().mapDictionaryOfArrays(map.currentValue)
field = Mapper<N>(context: map.context).mapDictionaryOfArrays(map.currentValue)
}

/// Implicitly unwrapped Dictionary containing Array of Mappable objects
class func optionalObjectDictionaryOfArrays<N: Mappable>(inout field: Dictionary<String, [N]>!, map: Map) {
field = Mapper<N>().mapDictionaryOfArrays(map.currentValue)
field = Mapper<N>(context: map.context).mapDictionaryOfArrays(map.currentValue)
}

/// mappable object Set
class func objectSet<N: Mappable>(inout field: Set<N>, map: Map) {
if let objects = Mapper<N>().mapSet(map.currentValue) {
if let objects = Mapper<N>(context: map.context).mapSet(map.currentValue) {
field = objects
}
}

/// optional mappable object array
class func optionalObjectSet<N: Mappable>(inout field: Set<N>?, map: Map) {
field = Mapper().mapSet(map.currentValue)
field = Mapper(context: map.context).mapSet(map.currentValue)
}

/// Implicitly unwrapped optional mappable object array
class func optionalObjectSet<N: Mappable>(inout field: Set<N>!, map: Map) {
field = Mapper().mapSet(map.currentValue)
field = Mapper(context: map.context).mapSet(map.currentValue)
}

}
12 changes: 10 additions & 2 deletions ObjectMapper/Core/Map.swift
Expand Up @@ -29,13 +29,19 @@

import Foundation

/// MapContext is available for developers who wish to pass information around during the mapping process.
public protocol MapContext {

}

/// A class used for holding mapping data
public final class Map {
public let mappingType: MappingType

public internal(set) var JSONDictionary: [String : AnyObject] = [:]
public internal(set) var isKeyPresent = false
public var currentValue: AnyObject?
public var context: MapContext?
var currentKey: String?
var keyIsNested = false

Expand All @@ -44,10 +50,11 @@ public final class Map {
/// Counter for failing cases of deserializing values to `let` properties.
private var failedCount: Int = 0

public init(mappingType: MappingType, JSONDictionary: [String : AnyObject], toObject: Bool = false) {
public init(mappingType: MappingType, JSONDictionary: [String : AnyObject], toObject: Bool = false, context: MapContext? = nil) {
self.mappingType = mappingType
self.JSONDictionary = JSONDictionary
self.toObject = toObject
self.context = context
}

/// Sets the current mapper value and key.
Expand All @@ -66,7 +73,8 @@ public final class Map {
// check if a value exists for the current key
// do this pre-check for performance reasons
if nested == false {
let object = JSONDictionary[key], isNSNull = object is NSNull
let object = JSONDictionary[key]
let isNSNull = object is NSNull
isKeyPresent = isNSNull ? true : object != nil
currentValue = isNSNull ? nil : object
} else {
Expand Down
12 changes: 9 additions & 3 deletions ObjectMapper/Core/Mappable.swift
Expand Up @@ -9,16 +9,22 @@
import Foundation

public protocol Mappable {
/// This function can be used to validate JSON prior to mapping. Return nil to cancel mapping at this point
init?(_ map: Map)
/// This function is where all variable mappings should occur. It is executed by Mapper during the mapping (serialization and deserialization) process.
mutating func mapping(map: Map)
}

public protocol MappableCluster: Mappable {
/// This is an optional function that can be used to:
/// 1) provide an existing cached object to be used for mapping
/// 2) return an object of another class (which conforms to Mappable) to be used for mapping. For instance, you may inspect the JSON to infer the type of object that should be used for any given mapping
static func objectForMapping(map: Map) -> Mappable?
}

public extension Mappable {

public static func objectForMapping(map: Map) -> Mappable? {
return nil
}

/// Initializes object from a JSON String
public init?(JSONString: String) {
if let obj: Self = Mapper().map(JSONString) {
Expand Down
28 changes: 16 additions & 12 deletions ObjectMapper/Core/Mapper.swift
Expand Up @@ -36,7 +36,11 @@ public enum MappingType {
/// The Mapper class provides methods for converting Model objects to JSON and methods for converting JSON to Model objects
public final class Mapper<N: Mappable> {

public init(){}
public var context: MapContext?

public init(context: MapContext? = nil){
self.context = context
}

// MARK: Mapping functions that map to an existing object toObject

Expand All @@ -61,7 +65,7 @@ public final class Mapper<N: Mappable> {
/// Usefull for those pesky objects that have crappy designated initializers like NSManagedObject
public func map(JSONDictionary: [String : AnyObject], toObject object: N) -> N {
var mutableObject = object
let map = Map(mappingType: .FromJSON, JSONDictionary: JSONDictionary, toObject: true)
let map = Map(mappingType: .FromJSON, JSONDictionary: JSONDictionary, toObject: true, context: context)
mutableObject.mapping(map)
return mutableObject
}
Expand Down Expand Up @@ -102,20 +106,20 @@ public final class Mapper<N: Mappable> {

/// Maps a JSON dictionary to an object that conforms to Mappable
public func map(JSONDictionary: [String : AnyObject]) -> N? {
let map = Map(mappingType: .FromJSON, JSONDictionary: JSONDictionary)
let map = Map(mappingType: .FromJSON, JSONDictionary: JSONDictionary, context: context)

// check if N is of type MappableCluster
if let klass = N.self as? MappableCluster.Type {
if var object = klass.objectForMapping(map) as? N {
object.mapping(map)
return object
}
// check if objectForMapping returns an object for mapping
if var object = N.self.objectForMapping(map) as? N {
object.mapping(map)
return object
}

// fall back to using init? to create N
if var object = N(map) {
object.mapping(map)
return object
}

return nil
}

Expand Down Expand Up @@ -203,9 +207,9 @@ public final class Mapper<N: Mappable> {
var mutableDictionary = dictionary
for (key, value) in JSONDictionary {
if let object = dictionary[key] {
Mapper().map(value, toObject: object)
Mapper(context: context).map(value, toObject: object)
} else {
mutableDictionary[key] = Mapper().map(value)
mutableDictionary[key] = Mapper(context: context).map(value)
}
}

Expand Down Expand Up @@ -295,7 +299,7 @@ extension Mapper {
///Maps an object that conforms to Mappable to a JSON dictionary <String : AnyObject>
public func toJSON( object: N) -> [String : AnyObject] {
var mutableObject = object
let map = Map(mappingType: .ToJSON, JSONDictionary: [:])
let map = Map(mappingType: .ToJSON, JSONDictionary: [:], context: context)
mutableObject.mapping(map)
return map.JSONDictionary
}
Expand Down
12 changes: 6 additions & 6 deletions ObjectMapper/Core/ToJSON.swift
Expand Up @@ -100,7 +100,7 @@ internal final class ToJSON {
}

class func object<N: Mappable>(field: N, map: Map) {
setValue(Mapper().toJSON(field), map: map)
setValue(Mapper(context: map.context).toJSON(field), map: map)
}

class func optionalObject<N: Mappable>(field: N?, map: Map) {
Expand All @@ -110,7 +110,7 @@ internal final class ToJSON {
}

class func objectArray<N: Mappable>(field: Array<N>, map: Map) {
let JSONObjects = Mapper().toJSONArray(field)
let JSONObjects = Mapper(context: map.context).toJSONArray(field)

setValue(JSONObjects, map: map)
}
Expand All @@ -124,7 +124,7 @@ internal final class ToJSON {
class func twoDimensionalObjectArray<N: Mappable>(field: Array<Array<N>>, map: Map) {
var array = [[[String : AnyObject]]]()
for innerArray in field {
let JSONObjects = Mapper().toJSONArray(innerArray)
let JSONObjects = Mapper(context: map.context).toJSONArray(innerArray)
array.append(JSONObjects)
}
setValue(array, map: map)
Expand All @@ -137,7 +137,7 @@ internal final class ToJSON {
}

class func objectSet<N: Mappable where N: Hashable>(field: Set<N>, map: Map) {
let JSONObjects = Mapper().toJSONSet(field)
let JSONObjects = Mapper(context: map.context).toJSONSet(field)

setValue(JSONObjects, map: map)
}
Expand All @@ -149,7 +149,7 @@ internal final class ToJSON {
}

class func objectDictionary<N: Mappable>(field: Dictionary<String, N>, map: Map) {
let JSONObjects = Mapper().toJSONDictionary(field)
let JSONObjects = Mapper(context: map.context).toJSONDictionary(field)

setValue(JSONObjects, map: map)
}
Expand All @@ -161,7 +161,7 @@ internal final class ToJSON {
}

class func objectDictionaryOfArrays<N: Mappable>(field: Dictionary<String, [N]>, map: Map) {
let JSONObjects = Mapper().toJSONDictionaryOfArrays(field)
let JSONObjects = Mapper(context: map.context).toJSONDictionaryOfArrays(field)

setValue(JSONObjects, map: map)
}
Expand Down

0 comments on commit cf7c4ba

Please sign in to comment.