diff --git a/ObjectMapper.xcodeproj/project.pbxproj b/ObjectMapper.xcodeproj/project.pbxproj index 329a283b..8e6e8c6d 100644 --- a/ObjectMapper.xcodeproj/project.pbxproj +++ b/ObjectMapper.xcodeproj/project.pbxproj @@ -728,11 +728,11 @@ }; 6AAC8F7519F03C2900E7A677 = { CreatedOnToolsVersion = 6.0.1; - LastSwiftMigration = 1120; + LastSwiftMigration = 0900; }; 6AAC8F8019F03C2900E7A677 = { CreatedOnToolsVersion = 6.0.1; - LastSwiftMigration = 1120; + LastSwiftMigration = 0900; }; CD1602FE1AC023D5000CD69A = { CreatedOnToolsVersion = 6.2; @@ -1108,7 +1108,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; @@ -1137,7 +1137,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = appletvos; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; @@ -1156,7 +1156,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.tristanhimmelman.ObjectMapper-tvOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Debug; @@ -1174,7 +1174,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.tristanhimmelman.ObjectMapper-tvOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = appletvos; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; TVOS_DEPLOYMENT_TARGET = 9.0; }; name = Release; @@ -1200,7 +1200,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = 4; TVOS_DEPLOYMENT_TARGET = 9.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; @@ -1228,7 +1228,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = watchos; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; TARGETED_DEVICE_FAMILY = 4; TVOS_DEPLOYMENT_TARGET = 9.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; @@ -1363,7 +1363,8 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.tristanhimmelman.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; TVOS_DEPLOYMENT_TARGET = 9.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; @@ -1384,7 +1385,8 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.tristanhimmelman.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(PROJECT_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; TVOS_DEPLOYMENT_TARGET = 9.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; @@ -1402,7 +1404,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.heart.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -1414,7 +1417,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.heart.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; }; name = Release; }; @@ -1442,7 +1446,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; TVOS_DEPLOYMENT_TARGET = 9.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; @@ -1470,7 +1474,7 @@ PRODUCT_NAME = "$(PROJECT_NAME)"; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; TVOS_DEPLOYMENT_TARGET = 9.0; WATCHOS_DEPLOYMENT_TARGET = 2.0; }; @@ -1491,7 +1495,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.heart.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -1508,7 +1512,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.heart.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; }; name = Release; }; @@ -1532,7 +1536,7 @@ PRODUCT_NAME = ObjectMapper; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -1557,7 +1561,7 @@ PRODUCT_NAME = ObjectMapper; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/ObjectMapper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ObjectMapper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/ObjectMapper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/README.md b/README.md index 63c15abf..bceae5b3 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,7 @@ init(map: Map) throws { createdAt = try map.value("createdAt", using: DateTransform()) // throws an error when it fails updatedAt = try? map.value("updatedAt", using: DateTransform()) // optional posts = (try? map.value("posts")) ?? [] // optional + default value + surname = try? map.value("surname", default: "DefaultSurname") // optional + default value as an argument } ``` diff --git a/Sources/Map.swift b/Sources/Map.swift index 38dc2a3f..1ca9903a 100644 --- a/Sources/Map.swift +++ b/Sources/Map.swift @@ -202,3 +202,45 @@ private func valueFor(_ keyPathComponents: ArraySlice, array: [Any]) -> return (false, nil) } + +// MARK: - Default Value + +public extension Map { + + /// Returns `default` value if there is nothing to parse. + func value(_ key: String, default: T.Object, using transform: T) throws -> T.Object where T: TransformType { + if let value: T.Object = try? self.value(key, using: transform) { + return value + } else { + return `default` + } + } + + /// Returns `default` value if there is nothing to parse. + func value(_ key: String, default: T) throws -> T { + if let value: T = try? self.value(key) { + return value + } else { + return `default` + } + } + + /// Returns `default` value if there is nothing to parse. + func value(_ key: String, default: [T]) -> [T] { + do { + let value: [T] = try self.value(key) + return value + } catch { + return `default` + } + } + + /// Returns `default` value if there is nothing to parse. + func value(_ key: String, default: T) throws -> T where T: BaseMappable { + if let value: T = try? self.value(key) as T { + return value + } else { + return `default` + } + } +} diff --git a/Tests/ObjectMapperTests/ImmutableTests.swift b/Tests/ObjectMapperTests/ImmutableTests.swift index f722add1..bafba916 100644 --- a/Tests/ObjectMapperTests/ImmutableTests.swift +++ b/Tests/ObjectMapperTests/ImmutableTests.swift @@ -201,12 +201,12 @@ class ImmutableObjectTests: XCTestCase { XCTAssertEqual(immutable.delimiterNestedDictionary, ["a": 10, "b": 20, "c": 30]) let JSON2: [String: Any] = [ "prop1": "prop1", "prop2": NSNull() ] - let immutable2 = try? mapper.map(JSON: JSON2) as Struct + let immutable2 = try? mapper.map(JSON: JSON2) XCTAssertNil(immutable2) // TODO: ImmutableMappable to JSON let JSONFromObject = mapper.toJSON(immutable) - let objectFromJSON = try? mapper.map(JSON: JSONFromObject) as Struct + let objectFromJSON = try? mapper.map(JSON: JSONFromObject) XCTAssertNotNil(objectFromJSON) assertImmutableObjectsEqual(objectFromJSON!, immutable) } diff --git a/Tests/ObjectMapperTests/MapContextTests.swift b/Tests/ObjectMapperTests/MapContextTests.swift index 3bf4e250..13a4b34f 100644 --- a/Tests/ObjectMapperTests/MapContextTests.swift +++ b/Tests/ObjectMapperTests/MapContextTests.swift @@ -138,7 +138,7 @@ class MapContextTests: XCTestCase { let JSON = ["name": "Anton"] let context = ImmutableContext(isDeveloper: true) - let person = try? Mapper(context: context).map(JSON: JSON) as ImmutablePerson + let person = try? Mapper(context: context).map(JSON: JSON) XCTAssertNotNil(person) @@ -149,7 +149,7 @@ class MapContextTests: XCTestCase { let JSON = ["name": "Anton"] let context = ImmutableContext(isDeveloper: true) - let person = try? ImmutablePerson(JSON: JSON, context: context) as ImmutablePerson + let person = try? ImmutablePerson(JSON: JSON, context: context) XCTAssertNotNil(person) XCTAssertEqual(person?.isDeveloper ?? !context.isDeveloper, context.isDeveloper) @@ -172,7 +172,7 @@ class MapContextTests: XCTestCase { let JSON = ["person": ["name": "Anton"]] let context = ImmutableContext(isDeveloper: true) - let nestedPerson = try? Mapper(context: context).map(JSON: JSON) as ImmutableNestedPerson + let nestedPerson = try? Mapper(context: context).map(JSON: JSON) XCTAssertNotNil(nestedPerson) XCTAssertEqual(nestedPerson?.person.isDeveloper ?? !context.isDeveloper, context.isDeveloper) @@ -182,7 +182,7 @@ class MapContextTests: XCTestCase { let JSON = ["person": ["name": "Anton"]] let context = ImmutableContext(isDeveloper: true) - let nestedPerson = try? ImmutableNestedPerson(JSON: JSON, context: context) as ImmutableNestedPerson + let nestedPerson = try? ImmutableNestedPerson(JSON: JSON, context: context) XCTAssertNotNil(nestedPerson) XCTAssertEqual(nestedPerson?.person.isDeveloper ?? !context.isDeveloper, context.isDeveloper) @@ -207,7 +207,7 @@ class MapContextTests: XCTestCase { let JSON = ["persons": [["name": "Tristan"], ["name": "Anton"]]] let context = ImmutableContext(isDeveloper: true) - let personList = try? Mapper(context: context).map(JSON: JSON) as ImmutablePersonList + let personList = try? Mapper(context: context).map(JSON: JSON) XCTAssertNotNil(personList) @@ -220,7 +220,7 @@ class MapContextTests: XCTestCase { let JSON = ["persons": [["name": "Tristan"], ["name": "Anton"]]] let context = ImmutableContext(isDeveloper: true) - let personList = try? ImmutablePersonList(JSON: JSON, context: context) as ImmutablePersonList + let personList = try? ImmutablePersonList(JSON: JSON, context: context) XCTAssertNotNil(personList) @@ -242,6 +242,32 @@ class MapContextTests: XCTestCase { XCTFail() } + + func testDefaultArgumentWithoutValue() { + let JSON: [String: Any] = [:] + let dog = try? Mapper().map(JSON: JSON) as ImmutableDog + + XCTAssertNotNil(dog) + XCTAssertTrue(dog!.name == "Sasha") + } + + func testDefaultArgumentWithValue() { + let JSON = ["name": "Sofie"] + let dog = try? Mapper().map(JSON: JSON) as ImmutableDog + + XCTAssertNotNil(dog) + XCTAssertTrue(dog!.name == "Sofie") + } + + // MARK: - Default Argument + public struct ImmutableDog: ImmutableMappable { + public let name: String + + /// Define `default` value to use if it is nothing to parse in `name` + public init(map: Map) throws { + name = try map.value("name", default: "Sasha") + } + } // MARK: - Nested Types // MARK: BaseMappable diff --git a/Tests/ObjectMapperTests/MappableExtensionsTests.swift b/Tests/ObjectMapperTests/MappableExtensionsTests.swift index d3ee7960..b885cf1f 100644 --- a/Tests/ObjectMapperTests/MappableExtensionsTests.swift +++ b/Tests/ObjectMapperTests/MappableExtensionsTests.swift @@ -44,14 +44,12 @@ struct TestMappable: Mappable, Equatable, Hashable { mutating func mapping(map: Map) { value <- map["value"] } - - - func hash(into hasher: inout Hasher) { + + var hashValue: Int { if let value = value { - hasher.combine(value) - } else { - hasher.combine(NSIntegerMax) + return value.hashValue } + return NSIntegerMax } } diff --git a/Tests/ObjectMapperTests/PerformanceTests.swift b/Tests/ObjectMapperTests/PerformanceTests.swift index fa2819be..6e0620ba 100644 --- a/Tests/ObjectMapperTests/PerformanceTests.swift +++ b/Tests/ObjectMapperTests/PerformanceTests.swift @@ -71,7 +71,7 @@ class PerformanceTests: XCTestCase { func testPerformanceImmutable() { self.measure { - _ = try? Mapper().mapArray(JSONString: self.JSONTestString) as [PerformanceImmutableMappableObject] + _ = try? Mapper().mapArray(JSONString: self.JSONTestString) } } }