├── .gitignore ├── .travis.yml ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md ├── RxJSON.podspec ├── Sources └── RxJSON │ ├── Functions.swift │ ├── Maybe+RxJSON.swift │ ├── ObservableType+RxJSON.swift │ ├── RxJSONError.swift │ └── Single+RxJSON.swift ├── Tests └── RxJSONTests │ ├── ArrayTests.swift │ └── DictionaryTests.swift └── codecov.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | osx_image: xcode10.2 2 | language: objective-c 3 | sudo: required 4 | env: 5 | global: 6 | - PROJECT="RxJSON.xcodeproj" 7 | - SCHEME="RxJSON-Package" 8 | - IOS_SDK="iphonesimulator12.2" 9 | - MACOS_SDK="macosx10.14" 10 | - TVOS_SDK="appletvsimulator12.2" 11 | matrix: 12 | - SDK="$IOS_SDK" DESTINATION="platform=iOS Simulator,name=iPhone 7,OS=12.2" 13 | - SDK="$MACOS_SDK" DESTINATION="arch=x86_64" 14 | - SDK="$TVOS_SDK" DESTINATION="OS=12.2,name=Apple TV 4K" 15 | 16 | install: 17 | - eval "$(curl -sL https://gist.githubusercontent.com/kylef/5c0475ff02b7c7671d2a/raw/9f442512a46d7a2af7b850d65a7e9bd31edfb09b/swiftenv-install.sh)" 18 | - swift --version 19 | 20 | before_script: 21 | - set -o pipefail 22 | - TEST=1 swift package generate-xcodeproj 23 | 24 | script: 25 | - xcodebuild clean build test 26 | -project "$PROJECT" 27 | -scheme "$SCHEME" 28 | -sdk "$SDK" 29 | -destination "$DESTINATION" 30 | -configuration Debug 31 | -enableCodeCoverage YES 32 | CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty -c 33 | 34 | after_success: 35 | - bash <(curl -s https://codecov.io/bash) -X xcodeplist -J 'RxJSON' 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Suyeol Jeon (xoul.kr) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "RxSwift", 6 | "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "b3e888b4972d9bc76495dd74d30a8c7fad4b9395", 10 | "version": "5.0.1" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "RxJSON", 7 | platforms: [ 8 | .macOS(.v10_11), .iOS(.v8), .tvOS(.v9), .watchOS(.v2) 9 | ], 10 | products: [ 11 | .library(name: "RxJSON", targets: ["RxJSON"]), 12 | ], 13 | dependencies: [ 14 | .package(url: "https://github.com/ReactiveX/RxSwift.git", .upToNextMajor(from: "5.0.0")), 15 | ], 16 | targets: [ 17 | .target(name: "RxJSON", dependencies: ["RxSwift"]), 18 | .testTarget(name: "RxJSONTests", dependencies: ["RxJSON", "RxBlocking"]), 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxJSON 2 | 3 | ![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg) 4 | [![CocoaPods](http://img.shields.io/cocoapods/v/RxJSON.svg)](https://cocoapods.org/pods/RxJSON) 5 | [![Build Status](https://travis-ci.org/devxoul/RxJSON.svg?branch=master)](https://travis-ci.org/devxoul/RxJSON) 6 | [![codecov](https://img.shields.io/codecov/c/github/devxoul/RxJSON.svg)](https://codecov.io/gh/devxoul/RxJSON) 7 | 8 | RxSwift wrapper for JSON. 9 | 10 | ## At a Glance 11 | 12 | This is an example of converting a json dictionary observable to a string observable: 13 | 14 | ```swift 15 | URLSession.shared.rx.json(url: "https://api.github.com/repos/ReactorKit/ReactorKit") 16 | .mapJSON("owner") // Observable -> Observable 17 | .mapJSON("login", String.self) // Observable -> Observable 18 | .bind(to: ownerNameLabel.rx.text) 19 | ``` 20 | 21 | `mapJSON()` supports both JSON dictionary and array: 22 | 23 | ```swift 24 | // Dictionary 25 | Observable.mapJSON("key") // Observable 26 | Observable.mapJSON("key", Int.self) // Observable 27 | 28 | // Array 29 | Observable.mapJSON(at: 2) // Observable 30 | Observable.mapJSON(at: 3, String.self) // Observable 31 | ``` 32 | 33 | `mapJSON()` will throw a `RxJSONError` when there's no value for given accessor or fails to cast to a given type: 34 | 35 | ```swift 36 | // Dictionary 37 | source.mapJSON("unknownKey") // Event.error(RxJSONError.valueNotFound) 38 | source.mapJSON("name", Int.key) // Event.error(RxJSONError.castingFailed) 39 | 40 | // Array 41 | source.mapJSON(at: -1) // Event.error(RxJSONError.valueNotFound) 42 | source.mapJSON(at: 0, Int.key) // Event.error(RxJSONError.castingFailed) 43 | ``` 44 | 45 | ## Installation 46 | 47 | * **Using [CocoaPods](https://cocoapods.org)**: 48 | 49 | ```ruby 50 | pod 'RxJSON' 51 | ``` 52 | 53 | * **Using [Carthage](https://github.com/Carthage/Carthage)**: 54 | 55 | 56 | This is not supported yet. See [Carthage#1945](https://github.com/Carthage/Carthage/pull/1945) for details. 57 | 58 | ## Contributing 59 | 60 | Any discussions and pull requests are welcomed 💖 61 | 62 | To create a Xcode project: 63 | 64 | ```console 65 | $ swift package generate-xcodeproj 66 | ``` 67 | 68 | ## License 69 | 70 | RxJSON is under MIT license. See the [LICENSE](LICENSE) file for more info. 71 | -------------------------------------------------------------------------------- /RxJSON.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "RxJSON" 3 | s.version = "2.0.0" 4 | s.summary = "RxSwift wrapper for JSON" 5 | s.homepage = "https://github.com/devxoul/RxJSON" 6 | s.license = { :type => "MIT", :file => "LICENSE" } 7 | s.author = { "Suyeol Jeon" => "devxoul@gmail.com" } 8 | s.source = { :git => "https://github.com/devxoul/RxJSON.git", 9 | :tag => s.version.to_s } 10 | s.source_files = "Sources/**/*.swift" 11 | s.dependency "RxSwift", "~> 5.0" 12 | s.swift_version = "5.0" 13 | 14 | s.ios.deployment_target = "8.0" 15 | s.osx.deployment_target = "10.11" 16 | s.tvos.deployment_target = "9.0" 17 | s.watchos.deployment_target = "3.0" 18 | end 19 | -------------------------------------------------------------------------------- /Sources/RxJSON/Functions.swift: -------------------------------------------------------------------------------- 1 | internal func mapJSONDictionary(_ key: String, _ type: T.Type) -> (_ original: Any) throws -> T { 2 | return { original -> T in 3 | guard let json = original as? [String: Any] else { 4 | throw RxJSONError.castingFailed(json: original, accessor: .key(key), type: [String: Any].self) 5 | } 6 | guard let rawValue = json[key] else { 7 | throw RxJSONError.valueNotFound(json: json, accessor: .key(key)) 8 | } 9 | guard let value = rawValue as? T else { 10 | throw RxJSONError.castingFailed(json: original, accessor: .key(key), type: T.self) 11 | } 12 | return value 13 | } 14 | } 15 | 16 | internal func mapJSONArray(_ index: Int, _ type: T.Type) -> (_ original: Any) throws -> T { 17 | return { original -> T in 18 | guard let jsonArray = original as? [Any] else { 19 | throw RxJSONError.castingFailed(json: original, accessor: .index(index), type: [Any].self) 20 | } 21 | guard jsonArray.indices.contains(index) else { 22 | throw RxJSONError.valueNotFound(json: jsonArray, accessor: .index(index)) 23 | } 24 | guard let value = jsonArray[index] as? T else { 25 | throw RxJSONError.castingFailed(json: original, accessor: .index(index), type: T.self) 26 | } 27 | return value 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/RxJSON/Maybe+RxJSON.swift: -------------------------------------------------------------------------------- 1 | import RxSwift 2 | 3 | public extension PrimitiveSequenceType where Trait == MaybeTrait { 4 | /// Converts each element to a JSON dictionary and projects each value for given key to a new 5 | /// observable sequence. 6 | /// 7 | /// - parameter key: A key for finding value from each JSON dictionary. 8 | /// - throws: `RxJSONError` when failed to cast each element to a JSON dictionary or failed to 9 | /// find an element with given key. 10 | /// - returns: An observable sequence whose elements are the value of each element of source. 11 | func mapJSON(_ key: String) -> PrimitiveSequence { 12 | return self.map(mapJSONDictionary(key, Element.self)) 13 | } 14 | 15 | /// Converts each element to a JSON dictionary and projects each value for given key to a new 16 | /// observable sequence. 17 | /// 18 | /// - parameter key: A key for finding value from each JSON dictionary. 19 | /// - parameter type: A type reference to cast result. 20 | /// - throws: `RxJSONError` when failed to cast each element to a JSON dictionary or failed to 21 | /// cast an element with given key to a given type. 22 | /// - returns: An observable sequence whose elements are the value of each element of source. 23 | func mapJSON(_ key: String, _ type: T.Type) -> PrimitiveSequence { 24 | return self.map(mapJSONDictionary(key, type)) 25 | } 26 | 27 | /// Converts each element to a JSON array and projects each value for given index to a new 28 | /// observable sequence. 29 | /// 30 | /// - parameter index: An index for finding value from each JSON array. 31 | /// - throws: `RxJSONError` when failed to cast each element to a JSON array or failed to 32 | /// find an element with given index. 33 | /// - returns: An observable sequence whose elements are the value of each element of source. 34 | func mapJSON(at index: Int) -> PrimitiveSequence { 35 | return self.map(mapJSONArray(index, Any.self)) 36 | } 37 | 38 | /// Converts each element to a JSON array and projects each value for given index to a new 39 | /// observable sequence. 40 | /// 41 | /// - parameter index: An index for finding value from each JSON array. 42 | /// - parameter type: A type reference to cast result. 43 | /// - throws: `RxJSONError` when failed to cast each element to a JSON array or failed to 44 | /// find an element with given index. 45 | /// - returns: An observable sequence whose elements are the value of each element of source. 46 | func mapJSON(at index: Int, _ type: T.Type) -> PrimitiveSequence { 47 | return self.map(mapJSONArray(index, T.self)) 48 | } 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /Sources/RxJSON/ObservableType+RxJSON.swift: -------------------------------------------------------------------------------- 1 | import RxSwift 2 | 3 | public extension ObservableType { 4 | /// Converts each element to a JSON dictionary and projects each value for given key to a new 5 | /// observable sequence. 6 | /// 7 | /// - parameter key: A key for finding value from each JSON dictionary. 8 | /// - throws: `RxJSONError` when failed to cast each element to a JSON dictionary or failed to 9 | /// find an element with given key. 10 | /// - returns: An observable sequence whose elements are the value of each element of source. 11 | func mapJSON(_ key: String) -> Observable { 12 | return self.map(mapJSONDictionary(key, Any.self)) 13 | } 14 | 15 | /// Converts each element to a JSON dictionary and projects each value for given key to a new 16 | /// observable sequence. 17 | /// 18 | /// - parameter key: A key for finding value from each JSON dictionary. 19 | /// - parameter type: A type reference to cast result. 20 | /// - throws: `RxJSONError` when failed to cast each element to a JSON dictionary or failed to 21 | /// cast an element with given key to a given type. 22 | /// - returns: An observable sequence whose elements are the value of each element of source. 23 | func mapJSON(_ key: String, _ type: T.Type) -> Observable { 24 | return self.map(mapJSONDictionary(key, type)) 25 | } 26 | 27 | /// Converts each element to a JSON array and projects each value for given index to a new 28 | /// observable sequence. 29 | /// 30 | /// - parameter index: An index for finding value from each JSON array. 31 | /// - throws: `RxJSONError` when failed to cast each element to a JSON array or failed to 32 | /// find an element with given index. 33 | /// - returns: An observable sequence whose elements are the value of each element of source. 34 | func mapJSON(at index: Int) -> Observable { 35 | return self.map(mapJSONArray(index, Any.self)) 36 | } 37 | 38 | /// Converts each element to a JSON array and projects each value for given index to a new 39 | /// observable sequence. 40 | /// 41 | /// - parameter index: An index for finding value from each JSON array. 42 | /// - parameter type: A type reference to cast result. 43 | /// - throws: `RxJSONError` when failed to cast each element to a JSON array or failed to 44 | /// find an element with given index. 45 | /// - returns: An observable sequence whose elements are the value of each element of source. 46 | func mapJSON(at index: Int, _ type: T.Type) -> Observable { 47 | return self.map(mapJSONArray(index, T.self)) 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /Sources/RxJSON/RxJSONError.swift: -------------------------------------------------------------------------------- 1 | public enum Accessor { 2 | case key(String) 3 | case index(Int) 4 | } 5 | 6 | public enum RxJSONError: Error { 7 | case castingFailed(json: Any, accessor: Accessor, type: Any.Type) 8 | case valueNotFound(json: Any, accessor: Accessor) 9 | } 10 | -------------------------------------------------------------------------------- /Sources/RxJSON/Single+RxJSON.swift: -------------------------------------------------------------------------------- 1 | import RxSwift 2 | 3 | public extension PrimitiveSequenceType where Trait == SingleTrait { 4 | /// Converts each element to a JSON dictionary and projects each value for given key to a new 5 | /// observable sequence. 6 | /// 7 | /// - parameter key: A key for finding value from each JSON dictionary. 8 | /// - throws: `RxJSONError` when failed to cast each element to a JSON dictionary or failed to 9 | /// find an element with given key. 10 | /// - returns: An observable sequence whose elements are the value of each element of source. 11 | func mapJSON(_ key: String) -> PrimitiveSequence { 12 | return self.map(mapJSONDictionary(key, Element.self)) 13 | } 14 | 15 | /// Converts each element to a JSON dictionary and projects each value for given key to a new 16 | /// observable sequence. 17 | /// 18 | /// - parameter key: A key for finding value from each JSON dictionary. 19 | /// - parameter type: A type reference to cast result. 20 | /// - throws: `RxJSONError` when failed to cast each element to a JSON dictionary or failed to 21 | /// cast an element with given key to a given type. 22 | /// - returns: An observable sequence whose elements are the value of each element of source. 23 | func mapJSON(_ key: String, _ type: T.Type) -> PrimitiveSequence { 24 | return self.map(mapJSONDictionary(key, type)) 25 | } 26 | 27 | /// Converts each element to a JSON array and projects each value for given index to a new 28 | /// observable sequence. 29 | /// 30 | /// - parameter index: An index for finding value from each JSON array. 31 | /// - throws: `RxJSONError` when failed to cast each element to a JSON array or failed to 32 | /// find an element with given index. 33 | /// - returns: An observable sequence whose elements are the value of each element of source. 34 | func mapJSON(at index: Int) -> PrimitiveSequence { 35 | return self.map(mapJSONArray(index, Any.self)) 36 | } 37 | 38 | /// Converts each element to a JSON array and projects each value for given index to a new 39 | /// observable sequence. 40 | /// 41 | /// - parameter index: An index for finding value from each JSON array. 42 | /// - parameter type: A type reference to cast result. 43 | /// - throws: `RxJSONError` when failed to cast each element to a JSON array or failed to 44 | /// find an element with given index. 45 | /// - returns: An observable sequence whose elements are the value of each element of source. 46 | func mapJSON(at index: Int, _ type: T.Type) -> PrimitiveSequence { 47 | return self.map(mapJSONArray(index, T.self)) 48 | } 49 | } 50 | 51 | 52 | -------------------------------------------------------------------------------- /Tests/RxJSONTests/ArrayTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import RxSwift 3 | import RxBlocking 4 | import RxJSON 5 | 6 | class ArrayTests: XCTestCase { 7 | 8 | // MARK: mapJSON(key:) 9 | 10 | func testMapJSONWithIndex_throws_whenJSONIsNotArray() { 11 | let json: [String: Any] = ["name": "devxoul"] 12 | XCTAssertThrowsError( 13 | try Observable.just(json) 14 | .mapJSON(at: 0) 15 | .toBlocking() 16 | .first()! 17 | ) 18 | XCTAssertThrowsError( 19 | try Single.just(json) 20 | .mapJSON(at: 0) 21 | .toBlocking() 22 | .first()! 23 | ) 24 | XCTAssertThrowsError( 25 | try Maybe.just(json) 26 | .mapJSON(at: 0) 27 | .toBlocking() 28 | .first()! 29 | ) 30 | } 31 | 32 | func testMapJSONWithIndex_throws_whenIndexNotExists() { 33 | let json: [Any] = ["Swift", "Python", "JavaScript"] 34 | XCTAssertThrowsError( 35 | try Observable.just(json) 36 | .mapJSON(at: 3) 37 | .toBlocking() 38 | .first() 39 | ) 40 | XCTAssertThrowsError( 41 | try Single.just(json) 42 | .mapJSON(at: 3) 43 | .toBlocking() 44 | .first() 45 | ) 46 | XCTAssertThrowsError( 47 | try Maybe.just(json) 48 | .mapJSON(at: 3) 49 | .toBlocking() 50 | .first() 51 | ) 52 | } 53 | 54 | func testMapJSONWithIndex_succeeds() { 55 | let json: [Any] = ["Swift", "Python", "JavaScript"] 56 | XCTAssertEqual( 57 | try Observable.just(json) 58 | .mapJSON(at: 1) 59 | .toBlocking() 60 | .first() as! String, 61 | "Python" 62 | ) 63 | XCTAssertEqual( 64 | try Single.just(json) 65 | .mapJSON(at: 1) 66 | .toBlocking() 67 | .first() as! String, 68 | "Python" 69 | ) 70 | XCTAssertEqual( 71 | try Maybe.just(json) 72 | .mapJSON(at: 1) 73 | .toBlocking() 74 | .first() as! String, 75 | "Python" 76 | ) 77 | } 78 | 79 | 80 | // MARK: mapJSON(key:type:) 81 | 82 | func testMapJSONWithIndexAndType_throws_whenJSONIsNotArray() { 83 | let json: [String: Any] = ["name": 123] 84 | XCTAssertThrowsError( 85 | try Observable.just(json) 86 | .mapJSON(at: 0, Int.self) 87 | .toBlocking() 88 | .first()! 89 | ) 90 | XCTAssertThrowsError( 91 | try Single.just(json) 92 | .mapJSON(at: 0, Int.self) 93 | .toBlocking() 94 | .first()! 95 | ) 96 | XCTAssertThrowsError( 97 | try Maybe.just(json) 98 | .mapJSON(at: 0, Int.self) 99 | .toBlocking() 100 | .first()! 101 | ) 102 | } 103 | 104 | func testMapJSONWithIndexAndType_throws_whenIndexNotExists() { 105 | let json: [Any] = ["Swift", "Python", "JavaScript"] 106 | XCTAssertThrowsError( 107 | try Observable.just(json) 108 | .mapJSON(at: 3, String.self) 109 | .toBlocking() 110 | .first()! 111 | ) 112 | XCTAssertThrowsError( 113 | try Single.just(json) 114 | .mapJSON(at: 3, String.self) 115 | .toBlocking() 116 | .first()! 117 | ) 118 | XCTAssertThrowsError( 119 | try Maybe.just(json) 120 | .mapJSON(at: 3, String.self) 121 | .toBlocking() 122 | .first()! 123 | ) 124 | } 125 | 126 | func testMapJSONWithIndexAndType_throws_whenFailedToCast() { 127 | let json: [Any] = ["Swift", "Python", "JavaScript"] 128 | XCTAssertThrowsError( 129 | try Observable.just(json) 130 | .mapJSON(at: 2, Int.self) 131 | .toBlocking() 132 | .first()! 133 | ) 134 | XCTAssertThrowsError( 135 | try Single.just(json) 136 | .mapJSON(at: 2, Int.self) 137 | .toBlocking() 138 | .first()! 139 | ) 140 | XCTAssertThrowsError( 141 | try Maybe.just(json) 142 | .mapJSON(at: 2, Int.self) 143 | .toBlocking() 144 | .first()! 145 | ) 146 | } 147 | 148 | func testMapJSONWithIndexAndType_succeeds() { 149 | let json: [Any] = ["Swift", "Python", "JavaScript"] 150 | XCTAssertEqual( 151 | try Observable.just(json) 152 | .mapJSON(at: 0, String.self) 153 | .toBlocking() 154 | .first()!, 155 | "Swift" 156 | ) 157 | XCTAssertEqual( 158 | try Single.just(json) 159 | .mapJSON(at: 0, String.self) 160 | .toBlocking() 161 | .first()!, 162 | "Swift" 163 | ) 164 | XCTAssertEqual( 165 | try Maybe.just(json) 166 | .mapJSON(at: 0, String.self) 167 | .toBlocking() 168 | .first()!, 169 | "Swift" 170 | ) 171 | } 172 | 173 | 174 | // MARK: Nested 175 | 176 | func testMapJSON_nested_succeeds() { 177 | let json: [Any] = [ 178 | ["name": "devxoul"], 179 | ["name": "helloworld"], 180 | ] 181 | XCTAssertEqual( 182 | try Observable.just(json) 183 | .mapJSON(at: 0) 184 | .mapJSON("name", String.self) 185 | .toBlocking() 186 | .first()!, 187 | "devxoul" 188 | ) 189 | XCTAssertEqual( 190 | try Single.just(json) 191 | .mapJSON(at: 0) 192 | .mapJSON("name", String.self) 193 | .toBlocking() 194 | .first()!, 195 | "devxoul" 196 | ) 197 | XCTAssertEqual( 198 | try Maybe.just(json) 199 | .mapJSON(at: 0) 200 | .mapJSON("name", String.self) 201 | .toBlocking() 202 | .first()!, 203 | "devxoul" 204 | ) 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /Tests/RxJSONTests/DictionaryTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | import RxSwift 3 | import RxBlocking 4 | import RxJSON 5 | 6 | class DictionaryTests: XCTestCase { 7 | 8 | // MARK: mapJSON(key:) 9 | 10 | func testMapJSONWithKey_throws_whenJSONIsNotDictionary() { 11 | let json: [Any] = ["Swift", "Python", "JavaScript"] 12 | XCTAssertThrowsError( 13 | try Observable.just(json) 14 | .mapJSON("Swift") 15 | .toBlocking() 16 | .first()! 17 | ) 18 | XCTAssertThrowsError( 19 | try Single.just(json) 20 | .mapJSON("Swift") 21 | .toBlocking() 22 | .first()! 23 | ) 24 | XCTAssertThrowsError( 25 | try Maybe.just(json) 26 | .mapJSON("Swift") 27 | .toBlocking() 28 | .first()! 29 | ) 30 | } 31 | 32 | func testMapJSONWithKey_throws_whenKeyNotExists() { 33 | let json: [String: Any] = ["name": "devxoul"] 34 | XCTAssertThrowsError( 35 | try Observable.just(json) 36 | .mapJSON("email") 37 | .toBlocking() 38 | .first() 39 | ) 40 | XCTAssertThrowsError( 41 | try Single.just(json) 42 | .mapJSON("email") 43 | .toBlocking() 44 | .first() 45 | ) 46 | XCTAssertThrowsError( 47 | try Maybe.just(json) 48 | .mapJSON("email") 49 | .toBlocking() 50 | .first() 51 | ) 52 | } 53 | 54 | func testMapJSONWithKey_succeeds() { 55 | let json: [String: Any] = ["name": "devxoul"] 56 | XCTAssertEqual( 57 | try Observable.just(json) 58 | .mapJSON("name") 59 | .toBlocking() 60 | .first() as! String, 61 | "devxoul" 62 | ) 63 | XCTAssertEqual( 64 | try Single.just(json) 65 | .mapJSON("name") 66 | .toBlocking() 67 | .first() as! String, 68 | "devxoul" 69 | ) 70 | XCTAssertEqual( 71 | try Maybe.just(json) 72 | .mapJSON("name") 73 | .toBlocking() 74 | .first() as! String, 75 | "devxoul" 76 | ) 77 | } 78 | 79 | 80 | // MARK: mapJSON(key:type:) 81 | 82 | func testMapJSONWithKeyAndType_throws_whenJSONIsNotDictionary() { 83 | let json: [Any] = ["Swift", "Python", "JavaScript"] 84 | XCTAssertThrowsError( 85 | try Observable.just(json) 86 | .mapJSON("Swift", String.self) 87 | .toBlocking() 88 | .first()! 89 | ) 90 | XCTAssertThrowsError( 91 | try Single.just(json) 92 | .mapJSON("Swift", String.self) 93 | .toBlocking() 94 | .first()! 95 | ) 96 | XCTAssertThrowsError( 97 | try Maybe.just(json) 98 | .mapJSON("Swift", String.self) 99 | .toBlocking() 100 | .first()! 101 | ) 102 | } 103 | 104 | func testMapJSONWithKeyAndType_throws_whenKeyNotExists() { 105 | let json: [String: Any] = ["name": 123] 106 | XCTAssertThrowsError( 107 | try Observable.just(json) 108 | .mapJSON("age", String.self) 109 | .toBlocking() 110 | .first()! 111 | ) 112 | XCTAssertThrowsError( 113 | try Single.just(json) 114 | .mapJSON("age", String.self) 115 | .toBlocking() 116 | .first()! 117 | ) 118 | XCTAssertThrowsError( 119 | try Maybe.just(json) 120 | .mapJSON("age", String.self) 121 | .toBlocking() 122 | .first()! 123 | ) 124 | } 125 | 126 | func testMapJSONWithKeyAndType_throws_whenFailedToCast() { 127 | let json: [String: Any] = ["name": 123] 128 | XCTAssertThrowsError( 129 | try Observable.just(json) 130 | .mapJSON("name", String.self) 131 | .toBlocking() 132 | .first()! 133 | ) 134 | XCTAssertThrowsError( 135 | try Single.just(json) 136 | .mapJSON("name", String.self) 137 | .toBlocking() 138 | .first()! 139 | ) 140 | XCTAssertThrowsError( 141 | try Maybe.just(json) 142 | .mapJSON("name", String.self) 143 | .toBlocking() 144 | .first()! 145 | ) 146 | } 147 | 148 | func testMapJSONWithKeyAndType_succeeds() { 149 | let json: [String: Any] = ["name": "devxoul"] 150 | XCTAssertEqual( 151 | try Observable.just(json) 152 | .mapJSON("name", String.self) 153 | .toBlocking() 154 | .first()!, 155 | "devxoul" 156 | ) 157 | XCTAssertEqual( 158 | try Single.just(json) 159 | .mapJSON("name", String.self) 160 | .toBlocking() 161 | .first()!, 162 | "devxoul" 163 | ) 164 | XCTAssertEqual( 165 | try Maybe.just(json) 166 | .mapJSON("name", String.self) 167 | .toBlocking() 168 | .first()!, 169 | "devxoul" 170 | ) 171 | } 172 | 173 | 174 | // MARK: Nested 175 | 176 | func testMapJSON_nested_succeeds() { 177 | let json: [String: Any] = ["user": ["name": "devxoul"]] 178 | XCTAssertEqual( 179 | try Observable.just(json) 180 | .mapJSON("user") 181 | .mapJSON("name", String.self) 182 | .toBlocking() 183 | .first()!, 184 | "devxoul" 185 | ) 186 | XCTAssertEqual( 187 | try Single.just(json) 188 | .mapJSON("user") 189 | .mapJSON("name", String.self) 190 | .toBlocking() 191 | .first()!, 192 | "devxoul" 193 | ) 194 | XCTAssertEqual( 195 | try Maybe.just(json) 196 | .mapJSON("user") 197 | .mapJSON("name", String.self) 198 | .toBlocking() 199 | .first()!, 200 | "devxoul" 201 | ) 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "Tests/" 3 | 4 | coverage: 5 | status: 6 | project: no 7 | patch: no 8 | changes: no 9 | --------------------------------------------------------------------------------