Skip to content

Commit e228243

Browse files
authored
Merge pull request #160 from hyperoslo/fix/json_wrapper
Add json wrapper
2 parents 627387f + fcb5c90 commit e228243

File tree

5 files changed

+149
-2
lines changed

5 files changed

+149
-2
lines changed

Cache.xcodeproj/project.pbxproj

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,13 @@
104104
D2D4CC171FA3145000E4A2D5 /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC151FA3145000E4A2D5 /* MD5.swift */; };
105105
D2D4CC181FA3145000E4A2D5 /* MD5.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC151FA3145000E4A2D5 /* MD5.swift */; };
106106
D2D4CC1A1FA3166900E4A2D5 /* MD5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC191FA3166900E4A2D5 /* MD5Tests.swift */; };
107+
D2D4CC201FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC1F1FA3411300E4A2D5 /* JSONDictionaryWrapper.swift */; };
108+
D2D4CC211FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC1F1FA3411300E4A2D5 /* JSONDictionaryWrapper.swift */; };
109+
D2D4CC221FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC1F1FA3411300E4A2D5 /* JSONDictionaryWrapper.swift */; };
110+
D2D4CC241FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */; };
111+
D2D4CC251FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */; };
112+
D2D4CC261FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */; };
113+
D2D4CC281FA342CA00E4A2D5 /* JSONWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2D4CC271FA342CA00E4A2D5 /* JSONWrapperTests.swift */; };
107114
D5291D1D1C2837DB00B702C9 /* Cache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D5DC59E01C20593E003BD79B /* Cache.framework */; };
108115
D5291D6A1C283B5400B702C9 /* Cache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D5291D601C283B5300B702C9 /* Cache.framework */; };
109116
D5291D851C283C7C00B702C9 /* TestHelper+OSX.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5291D811C283C7000B702C9 /* TestHelper+OSX.swift */; };
@@ -179,6 +186,9 @@
179186
D2CF98881F695F9400CE8F68 /* TypeWrapperStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeWrapperStorageTests.swift; sourceTree = "<group>"; };
180187
D2D4CC151FA3145000E4A2D5 /* MD5.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MD5.swift; sourceTree = "<group>"; };
181188
D2D4CC191FA3166900E4A2D5 /* MD5Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MD5Tests.swift; sourceTree = "<group>"; };
189+
D2D4CC1F1FA3411300E4A2D5 /* JSONDictionaryWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDictionaryWrapper.swift; sourceTree = "<group>"; };
190+
D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONArrayWrapper.swift; sourceTree = "<group>"; };
191+
D2D4CC271FA342CA00E4A2D5 /* JSONWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONWrapperTests.swift; sourceTree = "<group>"; };
182192
D5291CDF1C28374800B702C9 /* TestHelper+iOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TestHelper+iOS.swift"; sourceTree = "<group>"; };
183193
D5291D181C2837DB00B702C9 /* Cache-iOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Cache-iOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
184194
D5291D231C28380100B702C9 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -295,6 +305,8 @@
295305
D2CF98861F695B8F00CE8F68 /* Types.swift */,
296306
D292DAFC1F6A970B0060F614 /* Result.swift */,
297307
D2D4CC151FA3145000E4A2D5 /* MD5.swift */,
308+
D2D4CC1F1FA3411300E4A2D5 /* JSONDictionaryWrapper.swift */,
309+
D2D4CC231FA3426B00E4A2D5 /* JSONArrayWrapper.swift */,
298310
);
299311
path = Library;
300312
sourceTree = "<group>";
@@ -342,6 +354,7 @@
342354
D2CF98771F69513800CE8F68 /* TypeWrapperTests.swift */,
343355
D285143E1F6FFE1F00C674D1 /* ObjectConverterTests.swift */,
344356
D2D4CC191FA3166900E4A2D5 /* MD5Tests.swift */,
357+
D2D4CC271FA342CA00E4A2D5 /* JSONWrapperTests.swift */,
345358
);
346359
path = Library;
347360
sourceTree = "<group>";
@@ -761,10 +774,12 @@
761774
D21B66871F6A723C00125DE1 /* ExpirationMode.swift in Sources */,
762775
D21B66881F6A723C00125DE1 /* Expiry.swift in Sources */,
763776
D21B669E1F6A724700125DE1 /* MemoryConfig.swift in Sources */,
777+
D2D4CC221FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */,
764778
D2D4CC181FA3145000E4A2D5 /* MD5.swift in Sources */,
765779
D21B66951F6A724000125DE1 /* MemoryStorage.swift in Sources */,
766780
D292DAF31F6A85F30060F614 /* SyncStorage.swift in Sources */,
767781
D21B668C1F6A723C00125DE1 /* Types.swift in Sources */,
782+
D2D4CC261FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */,
768783
D292DAFF1F6A970B0060F614 /* Result.swift in Sources */,
769784
D21B669A1F6A724300125DE1 /* Date+Extensions.swift in Sources */,
770785
D21B66891F6A723C00125DE1 /* ImageWrapper.swift in Sources */,
@@ -806,6 +821,7 @@
806821
D2CF987F1F69513800CE8F68 /* ImageWrapperTests.swift in Sources */,
807822
D2D4CC1A1FA3166900E4A2D5 /* MD5Tests.swift in Sources */,
808823
D292DB041F6AA0730060F614 /* AsyncStorageTests.swift in Sources */,
824+
D2D4CC281FA342CA00E4A2D5 /* JSONWrapperTests.swift in Sources */,
809825
D28C9BAF1F67EF8300C180C1 /* UIImage+ExtensionsTests.swift in Sources */,
810826
D2CF987D1F69513800CE8F68 /* MemoryCapsuleTests.swift in Sources */,
811827
D2CF98801F69513800CE8F68 /* TypeWrapperTests.swift in Sources */,
@@ -828,10 +844,12 @@
828844
D21B667E1F6A723C00125DE1 /* ExpirationMode.swift in Sources */,
829845
D21B667F1F6A723C00125DE1 /* Expiry.swift in Sources */,
830846
D21B669C1F6A724600125DE1 /* MemoryConfig.swift in Sources */,
847+
D2D4CC211FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */,
831848
D2D4CC171FA3145000E4A2D5 /* MD5.swift in Sources */,
832849
D21B668F1F6A723F00125DE1 /* MemoryStorage.swift in Sources */,
833850
D292DAF21F6A85F30060F614 /* SyncStorage.swift in Sources */,
834851
D21B66831F6A723C00125DE1 /* Types.swift in Sources */,
852+
D2D4CC251FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */,
835853
D292DAFE1F6A970B0060F614 /* Result.swift in Sources */,
836854
D21B66991F6A724200125DE1 /* Date+Extensions.swift in Sources */,
837855
D21B66801F6A723C00125DE1 /* ImageWrapper.swift in Sources */,
@@ -874,10 +892,12 @@
874892
D2CF98621F694FFA00CE8F68 /* Date+Extensions.swift in Sources */,
875893
D2CF98641F694FFA00CE8F68 /* DataSerializer.swift in Sources */,
876894
D2CF986B1F694FFA00CE8F68 /* DiskStorage.swift in Sources */,
895+
D2D4CC201FA3411300E4A2D5 /* JSONDictionaryWrapper.swift in Sources */,
877896
D2D4CC161FA3145000E4A2D5 /* MD5.swift in Sources */,
878897
D292DAF11F6A85F30060F614 /* SyncStorage.swift in Sources */,
879898
D2CF98601F694FFA00CE8F68 /* DiskConfig.swift in Sources */,
880899
D292DAFD1F6A970B0060F614 /* Result.swift in Sources */,
900+
D2D4CC241FA3426B00E4A2D5 /* JSONArrayWrapper.swift in Sources */,
881901
D2CF986D1F694FFA00CE8F68 /* MemoryStorage.swift in Sources */,
882902
D2CF986F1F694FFA00CE8F68 /* StorageAware.swift in Sources */,
883903
D2CF98851F69598900CE8F68 /* TypeWrapperStorage.swift in Sources */,

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,9 @@ If you want to load image into `UIImageView` or `NSImageView`, then we also have
277277

