Skip to content

Commit 0fac1f2

Browse files
Merge branch 'master' into swift-4
# Conflicts: # Sources/ToJSON.swift # Tests/ObjectMapperTests/ImmutableTests.swift
2 parents 57459cf + fb16bce commit 0fac1f2

13 files changed

+120
-44
lines changed

.swift-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.1
1+
4.0

.travis.yml

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ env:
1515
- TVOS_SDK=appletvsimulator10.2
1616
- WATCHOS_SDK=watchsimulator3.2
1717
matrix:
18-
- DESTINATION="OS=10.3,name=iPhone 5" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
19-
- DESTINATION="OS=10.3,name=iPhone 5s" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
20-
- DESTINATION="OS=10.3,name=iPhone 6" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
21-
- DESTINATION="OS=10.3,name=iPhone 6 Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
22-
- DESTINATION="OS=10.3,name=iPhone 6s" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
23-
- DESTINATION="OS=10.3,name=iPhone 6s Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
18+
- DESTINATION="OS=10.3.1,name=iPhone 5s" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
19+
- DESTINATION="OS=10.3.1,name=iPhone 6" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
20+
- DESTINATION="OS=10.3.1,name=iPhone 6 Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
21+
- DESTINATION="OS=10.3.1,name=iPhone 6s" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
22+
- DESTINATION="OS=10.3.1,name=iPhone 6s Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
23+
- DESTINATION="OS=10.3.1,name=iPhone 7" SCHEME="$IOS_FRAMEWORK_SCHEME" SDK="$IOS_SDK" RUN_TESTS="YES"
2424
- DESTINATION="arch=x86_64" SCHEME="$OSX_FRAMEWORK_SCHEME" SDK="$OSX_SDK" RUN_TESTS="YES"
2525
- DESTINATION="OS=10.2,name=Apple TV 1080p" SCHEME="$TVOS_FRAMEWORK_SCHEME" SDK="$TVOS_SDK" RUN_TESTS="YES"
2626
- DESTINATION="OS=3.2,name=Apple Watch - 38mm" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" SDK="$WATCHOS_SDK" RUN_TESTS="NO"

ObjectMapper.podspec

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'ObjectMapper'
3-
s.version = '2.2.7'
3+
s.version = '2.3.0'
44
s.license = 'MIT'
55
s.summary = 'JSON Object mapping written in Swift'
66
s.homepage = 'https://github.com/Hearst-DD/ObjectMapper'
@@ -14,7 +14,7 @@ Pod::Spec.new do |s|
1414

1515

1616
s.pod_target_xcconfig = {
17-
'SWIFT_VERSION' => '3.1',
17+
'SWIFT_VERSION' => '4.0',
1818
}
1919

2020
s.requires_arc = true

