diff --git a/Sources/RxCodable/Maybe+RxCodable.swift b/Sources/RxCodable/Maybe+RxCodable.swift index 93f6052..e83d10b 100644 --- a/Sources/RxCodable/Maybe+RxCodable.swift +++ b/Sources/RxCodable/Maybe+RxCodable.swift @@ -18,3 +18,48 @@ public extension PrimitiveSequenceType where TraitType == MaybeTrait, ElementTyp } } +public extension PrimitiveSequenceType where TraitType == MaybeTrait, ElementType == [String: Any] { + public func map(_ type: T.Type, using decoder: JSONDecoder? = nil) -> PrimitiveSequence where T: Decodable { + return self + .map { dict in try JSONSerialization.data(withJSONObject: dict) } + .map(type, using: decoder) + } +} + +public extension PrimitiveSequenceType where TraitType == MaybeTrait, ElementType == [[String: Any]] { + public func map(_ type: Array.Type, using decoder: JSONDecoder? = nil) -> PrimitiveSequence where T: Decodable { + return self + .flatMap { + Observable.from($0) + .map(T.self, using: decoder) + .toArray() + .asMaybe() + } + } +} + +public extension PrimitiveSequenceType where TraitType == MaybeTrait, ElementType: Encodable { + public func mapDictionary(_ encoder: JSONEncoder? = nil) -> PrimitiveSequence { + return self.map { encodable -> [String: Any] in + let data = try (encoder ?? JSONEncoder()).encode(encodable) + let dict = try JSONSerialization.jsonObject(with: data) as? [String: Any] + guard let dictionary = dict else { + throw RxCodableError.castingFailed(data: data, type: [String: Any].self) + } + return dictionary + } + } +} + +public extension PrimitiveSequenceType where TraitType == MaybeTrait, ElementType: Encodable { + public func mapJSONString(_ encoder: JSONEncoder? = nil, encoding: String.Encoding = .utf8) -> PrimitiveSequence { + return self.map { encodable -> String in + let data = try (encoder ?? JSONEncoder()).encode(encodable) + let json = String(data: data, encoding: encoding) + guard let jsonString = json else { + throw RxCodableError.castingFailed(data: data, type: String.self) + } + return jsonString + } + } +} diff --git a/Sources/RxCodable/ObservableType+RxCodable.swift b/Sources/RxCodable/ObservableType+RxCodable.swift index 7ff1765..dfcdd19 100644 --- a/Sources/RxCodable/ObservableType+RxCodable.swift +++ b/Sources/RxCodable/ObservableType+RxCodable.swift @@ -17,3 +17,47 @@ public extension ObservableType where E == String { .map(type, using: decoder) } } + +public extension ObservableType where E == [String: Any] { + public func map(_ type: T.Type, using decoder: JSONDecoder? = nil) -> Observable where T: Decodable { + return self + .map { dict in try JSONSerialization.data(withJSONObject: dict) } + .map(type, using: decoder) + } +} + +public extension ObservableType where E == [[String: Any]] { + public func map(_ type: Array.Type, using decoder: JSONDecoder? = nil) -> Observable<[T]> where T: Decodable { + return self + .flatMap { Observable.from($0) } + .map(T.self, using: decoder) + .toArray() + } +} + +public extension ObservableType where E: Encodable { + public func mapDictionary(_ encoder: JSONEncoder? = nil) -> Observable<[String: Any]> { + return self.map { encodable -> [String: Any] in + let data = try (encoder ?? JSONEncoder()).encode(encodable) + let dict = try JSONSerialization.jsonObject(with: data) as? [String: Any] + guard let dictionary = dict else { + throw RxCodableError.castingFailed(data: data, type: [String: Any].self) + } + return dictionary + } + } +} + +public extension ObservableType where E: Encodable { + public func mapJSONString(_ encoder: JSONEncoder? = nil, encoding: String.Encoding = .utf8) -> Observable { + return self.map { encodable -> String in + let data = try (encoder ?? JSONEncoder()).encode(encodable) + let json = String(data: data, encoding: encoding) + guard let jsonString = json else { + throw RxCodableError.castingFailed(data: data, type: String.self) + } + return jsonString + } + } +} + diff --git a/Sources/RxCodable/RxCodableError.swift b/Sources/RxCodable/RxCodableError.swift new file mode 100644 index 0000000..e563f7f --- /dev/null +++ b/Sources/RxCodable/RxCodableError.swift @@ -0,0 +1,5 @@ +import Foundation + +public enum RxCodableError: Error { + case castingFailed(data: Any, type: Any.Type) +} diff --git a/Sources/RxCodable/Single+RxCodable.swift b/Sources/RxCodable/Single+RxCodable.swift index 3eed6d0..dfb36fe 100644 --- a/Sources/RxCodable/Single+RxCodable.swift +++ b/Sources/RxCodable/Single+RxCodable.swift @@ -17,3 +17,49 @@ public extension PrimitiveSequenceType where TraitType == SingleTrait, ElementTy .map(type, using: decoder) } } + +public extension PrimitiveSequenceType where TraitType == SingleTrait, ElementType == [String: Any] { + public func map(_ type: T.Type, using decoder: JSONDecoder? = nil) -> PrimitiveSequence where T: Decodable { + return self + .map { dict in try JSONSerialization.data(withJSONObject: dict) } + .map(type, using: decoder) + } +} + +public extension PrimitiveSequenceType where TraitType == SingleTrait, ElementType == [[String: Any]] { + public func map(_ type: Array.Type, using decoder: JSONDecoder? = nil) -> PrimitiveSequence where T: Decodable { + return self + .flatMap { + Observable.from($0) + .map(T.self, using: decoder) + .toArray() + .asSingle() + } + } +} + +public extension PrimitiveSequenceType where TraitType == SingleTrait, ElementType: Encodable { + public func mapDictionary(_ encoder: JSONEncoder? = nil) -> PrimitiveSequence { + return self.map { encodable -> [String: Any] in + let data = try (encoder ?? JSONEncoder()).encode(encodable) + let dict = try JSONSerialization.jsonObject(with: data) as? [String: Any] + guard let dictionary = dict else { + throw RxCodableError.castingFailed(data: data, type: [String: Any].self) + } + return dictionary + } + } +} + +public extension PrimitiveSequenceType where TraitType == SingleTrait, ElementType: Encodable { + public func mapJSONString(_ encoder: JSONEncoder? = nil, encoding: String.Encoding = .utf8) -> PrimitiveSequence { + return self.map { encodable -> String in + let data = try (encoder ?? JSONEncoder()).encode(encodable) + let json = String(data: data, encoding: encoding) + guard let jsonString = json else { + throw RxCodableError.castingFailed(data: data, type: String.self) + } + return jsonString + } + } +} diff --git a/Tests/RxCodableTests/RxCodableTests.swift b/Tests/RxCodableTests/RxCodableTests.swift index 1bfbe39..4a9e474 100644 --- a/Tests/RxCodableTests/RxCodableTests.swift +++ b/Tests/RxCodableTests/RxCodableTests.swift @@ -76,4 +76,106 @@ class RxCodableTests: XCTestCase { XCTAssertEqual(try Single.just(jsonString).map([User].self).toBlocking().first()!, users) XCTAssertEqual(try Maybe.just(jsonString).map([User].self).toBlocking().first()!, users) } + + func testMapCodableFromDictionary() { + let dictionary: [String:Any] = [ + "id": 123, + "name": "iamchiwon", + ] + let user = User(id: 123, name: "iamchiwon") + XCTAssertEqual(try Observable.just(dictionary).map(User.self).toBlocking().first()!, user) + XCTAssertEqual(try Single.just(dictionary).map(User.self).toBlocking().first()!, user) + XCTAssertEqual(try Maybe.just(dictionary).map(User.self).toBlocking().first()!, user) + } + + func testMapCodablemapDictionary() { + let dictionary: [String:Any] = [ + "id": 123, + "name": "iamchiwon", + ] + let user = User(id: 123, name: "iamchiwon") + XCTAssertEqual(try Observable.just(user).mapDictionary().toBlocking().first()! as NSObject, dictionary as NSObject) + XCTAssertEqual(try Single.just(user).mapDictionary().toBlocking().first()! as NSObject, dictionary as NSObject) + XCTAssertEqual(try Maybe.just(user).mapDictionary().toBlocking().first()! as NSObject, dictionary as NSObject) + } + + func testMapCodablemapJSONString() { + let jsonString = """ + { + "id": 123, + "name": "iamchiwon" + } + """ + let user = User(id: 123, name: "iamchiwon") + XCTAssertEqual(try Observable.just(user).mapJSONString().toBlocking().first()!, + try Observable.just(jsonString).map(User.self).mapJSONString().toBlocking().first()!) + XCTAssertEqual(try Single.just(user).mapJSONString().toBlocking().first()!, + try Single.just(jsonString).map(User.self).mapJSONString().toBlocking().first()!) + XCTAssertEqual(try Maybe.just(user).mapJSONString().toBlocking().first()!, + try Maybe.just(jsonString).map(User.self).mapJSONString().toBlocking().first()!) + } + + func testMapCodableArraymapJSONString() { + let jsonString = """ + [ + { + "id": 123, + "name": "iamchiwon" + }, + { + "id": 456, + "name": "devxoul" + } + ] + """ + let users = [ + User(id: 123, name: "iamchiwon"), + User(id: 456, name: "devxoul"), + ] + XCTAssertEqual(try Observable.just(users).mapJSONString().toBlocking().first()!, + try Observable.just(jsonString).map([User].self).mapJSONString().toBlocking().first()!) + XCTAssertEqual(try Single.just(users).mapJSONString().toBlocking().first()!, + try Single.just(jsonString).map([User].self).mapJSONString().toBlocking().first()!) + XCTAssertEqual(try Maybe.just(users).mapJSONString().toBlocking().first()!, + try Maybe.just(jsonString).map([User].self).mapJSONString().toBlocking().first()!) + } + + func testMapCodableFromJSONStringmapDictionary() { + let jsonString = """ + { + "id": 123, + "name": "iamchiwon" + } + """ + let dictionary: [String:Any] = [ + "id": 123, + "name": "iamchiwon", + ] + XCTAssertEqual(try Observable.just(jsonString).map(User.self).mapDictionary().toBlocking().first()! as NSObject, dictionary as NSObject) + XCTAssertEqual(try Single.just(jsonString).map(User.self).mapDictionary().toBlocking().first()! as NSObject, dictionary as NSObject) + XCTAssertEqual(try Maybe.just(jsonString).map(User.self).mapDictionary().toBlocking().first()! as NSObject, dictionary as NSObject) + } + + func testMapCodableArraymapDictionary() { + let dictionaries: [[String:Any]] = [ + [ + "id": 123, + "name": "iamchiwon", + ], + [ + "id": 456, + "name": "devxoul", + ] + ] + let users = [ + User(id: 123, name: "iamchiwon"), + User(id: 456, name: "devxoul"), + ] + XCTAssertEqual(try Observable.just(dictionaries).map([User].self).mapJSONString().toBlocking().first()!, + try Observable.just(users).mapJSONString().toBlocking().first()!) + XCTAssertEqual(try Single.just(dictionaries).map([User].self).mapJSONString().toBlocking().first()!, + try Single.just(users).mapJSONString().toBlocking().first()!) + XCTAssertEqual(try Maybe.just(dictionaries).map([User].self).mapJSONString().toBlocking().first()!, + try Maybe.just(users).mapJSONString().toBlocking().first()!) + } }