278278
Most of the time, our use case is to fetch some json from backend, display it while saving the json to storage for future uses. If you're using libraries like [Alamofire](https://github.com/Alamofire/Alamofire) or [Malibu](https://github.com/hyperoslo/Malibu), you mostly get json in the form of dictionary, string, or data.
279279

280-
While `Storage` can persist `String` or `Data`, we recommend persisting the strong typed objects, since those are the objects that you will use to display in UI. Furthermore, if the json data can't be converted to strongly typed objects, what's the point of saving it ? 😉
280+
`Storage` can persist `String` or `Data`. You can even save json to `Storage` using `JSONArrayWrapper` and `JSONDictionaryWrapper`, but we prefer persisting the strong typed objects, since those are the objects that you will use to display in UI. Furthermore, if the json data can't be converted to strongly typed objects, what's the point of saving it ? 😉
281281

282-
You can use `JSONDecoder` to decode json dictionary, string or data to objects.
282+
You can use these extensions on `JSONDecoder` to decode json dictionary, string or data to objects.
283283

284284
```swift
285285
let user = JSONDecoder.decode(jsonString, to: User.self)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import Foundation
2+
3+
public typealias JSONArray = [JSONDictionary]
4+
5+
public struct JSONArrayWrapper: Codable {
6+
public let jsonArray: JSONArray
7+
8+
public enum CodingKeys: String, CodingKey {
9+
case jsonArray
10+
}
11+
12+
public init(jsonArray: JSONArray) {
13+
self.jsonArray = jsonArray
14+
}
15+
16+
public init(from decoder: Decoder) throws {
17+
let container = try decoder.container(keyedBy: CodingKeys.self)
18+
let data = try container.decode(Data.self, forKey: CodingKeys.jsonArray)
19+
let object = try JSONSerialization.jsonObject(
20+
with: data,
21+
options: []
22+
)
23+
24+
guard let jsonArray = object as? JSONArray else {
25+
throw StorageError.decodingFailed
26+
}
27+
28+
self.jsonArray = jsonArray
29+
}
30+
31+
public func encode(to encoder: Encoder) throws {
32+
var container = encoder.container(keyedBy: CodingKeys.self)
33+
let data = try JSONSerialization.data(
34+
withJSONObject: jsonArray,
35+
options: []
36+
)
37+
38+
try container.encode(data, forKey: CodingKeys.jsonArray)
39+
}
40+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import Foundation
2+
3+
public typealias JSONDictionary = [String: Any]
4+
5+
public struct JSONDictionaryWrapper: Codable {
6+
public let jsonDictionary: JSONDictionary
7+
8+
public enum CodingKeys: String, CodingKey {
9+
case jsonDictionary
10+
}
11+
12+
public init(jsonDictionary: JSONDictionary) {
13+
self.jsonDictionary = jsonDictionary
14+
}
15+
16+
public init(from decoder: Decoder) throws {
17+
let container = try decoder.container(keyedBy: CodingKeys.self)
18+
let data = try container.decode(Data.self, forKey: CodingKeys.jsonDictionary)
19+
let object = try JSONSerialization.jsonObject(
20+
with: data,
21+
options: []
22+
)
23+
24+
guard let jsonDictionary = object as? JSONDictionary else {
25+
throw StorageError.decodingFailed
26+
}
27+
28+
self.jsonDictionary = jsonDictionary
29+
}
30+
31+
public func encode(to encoder: Encoder) throws {
32+
var container = encoder.container(keyedBy: CodingKeys.self)
33+
let data = try JSONSerialization.data(
34+
withJSONObject: jsonDictionary,
35+
options: []
36+
)
37+
38+
try container.encode(data, forKey: CodingKeys.jsonDictionary)
39+
}
40+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import XCTest
2+
import Cache
3+
4+
final class JSONWrapperTests: XCTestCase {
5+
func testJSONDictionary() {
6+
let json: JSONDictionary = [
7+
"name": "John Snow",
8+
"location": "Winterfell"
9+
]
10+
11+
let wrapper = JSONDictionaryWrapper(jsonDictionary: json)
12+
13+
let data = try! JSONEncoder().encode(wrapper)
14+
let decodedWrapper = try! JSONDecoder().decode(JSONDictionaryWrapper.self, from: data)
15+
16+
XCTAssertEqual(
17+
NSDictionary(dictionary: decodedWrapper.jsonDictionary),
18+
NSDictionary(dictionary: json)
19+
)
20+
}
21+
22+
func testJSONArray() {
23+
let json: JSONArray = [
24+
[
25+
"name": "John Snow",
26+
"location": "Winterfell"
27+
],
28+
[
29+
"name": "Daenerys Targaryen",
30+
"location": "Dragonstone"
31+
]
32+
]
33+
34+
let wrapper = JSONArrayWrapper(jsonArray: json)
35+
36+
let data = try! JSONEncoder().encode(wrapper)
37+
let decodedWrapper = try! JSONDecoder().decode(JSONArrayWrapper.self, from: data)
38+
39+
zip(json, decodedWrapper.jsonArray).forEach {
40+
XCTAssertEqual(
41+
NSDictionary(dictionary: $0),
42+
NSDictionary(dictionary: $1)
43+
)
44+
}
45+
}
46+
}
47+

0 commit comments

Comments
 (0)