README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ ObjectMapper is a framework written in Swift that makes it easy for you to conve
2727
- Nested Objects (stand alone, in arrays or in dictionaries)
2828
- Custom transformations during mapping
2929
- Struct support
30-
- [Immutable support](#immutablemappable-protocol-beta) (currently in beta)
30+
- [Immutable support](#immutablemappable-protocol)
3131

3232
# The Basics
3333
To support mapping, a class or struct just needs to implement the ```Mappable``` protocol which includes the following functions:
@@ -93,7 +93,7 @@ let JSONString = user.toJSONString(prettyPrint: true)
9393
```
9494

9595
Alternatively, the `Mapper.swift` class can also be used to accomplish the above (it also provides extra functionality for other situations):
96-
```
96+
```swift
9797
// Convert JSON String to Model
9898
let user = Mapper<User>().map(JSONString: JSONString)
9999
// Create JSON String from Model
@@ -148,9 +148,7 @@ ObjectMapper uses this function to get objects to use for mapping. Developers sh
148148

149149
If you need to implemented ObjectMapper in an extension, you will need to select this protocol instead of `Mappable`.
150150

151-
## `ImmutableMappable` Protocol (Beta)
152-
153-
> ⚠️ This feature is currently in Beta. There might be breaking API changes in the future.
151+
## `ImmutableMappable` Protocol
154152

155153
`ImmutableMappable` provides the ability to map immutable properties. This is how `ImmutableMappable` differs from `Mappable`:
156154

@@ -203,7 +201,7 @@ mutating func mapping(map: Map) {
203201
<tr>
204202
<td>
205203
<pre>
206-
mutating func mapping(map: Map) {
204+
func mapping(map: Map) {
207205
id <strong>>>></strong> map["id"]
208206
name <strong>>>></strong> map["name"]
209207
}
@@ -446,6 +444,8 @@ class Model: Object, Mappable {
446444

447445
If you want to serialize associated RealmObjects, you can use [ObjectMapper+Realm](https://github.com/jakenberg/ObjectMapper-Realm). It is a simple Realm extension that serializes arbitrary JSON into Realm's List class.
448446

447+
To serialize Swift String, Int, Double and Bool arrays you can use [ObjectMapperAdditions/Realm](https://github.com/APUtils/ObjectMapperAdditions#realm-features). It'll wrap Swift types into RealmValues that can be stored in Realm's List class.
448+
449449
Note: Generating a JSON string of a Realm Object using ObjectMappers' `toJSON` function only works within a Realm write transaction. This is caused because ObjectMapper uses the `inout` flag in its mapping functions (`<-`) which are used both for serializing and deserializing. Realm detects the flag and forces the `toJSON` function to be called within a write block even though the objects are not being modified.
450450

451451
# Projects Using ObjectMapper

Sources/FromJSON.swift

+10-10
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ internal final class FromJSON {
4848
/// Mappable object
4949
class func object<N: BaseMappable>(_ field: inout N, map: Map) {
5050
if map.toObject {
51-
_ = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: field)
51+
field = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: field)
5252
} else if let value: N = Mapper(context: map.context).map(JSONObject: map.currentValue) {
5353
field = value
5454
}
@@ -57,17 +57,17 @@ internal final class FromJSON {
5757
/// Optional Mappable Object
5858

5959
class func optionalObject<N: BaseMappable>(_ field: inout N?, map: Map) {
60-
if let field = field , map.toObject && map.currentValue != nil {
61-
_ = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: field)
60+
if let f = field , map.toObject && map.currentValue != nil {
61+
field = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: f)
6262
} else {
6363
field = Mapper(context: map.context).map(JSONObject: map.currentValue)
6464
}
6565
}
6666

6767
/// Implicitly unwrapped Optional Mappable Object
6868
class func optionalObject<N: BaseMappable>(_ field: inout N!, map: Map) {
69-
if let field = field , map.toObject && map.currentValue != nil {
70-
_ = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: field)
69+
if let f = field , map.toObject && map.currentValue != nil {
70+
field = Mapper(context: map.context).map(JSONObject: map.currentValue, toObject: f)
7171
} else {
7272
field = Mapper(context: map.context).map(JSONObject: map.currentValue)
7373
}
@@ -119,7 +119,7 @@ internal final class FromJSON {
119119
/// Dctionary containing Mappable objects
120120
class func objectDictionary<N: BaseMappable>(_ field: inout Dictionary<String, N>, map: Map) {
121121
if map.toObject {
122-
_ = Mapper<N>(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: field)
122+
field = Mapper<N>(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: field)
123123
} else {
124124
if let objects = Mapper<N>(context: map.context).mapDictionary(JSONObject: map.currentValue) {
125125
field = objects
@@ -129,17 +129,17 @@ internal final class FromJSON {
129129

130130
/// Optional dictionary containing Mappable objects
131131
class func optionalObjectDictionary<N: BaseMappable>(_ field: inout Dictionary<String, N>?, map: Map) {
132-
if let field = field , map.toObject && map.currentValue != nil {
133-
_ = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: field)
132+
if let f = field , map.toObject && map.currentValue != nil {
133+
field = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: f)
134134
} else {
135135
field = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue)
136136
}
137137
}
138138

139139
/// Implicitly unwrapped Dictionary containing Mappable objects
140140
class func optionalObjectDictionary<N: BaseMappable>(_ field: inout Dictionary<String, N>!, map: Map) {
141-
if let field = field , map.toObject && map.currentValue != nil {
142-
_ = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: field)
141+
if let f = field , map.toObject && map.currentValue != nil {
142+
field = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue, toDictionary: f)
143143
} else {
144144
field = Mapper(context: map.context).mapDictionary(JSONObject: map.currentValue)
145145
}

Sources/ISO8601DateTransform.swift

+12-6
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,20 @@
2828

2929
import Foundation
3030

31+
public extension DateFormatter {
32+
public convenience init(withFormat format : String, locale : String) {
33+
self.init()
34+
self.locale = Locale(identifier: locale)
35+
dateFormat = format
36+
}
37+
}
38+
3139
open class ISO8601DateTransform: DateFormatterTransform {
40+
41+
static let reusableISODateFormatter = DateFormatter(withFormat: "yyyy-MM-dd'T'HH:mm:ssZZZZZ", locale: "en_US_POSIX")
3242

3343
public init() {
34-
let formatter = DateFormatter()
35-
formatter.locale = Locale(identifier: "en_US_POSIX")
36-
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
37-
38-
super.init(dateFormatter: formatter)
44+
super.init(dateFormatter: ISO8601DateTransform.reusableISODateFormatter)
3945
}
40-
4146
}
47+

Sources/ImmutableMappable.swift

+5
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ public extension Map {
8383
public func value<T: RawRepresentable>(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> T {
8484
return try self.value(key, nested: nested, delimiter: delimiter, using: EnumTransform(), file: file, function: function, line: line)
8585
}
86+
87+
/// Returns a `[RawRepresentable]` type or throws an error.
88+
public func value<T: RawRepresentable>(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [T] {
89+
return try self.value(key, nested: nested, delimiter: delimiter, using: EnumTransform(), file: file, function: function, line: line)
90+
}
8691

8792
// MARK: BaseMappable
8893

Sources/Map.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public final class Map {
4747
public var context: MapContext?
4848
public var shouldIncludeNilValues = false /// If this is set to true, toJSON output will include null values for any variables that are not set.
4949

50-
let toObject: Bool // indicates whether the mapping is being applied to an existing object
50+
public let toObject: Bool // indicates whether the mapping is being applied to an existing object
5151

5252
public init(mappingType: MappingType, JSON: [String: Any], toObject: Bool = false, context: MapContext? = nil, shouldIncludeNilValues: Bool = false) {
5353

Sources/Operators.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ public func <- <T: BaseMappable>(left: inout Array<Array<T>>!, right: Map) {
327327
}
328328
}
329329

330-
// MARK:- Set of Mappable objects - Set<T: BaseMappable where T: Hashable>
330+
// MARK:- Set of Mappable objects - Set<T: BaseMappable>
331331

332332
/// Set of Mappable objects
333333
public func <- <T: BaseMappable>(left: inout Set<T>, right: Map) {

Sources/ToJSON.swift

+6-6
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ internal final class ToJSON {
103103
}
104104

105105
class func object<N: BaseMappable>(_ field: N, map: Map) {
106-
if let result = Mapper(context: map.context).toJSON(field) as Any? {
106+
if let result = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSON(field) as Any? {
107107
setValue(result, map: map)
108108
}
109109
}
@@ -115,7 +115,7 @@ internal final class ToJSON {
115115
}
116116

117117
class func objectArray<N: BaseMappable>(_ field: Array<N>, map: Map) {
118-
let JSONObjects = Mapper(context: map.context).toJSONArray(field)
118+
let JSONObjects = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSONArray(field)
119119

120120
setValue(JSONObjects, map: map)
121121
}
@@ -129,7 +129,7 @@ internal final class ToJSON {
129129
class func twoDimensionalObjectArray<N: BaseMappable>(_ field: Array<Array<N>>, map: Map) {
130130
var array = [[[String: Any]]]()
131131
for innerArray in field {
132-
let JSONObjects = Mapper(context: map.context).toJSONArray(innerArray)
132+
let JSONObjects = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSONArray(innerArray)
133133
array.append(JSONObjects)
134134
}
135135
setValue(array, map: map)
@@ -142,7 +142,7 @@ internal final class ToJSON {
142142
}
143143

144144
class func objectSet<N: BaseMappable>(_ field: Set<N>, map: Map) {
145-
let JSONObjects = Mapper(context: map.context).toJSONSet(field)
145+
let JSONObjects = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSONSet(field)
146146

147147
setValue(JSONObjects, map: map)
148148
}
@@ -154,7 +154,7 @@ internal final class ToJSON {
154154
}
155155

156156
class func objectDictionary<N: BaseMappable>(_ field: Dictionary<String, N>, map: Map) {
157-
let JSONObjects = Mapper(context: map.context).toJSONDictionary(field)
157+
let JSONObjects = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSONDictionary(field)
158158

159159
setValue(JSONObjects, map: map)
160160
}
@@ -166,7 +166,7 @@ internal final class ToJSON {
166166
}
167167

168168
class func objectDictionaryOfArrays<N: BaseMappable>(_ field: Dictionary<String, [N]>, map: Map) {
169-
let JSONObjects = Mapper(context: map.context).toJSONDictionaryOfArrays(field)
169+
let JSONObjects = Mapper(context: map.context, shouldIncludeNilValues: map.shouldIncludeNilValues).toJSONDictionaryOfArrays(field)
170170

171171
setValue(JSONObjects, map: map)
172172
}

Sources/TransformOperators.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ public func <- <Transform: TransformType>(left: inout [[Transform.Object]]!, rig
522522
}
523523
}
524524

525-
// MARK:- Set of Mappable objects with a transform - Set<T: BaseMappable where T: Hashable>
525+
// MARK:- Set of Mappable objects with a transform - Set<T: BaseMappable>
526526

527527
/// Set of Mappable objects with transform
528528
public func <- <Transform: TransformType>(left: inout Set<Transform.Object>, right: (Map, Transform)) where Transform.Object: BaseMappable {

Tests/ObjectMapperTests/ImmutableTests.swift

+29-4
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,23 @@ class ImmutableObjectTests: XCTestCase {
7878
// RawRepresentable
7979
"prop27a": NSNumber(value: 0),
8080
"prop27b": NSNumber(value: 1000),
81-
81+
"prop27c": [NSNumber(value: 0), NSNumber(value: 1000)],
82+
8283
"prop28a": Int(0),
8384
"prop28b": Int(255),
84-
85+
"prop28c": [Int(0), Int(255)],
86+
8587
"prop29a": Double(0),
8688
"prop29b": Double(100),
87-
89+
"prop29c": [Double(0), Double(100)],
90+
8891
"prop30a": Float(0),
8992
"prop30b": Float(100),
93+
"prop30c": [Float(0), Float(100)],
9094

9195
"prop31a": "String A",
9296
"prop31b": "String B",
97+
"prop31c": ["String A", "String B"],
9398

9499
// [[String]]
95100
"prop32": [["prop32"]],
@@ -157,18 +162,23 @@ class ImmutableObjectTests: XCTestCase {
157162

158163
XCTAssertEqual(immutable.prop27a.rawValue, Int64Enum.a.rawValue)
159164
XCTAssertEqual(immutable.prop27b.rawValue, Int64Enum.b.rawValue)
165+
XCTAssertEqual(immutable.prop27c, [Int64Enum.a, Int64Enum.b])
160166

161167
XCTAssertEqual(immutable.prop28a.rawValue, IntEnum.a.rawValue)
162168
XCTAssertEqual(immutable.prop28b.rawValue, IntEnum.b.rawValue)
169+
XCTAssertEqual(immutable.prop28c, [IntEnum.a, IntEnum.b])
163170

164171
XCTAssertEqual(immutable.prop29a.rawValue, DoubleEnum.a.rawValue)
165172
XCTAssertEqual(immutable.prop29b.rawValue, DoubleEnum.b.rawValue)
173+
XCTAssertEqual(immutable.prop29c, [DoubleEnum.a, DoubleEnum.b])
166174

167175
XCTAssertEqual(immutable.prop30a.rawValue, FloatEnum.a.rawValue)
168176
XCTAssertEqual(immutable.prop30b.rawValue, FloatEnum.b.rawValue)
177+
XCTAssertEqual(immutable.prop30c, [FloatEnum.a, FloatEnum.b])
169178

170179
XCTAssertEqual(immutable.prop31a.rawValue, StringEnum.A.rawValue)
171180
XCTAssertEqual(immutable.prop31b.rawValue, StringEnum.B.rawValue)
181+
XCTAssertEqual(immutable.prop31c, [StringEnum.A, StringEnum.B])
172182

173183
XCTAssertEqual(immutable.prop32[0][0], "prop32_TRANSFORMED")
174184
XCTAssertEqual(immutable.prop33![0][0], "prop33_TRANSFORMED")
@@ -362,18 +372,23 @@ struct Struct {
362372
// RawRepresentable
363373
let prop27a: Int64Enum
364374
let prop27b: Int64Enum
375+
let prop27c: [Int64Enum]
365376

366377
let prop28a: IntEnum
367378
let prop28b: IntEnum
379+
let prop28c: [IntEnum]
368380

369381
let prop29a: DoubleEnum
370382
let prop29b: DoubleEnum
383+
let prop29c: [DoubleEnum]
371384

372385
let prop30a: FloatEnum
373386
let prop30b: FloatEnum
387+
let prop30c: [FloatEnum]
374388

375389
let prop31a: StringEnum
376390
let prop31b: StringEnum
391+
let prop31c: [StringEnum]
377392

378393
let prop32: [[String]]
379394
let prop33: [[String]]?
@@ -428,18 +443,23 @@ extension Struct: ImmutableMappable {
428443

429444
prop27a = try map.value("prop27a")
430445
prop27b = try map.value("prop27b")
446+
prop27c = try map.value("prop27c")
431447

432448
prop28a = try map.value("prop28a")
433449
prop28b = try map.value("prop28b")
450+
prop28c = try map.value("prop28c")
434451

435452
prop29a = try map.value("prop29a")
436453
prop29b = try map.value("prop29b")
454+
prop29c = try map.value("prop29c")
437455

438456
prop30a = try map.value("prop30a")
439457
prop30b = try map.value("prop30b")
458+
prop30c = try map.value("prop30c")
440459

441460
prop31a = try map.value("prop31a")
442461
prop31b = try map.value("prop31b")
462+
prop31c = try map.value("prop31c")
443463

444464
prop32 = try map.value("prop32", using: stringTransform)
445465
prop33 = try? map.value("prop33", using: stringTransform)
@@ -499,19 +519,24 @@ extension Struct: ImmutableMappable {
499519

500520
prop27a >>> map["prop27a"]
501521
prop27b >>> map["prop27b"]
522+
prop27c >>> map["prop27c"]
502523

503524
prop28a >>> map["prop28a"]
504525
prop28b >>> map["prop28b"]
526+
prop28c >>> map["prop28c"]
505527

506528
prop29a >>> map["prop29a"]
507529
prop29b >>> map["prop29b"]
530+
prop29c >>> map["prop29c"]
508531

509532
prop30a >>> map["prop30a"]
510533
prop30b >>> map["prop30b"]
534+
prop30c >>> map["prop30c"]
511535

512536
prop31a >>> map["prop31a"]
513537
prop31b >>> map["prop31b"]
514-
538+
prop31c >>> map["prop31c"]
539+
515540
prop32 >>> (map["prop32"], stringTransform)
516541
prop33 >>> (map["prop33"], stringTransform)
517542
prop34 >>> (map["prop34"], stringTransform)

0 commit comments

Comments
 (0)