├── .swift-version
├── Tests
├── JSONExamples
│ ├── NoJsonObject.json
│ ├── MissingKey.json
│ ├── TypeMismatch.json
│ ├── Repository.json
│ └── Vehicle.json
├── NSNullTests.swift
├── Info.plist
├── Vehicle.swift
├── MissingKeyOperatorTests.swift
├── DynamicDecodableTests.swift
├── DecodeAsOneOfTests.swift
├── RawRepresentableDecodableTests.swift
├── ParseTests.swift
├── Repository.swift
├── NSValueDecodableTests.swift
├── DictionaryTests.swift
├── KeyPathTests.swift
├── ErrorPathTests.swift
├── DecodableExtensionTests.swift
├── ArrayTests.swift
├── DecodableTests.swift
└── DecodableOperatorsTests.swift
├── Sources
├── Playground.playground
│ ├── Contents.swift
│ └── contents.xcplayground
├── Decodable.h
├── Info.plist
├── RawRepresentableDecodable.swift
├── KeyPath.swift
├── Operators.swift
├── NSValueCastable.swift
├── Decodable.swift
├── Decoders.swift
├── OptionalKeyPath.swift
├── Parse.swift
├── Castable.swift
└── DecodingError.swift
├── Package.swift
├── Decodable.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── WorkspaceSettings.xcsettings
├── xcshareddata
│ ├── xcbaselines
│ │ ├── 8FE7B56B1B4C9FB900837609.xcbaseline
│ │ │ ├── 73DA0DF5-ACA7-4948-824F-2F1739DC2034.plist
│ │ │ ├── 2B184EB9-20A6-44F1-BFA7-F9185332D574.plist
│ │ │ └── Info.plist
│ │ └── 17FB80FF1B530FED0012F106.xcbaseline
│ │ │ ├── 78C19493-4A8B-4FE0-88D6-957F92628060.plist
│ │ │ └── Info.plist
│ └── xcschemes
│ │ ├── Decodable-tvOS.xcscheme
│ │ ├── Decodable-watchOS.xcscheme
│ │ ├── Decodable-Mac.xcscheme
│ │ └── Decodable-iOS.xcscheme
└── project.pbxproj
├── Generator
├── Templates
│ ├── Header.swift
│ └── Documentation.swift
└── Generator.swift
├── .travis.yml
├── Decodable.podspec
├── .gitignore
├── LICENSE
└── README.md
/.swift-version:
--------------------------------------------------------------------------------
1 | 4.0
2 |
--------------------------------------------------------------------------------
/Tests/JSONExamples/NoJsonObject.json:
--------------------------------------------------------------------------------
1 | "id"
--------------------------------------------------------------------------------
/Sources/Playground.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | //: Playground - noun: a place where people can play
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | import PackageDescription
2 |
3 | let package = Package(
4 | name: "Decodable",
5 | exclude: [ "Tests" ]
6 | )
7 |
--------------------------------------------------------------------------------
/Sources/Playground.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Generator/Templates/Header.swift:
--------------------------------------------------------------------------------
1 | //
2 | // {filename}
3 | // Decodable
4 | //
5 | // Generated automatically by {by} as a build phase.
6 | // Copyright © 2016 anviking. All rights reserved.
7 | //
8 |
9 | // {count} overloads were generated with the following return types:
10 | // {overloads}
11 |
--------------------------------------------------------------------------------
/Generator/Templates/Documentation.swift:
--------------------------------------------------------------------------------
1 | /// Retrieves the object at `path` from `json` and decodes it according to the return type
2 | ///
3 | /// - parameter json: An object from NSJSONSerialization, preferably a `NSDictionary`.
4 | /// - parameter path: {path}
5 | /// - throws: {throws}
6 | /// - returns: {returns}
7 | ///
8 |
--------------------------------------------------------------------------------
/Tests/JSONExamples/MissingKey.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Decodable",
3 | "description": "Decodable",
4 | "html_url": "https://github.com/Anviking/Decodable",
5 | "owner": {
6 | "id": 23,
7 | "login": "fjbelchi"
8 | },
9 | "coverage": 90.9,
10 | "files": ["file1", "file2", "file3"],
11 | "optional": null
12 | }
--------------------------------------------------------------------------------
/Tests/JSONExamples/TypeMismatch.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "23",
3 | "name": "Decodable",
4 | "description": "Decodable",
5 | "html_url": "https://github.com/Anviking/Decodable",
6 | "owner": {
7 | "id": 23,
8 | "login": "fjbelchi"
9 | },
10 | "coverage": 90.9,
11 | "files": ["file1", "file2", "file3"],
12 | "optional": null
13 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | xcode_project: Decodable.xcodeproj # path to your xcodeproj folder
3 | osx_image: xcode9
4 | script:
5 | - xcodebuild build -project Decodable.xcodeproj/ -scheme 'Decodable-iOS' -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO -destination 'platform=iOS Simulator,name=iPhone 6,OS=10.0'
6 | - xcodebuild test -project Decodable.xcodeproj/ -scheme 'Decodable-Mac' ONLY_ACTIVE_ARCH=NO
7 |
8 |
--------------------------------------------------------------------------------
/Tests/JSONExamples/Repository.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": 23,
3 | "name": "Decodable",
4 | "description": "Decodable",
5 | "html_url": "https://github.com/Anviking/Decodable",
6 | "owner": {
7 | "id": 23,
8 | "login": "fjbelchi"
9 | },
10 | "coverage": 90.9,
11 | "files": ["file1", "file2", "file3"],
12 | "optional": null,
13 | "active": true,
14 | "optionalActive": null
15 | }
--------------------------------------------------------------------------------
/Tests/JSONExamples/Vehicle.json:
--------------------------------------------------------------------------------
1 | {
2 | "vehicles": [
3 | {
4 | "type":"train",
5 | "driverless":true,
6 | "electric":true
7 | },
8 | {
9 | "type":"car",
10 | "driverless":false
11 | },
12 | {
13 | "type":"truck",
14 | "driverless":false,
15 | "wheels":18
16 | },
17 | {
18 | "type":"truck",
19 | "driverless":true,
20 | "wheels":18
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/Tests/NSNullTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSNullTests.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2016-12-28.
6 | // Copyright © 2016 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Decodable
11 |
12 | class NSNullTests: XCTestCase {
13 |
14 | // https://github.com/Anviking/Decodable/issues/135
15 | func testNullToAny() {
16 | let json = NSDictionary(dictionary: ["tone": NSNull()])
17 | let maybeTone: Any? = try! json =>? "tone"
18 | XCTAssertNil(maybeTone)
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Decodable.h:
--------------------------------------------------------------------------------
1 | //
2 | // Decodable.h
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2015-07-08.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Decodable.
12 | FOUNDATION_EXPORT double DecodableVersionNumber;
13 |
14 | //! Project version string for Decodable.
15 | FOUNDATION_EXPORT const unsigned char DecodableVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/xcshareddata/xcbaselines/8FE7B56B1B4C9FB900837609.xcbaseline/73DA0DF5-ACA7-4948-824F-2F1739DC2034.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | classNames
6 |
7 | DecodableTests
8 |
9 | testDecodeArrayOfRepositoriesSuccess()
10 |
11 | com.apple.XCTPerformanceMetric_WallClockTime
12 |
13 | baselineAverage
14 | 0.5
15 | baselineIntegrationDisplayName
16 | Local Baseline
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/xcshareddata/xcbaselines/8FE7B56B1B4C9FB900837609.xcbaseline/2B184EB9-20A6-44F1-BFA7-F9185332D574.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | classNames
6 |
7 | DecodableTests
8 |
9 | testDecodeArrayOfRepositoriesAndMeasureTime()
10 |
11 | com.apple.XCTPerformanceMetric_WallClockTime
12 |
13 | baselineAverage
14 | 0.10205
15 | baselineIntegrationDisplayName
16 | Local Baseline
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Decodable.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "Decodable"
3 | s.version = "0.6.0"
4 | s.summary = "Swift JSON parsing done (more) right"
5 | s.description = "Simple yet powerful object mapping made possible by Swift 2's error handling. Greatly inspired by Argo, but without any functional programming and bizillion operators."
6 | s.homepage = "https://github.com/Anviking/Decodable"
7 | s.license = 'MIT'
8 | s.author = { "Anviking" => "anviking@me.com" }
9 | s.source = { :git => "https://github.com/Anviking/Decodable.git", :tag => "#{s.version}" }
10 | s.ios.deployment_target = '8.0'
11 | s.osx.deployment_target = '10.9'
12 | s.tvos.deployment_target = '9.0'
13 | s.watchos.deployment_target = '2.0'
14 | s.requires_arc = true
15 | s.source_files = 'Sources/*.{swift,h}'
16 | end
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | # Pods/
27 |
28 | # Carthage
29 | #
30 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
31 | # Carthage/Checkouts
32 |
33 | Carthage/Build
34 |
35 | *.xcscmblueprint
36 |
37 | # Swift Package Manager
38 |
39 | .build/
40 |
41 | # General OS X
42 | .DS_Store
43 |
--------------------------------------------------------------------------------
/Sources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/xcshareddata/xcbaselines/17FB80FF1B530FED0012F106.xcbaseline/78C19493-4A8B-4FE0-88D6-957F92628060.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | classNames
6 |
7 | DecodableTests
8 |
9 | testCustomParseAndMeasureTime()
10 |
11 | com.apple.XCTPerformanceMetric_WallClockTime
12 |
13 | baselineAverage
14 | 0.01
15 | baselineIntegrationDisplayName
16 | Local Baseline
17 |
18 |
19 | testDecodeArrayOfRepositoriesAndMeasureTime()
20 |
21 | com.apple.XCTPerformanceMetric_WallClockTime
22 |
23 | baselineAverage
24 | 0.23
25 | baselineIntegrationDisplayName
26 | Local Baseline
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Sources/RawRepresentableDecodable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RawRepresentableDecodable.swift
3 | // Decodable
4 | //
5 | // Created by Daniel Garbień on 06/11/15.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | /**
10 | * Extends all RawRepresentables (enums) which are also Decodable with decode implementation.
11 | *
12 | * I could not find a way to implicitly declare RawRepresentable conforming to Decodable, what would make all enums Decodable automatically.
13 | * Because of that for an enum to be compatible with Decodable operators it must be declared as implementing Decodable protocol.
14 | */
15 | public extension RawRepresentable where RawValue: Decodable, Self: Decodable {
16 |
17 | static func decode(_ json: Any) throws -> Self {
18 | let rawValue = try RawValue.decode(json)
19 | guard let rawRepresentable = Self(rawValue: rawValue) else {
20 | let metadata = DecodingError.Metadata(object: json)
21 | throw DecodingError.rawRepresentableInitializationError(rawValue: rawValue, metadata)
22 | }
23 | return rawRepresentable
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/xcshareddata/xcbaselines/17FB80FF1B530FED0012F106.xcbaseline/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | runDestinationsByUUID
6 |
7 | 78C19493-4A8B-4FE0-88D6-957F92628060
8 |
9 | localComputer
10 |
11 | busSpeedInMHz
12 | 100
13 | cpuCount
14 | 1
15 | cpuKind
16 | Intel Core i7
17 | cpuSpeedInMHz
18 | 2300
19 | logicalCPUCoresPerPackage
20 | 8
21 | modelCode
22 | MacBookPro10,1
23 | physicalCPUCoresPerPackage
24 | 4
25 | platformIdentifier
26 | com.apple.platform.macosx
27 |
28 | targetArchitecture
29 | x86_64
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Johannes Lund
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 |
23 |
--------------------------------------------------------------------------------
/Tests/Vehicle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Vehicle.swift
3 | // Decodable
4 | //
5 | // Created by Charlotte Tortorella on 12/04/2016.
6 | // Copyright © 2016 anviking. All rights reserved.
7 | //
8 |
9 | import protocol Decodable.Decodable
10 | import enum Decodable.DecodingError
11 | @testable import Decodable
12 |
13 | protocol Vehicle {
14 | var driverless: Bool {get}
15 | }
16 |
17 | struct Car: Vehicle {
18 | let driverless: Bool
19 | }
20 |
21 | extension Car: Decodable {
22 | static func decode(_ json: Any) throws -> Car {
23 | return try Car(driverless: json => "driverless")
24 | }
25 | }
26 |
27 | struct Train: Vehicle {
28 | let driverless: Bool
29 | let electric: Bool
30 | }
31 |
32 | extension Train: Decodable {
33 | static func decode(_ json: Any) throws -> Train {
34 | return try Train(driverless: json => "driverless",
35 | electric: json => "electric")
36 | }
37 | }
38 |
39 | struct Truck: Vehicle {
40 | let driverless: Bool
41 | let wheels: UInt8
42 | }
43 |
44 | extension Truck: Decodable {
45 | static func decode(_ json: Any) throws -> Truck {
46 | return try Truck(driverless: json => "driverless",
47 | wheels: json => "wheels")
48 | }
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/Sources/KeyPath.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeyPath.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2016-07-09.
6 | // Copyright © 2016 anviking. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// `KeyPath` represents the path to a specific node in a tree of nested dictionaries.
12 | ///
13 | /// Can be created from string and array literals and can be joined by the `=>` operator.
14 | /// ```
15 | /// let a: KeyPath = "a"
16 | /// let b: KeyPath = ["a", "b"]
17 | /// let c: KeyPath = "a" => "b" => "c"
18 | /// ```
19 |
20 | public struct KeyPath {
21 | public var keys: [String]
22 |
23 | public init(_ keys: [String]) {
24 | self.keys = keys
25 | }
26 |
27 | public init(_ key: String) {
28 | self.keys = [key]
29 | }
30 |
31 | }
32 |
33 | extension KeyPath: ExpressibleByStringLiteral {
34 | public init(stringLiteral value: String) {
35 | self.keys = [value]
36 | }
37 |
38 | public init(extendedGraphemeClusterLiteral value: String) {
39 | self.keys = [value]
40 | }
41 |
42 | public init(unicodeScalarLiteral value: String) {
43 | self.keys = [value]
44 | }
45 | }
46 |
47 | extension KeyPath: ExpressibleByArrayLiteral {
48 | public init(arrayLiteral elements: String...) {
49 | self.keys = elements
50 | }
51 | }
52 |
53 | extension KeyPath: Equatable { }
54 | public func ==(lhs: KeyPath, rhs: KeyPath) -> Bool {
55 | return lhs.keys == rhs.keys
56 | }
57 |
--------------------------------------------------------------------------------
/Tests/MissingKeyOperatorTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // missingKeyOperatorTests.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2015-12-20.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import protocol Decodable.Decodable
11 | import enum Decodable.DecodingError
12 | import Decodable
13 |
14 | class missingKeyOperatorTests: XCTestCase {
15 |
16 | func testMissingKey() {
17 | // Should return nil
18 | let dictionary: NSDictionary = ["key": 3]
19 | let result: Int? = try! dictionary =>? "missingKeyError"
20 | XCTAssertEqual(result, nil)
21 | }
22 |
23 | func testNSNull() {
24 | // Should return nil
25 | let dictionary: NSDictionary = ["key": NSNull()]
26 | let result: Int? = try! dictionary =>? "key"
27 | XCTAssertEqual(result, nil)
28 | }
29 |
30 | func testSuccess() {
31 | let dictionary: NSDictionary = ["key": 3]
32 | let result: Int? = try! dictionary =>? "key"
33 | XCTAssertEqual(result, 3)
34 | }
35 |
36 | func testTypeMismatch() {
37 | // Should throw
38 | let dictionary: NSDictionary = ["key": "3"]
39 | do {
40 | let _: Int? = try dictionary =>? "key"
41 | XCTFail("should throw")
42 | } catch let DecodingError.typeMismatch(expected, _, _) {
43 | XCTAssert(expected == Int.self, "\(expected) != Int.self")
44 | } catch {
45 | XCTFail("Should not throw \(error)")
46 | }
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/Sources/Operators.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Operators.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2015-07-08.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - Operators
12 |
13 | precedencegroup DecodingPrecedence {
14 | associativity: right
15 | higherThan: CastingPrecedence
16 | }
17 |
18 | infix operator => : DecodingPrecedence
19 | infix operator =>? : DecodingPrecedence
20 |
21 | public func => (lhs: Any, rhs: KeyPath) throws -> Any {
22 | return try parse(lhs, keyPath: rhs, decoder: { $0 })
23 | }
24 |
25 |
26 | public func =>? (lhs: Any, rhs: OptionalKeyPath) throws -> Any? {
27 | return try parse(lhs, keyPath: rhs, decoder: Optional.decoder({$0}))
28 | }
29 |
30 | // MARK: - JSONPath
31 |
32 | /// Enables parsing nested objects e.g json => "a" => "b"
33 |
34 | public func => (lhs: KeyPath, rhs: KeyPath) -> KeyPath {
35 | return KeyPath(lhs.keys + rhs.keys)
36 | }
37 |
38 | public func => (lhs: OptionalKeyPath, rhs: OptionalKeyPath) -> OptionalKeyPath {
39 | return OptionalKeyPath(keys: lhs.keys + rhs.markingFirst(required: true).keys)
40 | }
41 |
42 | public func =>? (lhs: OptionalKeyPath, rhs: OptionalKeyPath) -> OptionalKeyPath {
43 | return OptionalKeyPath(keys: lhs.keys + rhs.keys)
44 | }
45 |
46 | public func => (lhs: OptionalKeyPath, rhs: KeyPath) -> OptionalKeyPath {
47 | return OptionalKeyPath(keys: lhs.keys + rhs.keys.map { OptionalKey(key: $0, isRequired: true) })
48 | }
49 |
50 |
51 | public func =>? (lhs: KeyPath, rhs: OptionalKeyPath) -> OptionalKeyPath {
52 | return OptionalKeyPath(keys: lhs.keys.map { OptionalKey(key: $0, isRequired: true) } + rhs.keys )
53 | }
54 |
--------------------------------------------------------------------------------
/Tests/DynamicDecodableTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DynamicDecodableTests.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2016-07-29.
6 | // Copyright © 2016 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import Foundation
11 | @testable import Decodable
12 |
13 | class DynamicDecodableTests: XCTestCase {
14 |
15 | private var originalBoolDecoder = Bool.decoder
16 | private var originalNSArrayDecoder = NSArray.decoder
17 |
18 | func testCustomBooleanDecoding() {
19 | Bool.decoder = { json in
20 | switch json {
21 | case let str as String where str == "true":
22 | return true
23 | case let str as String where str == "false":
24 | return false
25 | default:
26 | return try cast(json)
27 | }
28 | }
29 | let json: NSString = "true"
30 | let result = try! Bool.decode(json)
31 | XCTAssertEqual(result, true)
32 |
33 | Bool.decoder = originalBoolDecoder
34 |
35 | XCTAssertNil(try? Bool.decode(json))
36 | }
37 |
38 | func testCustomArrayDecoding() {
39 | NSArray.decoder = { json in
40 | switch json {
41 | case let array as NSArray:
42 | return array
43 | default:
44 | return [json]
45 | }
46 | }
47 |
48 | let arrayJSON: NSArray = ["a", "b", "c"]
49 | let objectJSON: NSString = "d"
50 |
51 | XCTAssertEqual(try! [String].decode(arrayJSON), ["a", "b", "c"])
52 | XCTAssertEqual(try! [String].decode(objectJSON), ["d"])
53 |
54 | NSArray.decoder = originalNSArrayDecoder
55 |
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Tests/DecodeAsOneOfTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DecodableTests.swift
3 | // DecodableTests
4 | //
5 | // Created by Johannes Lund on 2015-07-08.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import protocol Decodable.Decodable
11 | import enum Decodable.DecodingError
12 | import struct Decodable.KeyPath
13 | @testable import Decodable
14 |
15 | class DecodeAsOneOfTests: XCTestCase {
16 |
17 | private func readJsonFile(_ file: String) -> NSDictionary {
18 | let filePath = (Bundle(for: object_getClass(self)!).resourcePath! as NSString).appendingPathComponent(file)
19 | print(filePath)
20 | let jsonString = try! String(contentsOfFile: filePath)
21 | let jsonData = jsonString.data(using: String.Encoding.utf8)!
22 | return try! JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
23 | }
24 |
25 | func testDecodingSubtypesShouldSucceed() {
26 | // given
27 | let json = readJsonFile("Vehicle.json")
28 |
29 | // when
30 | do {
31 |
32 | let vehiclesRaw: Any = try json => "vehicles"
33 |
34 | let vehicles1 = try decodeArrayAsOneOf(vehiclesRaw, objectTypes: Train.self, Truck.self, Car.self)
35 |
36 | guard let vehiclesArray = vehiclesRaw as? [Any] else {
37 | let metadata = DecodingError.Metadata(object: vehiclesRaw)
38 | throw DecodingError.typeMismatch(expected: NSArray.self, actual: Mirror(reflecting: vehiclesRaw).subjectType, metadata)
39 | }
40 |
41 | let vehicles2 = try vehiclesArray.map { try decodeAsOneOf($0, objectTypes: Train.self, Truck.self, Car.self) }
42 |
43 | XCTAssertEqual(vehicles1.count, vehicles2.count)
44 | XCTAssertEqual(vehicles1.count, vehiclesArray.count)
45 | for truck in vehicles1.flatMap({ $0 as? Truck }) {
46 | XCTAssertEqual(truck.wheels, 18)
47 | }
48 |
49 | let train = vehicles1.flatMap { $0 as? Train }.first!
50 | XCTAssertEqual(train.electric, true)
51 |
52 | } catch {
53 | XCTFail("it should not throw an exception")
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Tests/RawRepresentableDecodableTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RawRepresentableDecodableTests.swift
3 | // Decodable
4 | //
5 | // Created by Daniel Garbień on 06/11/15.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import protocol Decodable.Decodable
11 | import enum Decodable.DecodingError
12 | @testable import Decodable
13 |
14 | enum CMYKColor: String, Decodable {
15 | case Cyan = "Cyan"
16 | case Magenta = "Magenta"
17 | case Yellow = "Yellow"
18 | case Black = "Black"
19 | }
20 |
21 | class RawRepresentableDecodableTests: XCTestCase {
22 |
23 | func testDecodingCorrectRawRepresentableValueSucceed() {
24 | // given
25 | let key = "color"
26 | let color = "Cyan"
27 | let json: NSDictionary = [key: color]
28 | // when
29 | let cmykColor: CMYKColor = try! json => KeyPath(key)
30 | // then
31 | XCTAssertEqual(cmykColor, CMYKColor.Cyan)
32 | }
33 |
34 | func testDecodingIncorrectRawRepresentableValueFail() {
35 | // given
36 | let key = "color"
37 | let color = "Green"
38 | let json: NSDictionary = [key: color]
39 | // when
40 | do {
41 | _ = try json => KeyPath(key) as CMYKColor
42 | XCTFail()
43 | } catch DecodingError.rawRepresentableInitializationError(_, let metadata) {
44 | // then
45 | XCTAssertNotNil(metadata.object)
46 | } catch {
47 | XCTFail("should not throw \(error)")
48 | }
49 | }
50 |
51 | func testDecodingIncorrectRawRepresentableTypeFail() {
52 | // given
53 | let key = "color"
54 | let color = 0
55 | let json: NSDictionary = [key: color]
56 | // when
57 | do {
58 | _ = try json => KeyPath(key) as CMYKColor
59 | XCTFail()
60 | } catch let DecodingError.typeMismatch(expected, _, metadata) where expected == CMYKColor.RawValue.self {
61 | // then
62 | XCTAssertNotNil(metadata.object)
63 | } catch {
64 | XCTFail("should not throw \(error)")
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Tests/ParseTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ParseTests.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2016-07-13.
6 | // Copyright © 2016 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import Foundation
11 | import protocol Decodable.Decodable
12 | import enum Decodable.DecodingError
13 | import struct Decodable.KeyPath
14 | @testable import Decodable
15 |
16 | class ParseTests: XCTestCase {
17 |
18 | func testParseKeyPathSuccess() {
19 | let dict: NSDictionary = ["a": ["b": 3]]
20 | let a = try! parse(dict, ["a", "b"] as KeyPath)
21 | XCTAssertEqual(a as? Int, 3)
22 | }
23 |
24 | func testParseAndDecodeKeyPathSuccess() {
25 | let dict: NSDictionary = ["a": ["b": 3]]
26 | let a = try! parse(dict, keyPath: ["a", "b"], decoder: Int.decode)
27 | XCTAssertEqual(a, 3)
28 | }
29 |
30 | func testParseKeyPathMissingKey() {
31 | let dict: NSDictionary = ["a": ["b": 3]]
32 | do { _ = try parse(dict, KeyPath(["a", "c"])) }
33 | catch DecodingError.missingKey(let key, let metadata) {
34 | XCTAssertEqual(metadata.formattedPath, "a")
35 | XCTAssertEqual(key, "c")
36 | } catch {
37 | XCTFail("should not throw \(error)")
38 | }
39 | }
40 |
41 | func testParseAndDecodeKeyPathMissingKey() {
42 | let dict: NSDictionary = ["a": ["b": 3]]
43 | do { _ = try parse(dict, keyPath: ["a", "c"], decoder: Int.decode) }
44 | catch DecodingError.missingKey(let key, let metadata) {
45 | XCTAssertEqual(metadata.formattedPath, "a")
46 | XCTAssertEqual(key, "c")
47 | } catch {
48 | XCTFail("should not throw \(error)")
49 | }
50 | }
51 |
52 | func testParseAndDecodeKeyPathTypeMismatch() {
53 | let dict: NSDictionary = ["a": ["b": "3"]]
54 | do { _ = try parse(dict, keyPath: ["a", "b"], decoder: Int.decode) }
55 | catch DecodingError.typeMismatch(let expected, _, let metadata) {
56 | XCTAssertEqual("\(expected)", "Int")
57 | XCTAssertEqual(metadata.formattedPath, "a.b")
58 | } catch {
59 | XCTFail("should not throw \(error)")
60 | }
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/Sources/NSValueCastable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSValueCastable.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2016-01-06.
6 | // Copyright © 2016 anviking. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Int64: NSNumberCastable {
12 | public static func convertFrom(_ n: NSNumber) -> Int64 { return n.int64Value }
13 | }
14 | extension Int32: NSNumberCastable {
15 | public static func convertFrom(_ n: NSNumber) -> Int32 { return n.int32Value }
16 | }
17 | extension Int16: NSNumberCastable {
18 | public static func convertFrom(_ n: NSNumber) -> Int16 { return n.int16Value }
19 | }
20 | extension Int8: NSNumberCastable {
21 | public static func convertFrom(_ n: NSNumber) -> Int8 { return n.int8Value }
22 | }
23 | extension UInt64: NSNumberCastable {
24 | public static func convertFrom(_ n: NSNumber) -> UInt64 { return n.uint64Value }
25 | }
26 | extension UInt32: NSNumberCastable {
27 | public static func convertFrom(_ n: NSNumber) -> UInt32 { return n.uint32Value }
28 | }
29 | extension UInt16: NSNumberCastable {
30 | public static func convertFrom(_ n: NSNumber) -> UInt16 { return n.uint16Value }
31 | }
32 | extension UInt8: NSNumberCastable {
33 | public static func convertFrom(_ n: NSNumber) -> UInt8 { return n.uint8Value }
34 | }
35 |
36 | /// Provides a default implementation of decode() which casts the object to a NSValue and unsafely casts its value as Self.
37 | public protocol NSValueCastable: Decodable {}
38 |
39 | /// Used to enable decoding to different IntegerTypes from NSNumber.
40 | public protocol NSNumberCastable: NSValueCastable {
41 | static func convertFrom(_ n: NSNumber) -> Self
42 | }
43 |
44 | extension NSValueCastable {
45 | private typealias PointerOfSelf = UnsafeMutablePointer // Why do we have to do this?
46 | public static func decode(_ j: Any) throws -> Self {
47 | let value: NSValue = try cast(j)
48 | let pointer = PointerOfSelf.allocate(capacity: 1)
49 | defer { pointer.deallocate(capacity: 1) }
50 | value.getValue(pointer)
51 | return pointer.move()
52 | }
53 | }
54 |
55 | extension NSNumberCastable {
56 | public static func decode(_ json: Any) throws -> Self {
57 | return try convertFrom(cast(json))
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/xcshareddata/xcbaselines/8FE7B56B1B4C9FB900837609.xcbaseline/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | runDestinationsByUUID
6 |
7 | 2B184EB9-20A6-44F1-BFA7-F9185332D574
8 |
9 | localComputer
10 |
11 | busSpeedInMHz
12 | 100
13 | cpuCount
14 | 1
15 | cpuKind
16 | Intel Core i7
17 | cpuSpeedInMHz
18 | 2300
19 | logicalCPUCoresPerPackage
20 | 8
21 | modelCode
22 | MacBookPro10,1
23 | physicalCPUCoresPerPackage
24 | 4
25 | platformIdentifier
26 | com.apple.platform.macosx
27 |
28 | targetArchitecture
29 | x86_64
30 | targetDevice
31 |
32 | modelCode
33 | iPhone8,1
34 | platformIdentifier
35 | com.apple.platform.iphonesimulator
36 |
37 |
38 | 73DA0DF5-ACA7-4948-824F-2F1739DC2034
39 |
40 | localComputer
41 |
42 | busSpeedInMHz
43 | 100
44 | cpuCount
45 | 1
46 | cpuKind
47 | Intel Core i7
48 | cpuSpeedInMHz
49 | 2300
50 | logicalCPUCoresPerPackage
51 | 8
52 | modelCode
53 | MacBookPro10,1
54 | physicalCPUCoresPerPackage
55 | 4
56 | platformIdentifier
57 | com.apple.platform.macosx
58 |
59 | targetArchitecture
60 | x86_64
61 | targetDevice
62 |
63 | modelCode
64 | iPhone7,2
65 | platformIdentifier
66 | com.apple.platform.iphonesimulator
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/Sources/Decodable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Decodable.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2015-07-07.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public protocol Decodable {
12 | static func decode(_ json: Any) throws -> Self
13 | }
14 |
15 |
16 | extension Dictionary where Key: Decodable, Value: Decodable {
17 | public static func decode(_ j: Any) throws -> Dictionary {
18 | return try Dictionary.decoder(key: Key.decode, value: Value.decode)(j)
19 | }
20 | }
21 |
22 | /* FIXME: this causes ambiguity issues, in the meantime resort to `Dictionary.decoder`
23 | https://github.com/Anviking/Decodable/issues/120
24 |
25 | extension Dictionary where Key: Decodable, Value: Any {
26 |
27 | public static func decode(_ j: Any) throws -> Dictionary {
28 | let valueDecoder: (Any) throws -> Value = { try cast($0) }
29 | return try Dictionary.decoder(key: Key.decode, value: valueDecoder)(j)
30 | }
31 | }
32 | */
33 |
34 | extension Array where Element: Decodable {
35 | public static func decode(_ j: Any, ignoreInvalidObjects: Bool = false) throws -> [Element] {
36 | if ignoreInvalidObjects {
37 | return try [Element?].decoder { try? Element.decode($0) }(j).flatMap {$0}
38 | } else {
39 | return try Array.decoder(Element.decode)(j)
40 | }
41 | }
42 | }
43 |
44 |
45 |
46 |
47 | // MARK: Helpers
48 |
49 | /// Attempt to decode one of multiple objects in order until: A: we get a positive match, B: we throw an exception if the last object does not decode
50 | public func decodeAsOneOf(_ json: Any, objectTypes: Decodable.Type...) throws -> Decodable {
51 | for decodable in objectTypes.dropLast() {
52 | if let decoded = try? decodable.decode(json) {
53 | return decoded
54 | }
55 | }
56 | return try objectTypes.last!.decode(json)
57 | }
58 |
59 | /// Attempt to decode one of multiple objects in order until: A: we get a positive match, B: we throw an exception if the last object does not decode
60 | public func decodeArrayAsOneOf(_ json: Any, objectTypes: Decodable.Type...) throws -> [Decodable] {
61 | return try NSArray.decode(json).map {
62 | for decodable in objectTypes.dropLast() {
63 | if let decoded = try? decodable.decode($0) {
64 | return decoded
65 | }
66 | }
67 | return try objectTypes.last!.decode($0)
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Tests/Repository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RepositoryExample.swift
3 | // Decodable
4 | //
5 | // Created by Fran_DEV on 13/07/15.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import protocol Decodable.Decodable
11 | import enum Decodable.DecodingError
12 | import struct Decodable.KeyPath
13 | @testable import Decodable
14 |
15 | struct Owner {
16 | let id: Int
17 | let login: String
18 | }
19 |
20 | struct Repository {
21 | let id: Int
22 | let name: String
23 | let description: String
24 | let htmlUrlString : String
25 | let owner: Owner // Struct conforming to Decodable
26 | let coverage: Double
27 | let files: Array
28 | let optional: String?
29 | let active: Bool
30 | let optionalActive: Bool?
31 | }
32 |
33 | extension Owner : Decodable {
34 | static func decode(_ j: Any) throws -> Owner {
35 | return try Owner(
36 | id: j => "id",
37 | login: j => "login"
38 | )
39 | }
40 | }
41 |
42 | extension Repository : Decodable {
43 | static func decode(_ j: Any) throws -> Repository {
44 | return try Repository(
45 | id: j => "id",
46 | name: j => "name",
47 | description: j => "description",
48 | htmlUrlString : j => "html_url",
49 | owner: j => "owner",
50 | coverage: j => "coverage",
51 | files: j => "files",
52 | optional: j => "optional",
53 | active: j => "active",
54 | optionalActive: j => "optionalActive"
55 | )
56 | }
57 | }
58 |
59 | // MARK: Equatable
60 |
61 | func == (lhs: Owner, rhs: Owner) -> Bool {
62 | return lhs.id == rhs.id && lhs.login == rhs.login
63 | }
64 |
65 | extension Owner: Equatable {
66 | var hashValue: Int { return id.hashValue }
67 | }
68 |
69 | func == (lhs: Repository, rhs: Repository) -> Bool {
70 | return lhs.id == rhs.id &&
71 | lhs.name == rhs.name &&
72 | lhs.description == rhs.description &&
73 | lhs.htmlUrlString == rhs.htmlUrlString &&
74 | lhs.owner == rhs.owner &&
75 | lhs.coverage == rhs.coverage &&
76 | lhs.files == rhs.files &&
77 | lhs.optional == rhs.optional &&
78 | lhs.active == rhs.active &&
79 | lhs.optionalActive == rhs.optionalActive
80 | }
81 |
82 | extension Repository: Equatable {
83 | var hashValue: Int { return id.hashValue }
84 | }
85 |
--------------------------------------------------------------------------------
/Tests/NSValueDecodableTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSValueDecodableTests.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2016-01-06.
6 | // Copyright © 2016 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import Decodable
11 |
12 | class NSValueDecodableTests: XCTestCase {
13 | func testIntegerDecodingFromInt64() {
14 | let number = NSNumber(value: 100)
15 | XCTAssertEqual(try! Int64.decode(number), number.int64Value)
16 | XCTAssertEqual(try! Int32.decode(number), number.int32Value)
17 | XCTAssertEqual(try! Int16.decode(number), number.int16Value)
18 | XCTAssertEqual(try! Int8.decode(number), number.int8Value)
19 |
20 | XCTAssertEqual(try! UInt64.decode(number), number.uint64Value)
21 | XCTAssertEqual(try! UInt32.decode(number), number.uint32Value)
22 | XCTAssertEqual(try! UInt16.decode(number), number.uint16Value)
23 | XCTAssertEqual(try! UInt8.decode(number), number.uint8Value)
24 | }
25 |
26 | func testIntegerDecodingFromInt32() {
27 | let number = NSNumber(value: 100)
28 | XCTAssertEqual(try! Int64.decode(number), number.int64Value)
29 | XCTAssertEqual(try! Int32.decode(number), number.int32Value)
30 | XCTAssertEqual(try! Int16.decode(number), number.int16Value)
31 | XCTAssertEqual(try! Int8.decode(number), number.int8Value)
32 |
33 | XCTAssertEqual(try! UInt64.decode(number), number.uint64Value)
34 | XCTAssertEqual(try! UInt32.decode(number), number.uint32Value)
35 | XCTAssertEqual(try! UInt16.decode(number), number.uint16Value)
36 | XCTAssertEqual(try! UInt8.decode(number), number.uint8Value)
37 | }
38 |
39 | func testIntegerDecodingFromInt8() {
40 | let number = NSNumber(value: 100)
41 |
42 | XCTAssertEqual(try! Int64.decode(number), number.int64Value)
43 | XCTAssertEqual(try! Int32.decode(number), number.int32Value)
44 | XCTAssertEqual(try! Int16.decode(number), number.int16Value)
45 | XCTAssertEqual(try! Int8.decode(number), number.int8Value)
46 |
47 | XCTAssertEqual(try! UInt64.decode(number), number.uint64Value)
48 | XCTAssertEqual(try! UInt32.decode(number), number.uint32Value)
49 | XCTAssertEqual(try! UInt16.decode(number), number.uint16Value)
50 | XCTAssertEqual(try! UInt8.decode(number), number.uint8Value)
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Sources/Decoders.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Closure.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2016-07-10.
6 | // Copyright © 2016 anviking. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Optional {
12 |
13 | /// Creates an optional decoder from a decoder of the Wrapped type
14 | ///
15 | /// This function is used by `=>` and `=>?` overloads when decoding `T?`
16 | ///
17 | /// - parameter wrappedDecoder: A decoder (decode closure) for the wrapped type
18 | /// - returns: A closure takes an JSON object, checks it's `NSNull`, if so returns `nil`, otherwise calls the wrapped decode closure.
19 | static func decoder(_ wrappedDecoder: @escaping (Any) throws -> Wrapped) -> (Any) throws -> Wrapped? {
20 | return { json in
21 | if json is NSNull {
22 | return nil
23 | } else {
24 | return try wrappedDecoder(json)
25 | }
26 | }
27 | }
28 | }
29 |
30 | extension Array {
31 |
32 | /// Creates an array decoder from an element decoder
33 | ///
34 | /// This function is used by `=>` and `=>?` overloads when decoding `[T]`
35 | ///
36 | /// - parameter elementDecoder: A decoder (decode closure) for the `Element` type
37 | /// - throws: if `NSArray.decode` throws or any element decode closure throws
38 | /// - returns: A closure that takes an `NSArray` and maps it using the element decode closure
39 | public static func decoder(_ elementDecoder: @escaping (Any) throws -> Element) -> (Any) throws -> Array {
40 | return { json in
41 | return try NSArray.decode(json).map { try elementDecoder($0) }
42 | }
43 | }
44 | }
45 |
46 | extension Dictionary {
47 | /// Create a dictionary decoder from key- and value- decoders
48 | ///
49 | /// This function is used by `=>` and `=>?` overloads when decoding `[K: V]`
50 | ///
51 | /// - parameter key: A decoder (decode closure) for the `Key` type
52 | /// - parameter value: A decoder (decode closure) for the `Value` type
53 | /// - returns: A closure that takes a `NSDictionary` and "maps" it using key and value decode closures
54 | public static func decoder(key keyDecoder: @escaping (Any) throws -> Key, value valueDecoder: @escaping (Any) throws -> Value) -> (Any) throws -> Dictionary {
55 | return { json in
56 | var dict = Dictionary()
57 | for (key, value) in try NSDictionary.decode(json) {
58 | try dict[keyDecoder(key)] = valueDecoder(value)
59 | }
60 | return dict
61 | }
62 | }
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/Tests/DictionaryTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DictionaryTests.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2015-10-30.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import protocol Decodable.Decodable
11 | import enum Decodable.DecodingError
12 | import Decodable
13 |
14 | private struct Color: Decodable, Equatable {
15 | let name: String
16 | static func decode(_ json: Any) throws -> Color {
17 | return try Color(name: String.decode(json))
18 | }
19 |
20 | static func == (lhs: Color, rhs: Color) -> Bool {
21 | return lhs.name == rhs.name
22 | }
23 |
24 | }
25 |
26 |
27 |
28 | /* FIXME: https://github.com/Anviking/Decodable/issues/120
29 | private struct AccessibilityInfo: Decodable {
30 | let data: [String: Any]
31 |
32 | static func decode(_ json: Any) throws -> AccessibilityInfo {
33 | return try AccessibilityInfo(
34 | data: [String: Any].decode(json)
35 | )
36 | }
37 | }
38 | */
39 | class DictionaryTests: XCTestCase {
40 |
41 | override func setUp() {
42 | super.setUp()
43 | // Put setup code here. This method is called before the invocation of each test method in the class.
44 | }
45 |
46 | override func tearDown() {
47 | // Put teardown code here. This method is called after the invocation of each test method in the class.
48 | super.tearDown()
49 | }
50 |
51 | func testNormal() {
52 | let json: NSDictionary = ["object": ["key1": 1, "key2": 2, "key3": 3]]
53 | let a: [String: Int] = try! json => "object"
54 | XCTAssertEqual(a, ["key1": 1, "key2": 2, "key3": 3])
55 | }
56 |
57 | func testDictionaryWithDecodableValues() {
58 | let json: NSDictionary = ["r": "red", "g": "green"]
59 | let result = try! [String: Color].decode(json)
60 | XCTAssertEqual(["r": Color(name: "red"), "g": Color(name: "green")], result)
61 | }
62 |
63 | func testOptionalReturn() {
64 | var json: NSDictionary = ["object": ["key1": 1, "key2": 2, "key3": 3]]
65 | var result: [String: Int]? = try! json => "object"
66 | XCTAssertEqual(result!, ["key1": 1, "key2": 2, "key3": 3])
67 |
68 | json = ["object": NSNull()]
69 | result = try! json => "object"
70 | XCTAssertNil(result)
71 | }
72 |
73 | /* FIXME: https://github.com/Anviking/Decodable/issues/120
74 | func testStringAny() {
75 | let dict: NSDictionary = ["a": 2]
76 | let info = try! AccessibilityInfo.decode(dict)
77 | XCTAssertEqual(info.data as NSDictionary, dict)
78 |
79 | }
80 | */
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/Sources/OptionalKeyPath.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OptionalKeyPath.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2016-07-09.
6 | // Copyright © 2016 anviking. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// A key in a keyPath that may or may not be required.
12 | ///
13 | /// Trying to access a invalid key in a dictionary with an `OptionalKey` will usually result
14 | /// in a nil return value instead of a thrown error. Unless `isRequired` is `true`, in which
15 | /// it behaves as a "normal" `String` inside a "normal" `KeyPath`.
16 | public struct OptionalKey {
17 | public var key: String
18 | public var isRequired: Bool
19 | }
20 |
21 | extension OptionalKey: CustomStringConvertible {
22 | public var description: String {
23 | return key + (isRequired ? "" : "?")
24 | }
25 | }
26 |
27 | /// `OptionalKeyPath` represents the path to a specific node in a tree of nested dictionaries.
28 | ///
29 | /// Can be created from string and array literals and can be joined by the `=>?` operator.
30 | /// ```
31 | /// let a: OptionalKeyPath = "a"
32 | /// let b: OptionalKeyPath = ["a", "b"]
33 | /// let c: OptionalKeyPath = "a" =>? "b" =>? "c"
34 | /// ```
35 | /// Unlike `KeyPath`, `OptionalKeyPath` allows each key to be either required or optional.
36 | ///`isRequired` is `false` by default.
37 | ///
38 | /// When a `KeyPath` is converted to a OptionalKeyPath, `isRequired` is set to `true`.
39 | /// ```
40 | /// let c: OptionalKeyPath = "a" =>? "b" => "c"
41 | /// ^^
42 | /// isRequired=true
43 | /// ```
44 | /// In the above example `"c"` is inferred as `KeyPath`, then converted to `OptionalKeyPath`
45 | /// with `isRequired = true`
46 |
47 | public struct OptionalKeyPath {
48 | public var keys: [OptionalKey]
49 | mutating func markFirst(required: Bool) {
50 | if var first = keys.first {
51 | first.isRequired = required
52 | keys[0] = first
53 | }
54 | }
55 |
56 | func markingFirst(required: Bool) -> OptionalKeyPath {
57 | var new = self
58 | if var first = keys.first {
59 | first.isRequired = required
60 | new.keys[0] = first
61 | }
62 | return new
63 | }
64 | }
65 |
66 | extension OptionalKeyPath: ExpressibleByStringLiteral {
67 | public init(stringLiteral value: String) {
68 | self.keys = [OptionalKey(key: value, isRequired: false)]
69 | }
70 |
71 | public init(extendedGraphemeClusterLiteral value: String) {
72 | self.keys = [OptionalKey(key: value, isRequired: false)]
73 | }
74 |
75 | public init(unicodeScalarLiteral value: String) {
76 | self.keys = [OptionalKey(key: value, isRequired: false)]
77 | }
78 | }
79 |
80 | extension OptionalKeyPath: ExpressibleByArrayLiteral {
81 | public init(arrayLiteral elements: String...) {
82 | self.keys = elements.map { OptionalKey(key: $0, isRequired: false) }
83 | }
84 | }
85 |
86 | // MARK: Equality
87 |
88 | extension OptionalKey: Equatable {}
89 | public func == (lhs: OptionalKey, rhs: OptionalKey) -> Bool {
90 | return lhs.key == rhs.key && lhs.isRequired == rhs.isRequired
91 | }
92 |
--------------------------------------------------------------------------------
/Sources/Parse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Parse.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2015-08-13.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | func parse(_ json: Any, _ keyPath: KeyPath) throws -> Any {
12 | var currentDict = json
13 |
14 | for (index, key) in keyPath.keys.enumerated() {
15 | guard let result = try NSDictionary.decode(currentDict)[key] else {
16 | let currentPath = keyPath.keys[0 ..< index]
17 | let metadata = DecodingError.Metadata(path: Array(currentPath), object: currentDict, rootObject: json)
18 | throw DecodingError.missingKey(key, metadata)
19 | }
20 |
21 | currentDict = result
22 | }
23 |
24 | return currentDict
25 | }
26 |
27 | func parse(_ json: Any, _ path: OptionalKeyPath) throws -> Any? {
28 | var currentDict = json
29 |
30 | for (index, key) in path.keys.enumerated() {
31 | guard let result = try NSDictionary.decode(currentDict)[key.key] else {
32 | if key.isRequired {
33 | let currentPath = path.keys[0 ..< index].map { $0.key }
34 | let metadata = DecodingError.Metadata(path: currentPath, object: currentDict, rootObject: json)
35 | throw DecodingError.missingKey(key.key, metadata)
36 | } else {
37 | return nil
38 | }
39 | }
40 | currentDict = result
41 | }
42 |
43 | return currentDict
44 | }
45 | public func parse(_ json: Any, keyPath: KeyPath, decoder: ((Any) throws -> T)) throws -> T {
46 | let object = try parse(json, keyPath)
47 | return try catchAndRethrow(json, keyPath) { try decoder(object) }
48 | }
49 |
50 | // FIXME: Should perhaps not return T?, but this way we don't have to flatMap in certain overloads
51 | public func parse(_ json: Any, keyPath: OptionalKeyPath, decoder: ((Any) throws -> T?)) throws -> T? {
52 | guard let object = try parse(json, keyPath) else { return nil }
53 | return try catchAndRethrow(json, keyPath) { try decoder(object) }
54 | }
55 |
56 |
57 | // MARK: - Helpers
58 |
59 | func catchMissingKeyAndReturnNil(_ closure: () throws -> T) throws -> T? {
60 | do {
61 | return try closure()
62 | } catch DecodingError.missingKey {
63 | return nil
64 | }
65 | }
66 |
67 | func catchAndRethrow(_ json: Any, _ keyPath: KeyPath, block: () throws -> T) throws -> T {
68 | do {
69 | return try block()
70 | } catch let error as DecodingError {
71 | var error = error
72 | error.metadata.path = keyPath.keys + error.metadata.path
73 | error.metadata.rootObject = json
74 | throw error
75 | } catch let error {
76 | throw error
77 | }
78 | }
79 |
80 | func catchAndRethrow(_ json: Any, _ keyPath: OptionalKeyPath, block: () throws -> T) throws -> T {
81 | do {
82 | return try block()
83 | } catch let error as DecodingError {
84 | var error = error
85 | error.metadata.path = keyPath.keys.map{$0.key} + error.metadata.path
86 | error.metadata.rootObject = json
87 | throw error
88 | } catch let error {
89 | throw error
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/xcshareddata/xcschemes/Decodable-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/xcshareddata/xcschemes/Decodable-watchOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
34 |
35 |
45 |
46 |
52 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
70 |
71 |
72 |
73 |
75 |
76 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/Tests/KeyPathTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // KeyPathTests.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2016-07-09.
6 | // Copyright © 2016 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import protocol Decodable.Decodable
11 | import enum Decodable.DecodingError
12 | import struct Decodable.KeyPath
13 | @testable import Decodable
14 | class KeyPathTests: XCTestCase {
15 |
16 | func testCreateFromStringLiteral() {
17 | let keyPath: KeyPath = "a"
18 | XCTAssertEqual(keyPath.keys, ["a"])
19 | }
20 |
21 | func testCreateFromArrayLiteral() {
22 | let keyPath: KeyPath = ["a", "b"]
23 | XCTAssertEqual(keyPath.keys, ["a", "b"])
24 | }
25 |
26 | func testCreateWithOperators() {
27 | let keyPath: KeyPath = "a" => "b"
28 | XCTAssertEqual(keyPath.keys, ["a", "b"])
29 | }
30 |
31 | func testJoiningKeyPaths() {
32 | let a: KeyPath = "a"
33 | let bAndC: KeyPath = ["b", "c"]
34 | let keyPath: KeyPath = a => bAndC
35 | XCTAssertEqual(keyPath.keys, ["a", "b", "c"])
36 | }
37 | }
38 |
39 | class OptionalKeyPathTests: XCTestCase {
40 | func testCreateFromStringLiteral() {
41 | let keyPath: OptionalKeyPath = "a"
42 | XCTAssertEqual(keyPath.keys, [OptionalKey(key: "a", isRequired: false)])
43 | }
44 |
45 | func testCreateFromArrayLiteral() {
46 | let keyPath: OptionalKeyPath = ["a", "b"]
47 | XCTAssertEqual(keyPath.keys, [
48 | OptionalKey(key: "a", isRequired: false),
49 | OptionalKey(key: "b", isRequired: false),
50 | ])
51 | }
52 |
53 | func testCreateWithOperators() {
54 | let keyPath: OptionalKeyPath = "a" =>? "b"
55 | XCTAssertEqual(keyPath.keys, [
56 | OptionalKey(key: "a", isRequired: false),
57 | OptionalKey(key: "b", isRequired: false),
58 | ])
59 | }
60 |
61 | func testJoiningKeyPaths() {
62 | let a: OptionalKeyPath = "a"
63 | let bAndC: OptionalKeyPath = ["b", "c"]
64 | let keyPath: OptionalKeyPath = a =>? bAndC
65 | XCTAssertEqual(keyPath.keys, [
66 | OptionalKey(key: "a", isRequired: false),
67 | OptionalKey(key: "b", isRequired: false),
68 | OptionalKey(key: "c", isRequired: false)
69 | ])
70 | }
71 |
72 | // MARK: More difficult ones
73 |
74 |
75 | func testConversionFromKeyPath() {
76 | let keyPath1: OptionalKeyPath = "a" => "b" => "c"
77 | XCTAssertEqual(keyPath1.keys, [
78 | OptionalKey(key: "a", isRequired: false),
79 | OptionalKey(key: "b", isRequired: true),
80 | OptionalKey(key: "c", isRequired: true)
81 | ])
82 |
83 | let keyPath2: OptionalKeyPath = "a" =>? "b" => "c"
84 | XCTAssertEqual(keyPath2.keys, [
85 | OptionalKey(key: "a", isRequired: false),
86 | OptionalKey(key: "b", isRequired: false),
87 | OptionalKey(key: "c", isRequired: true)
88 | ])
89 |
90 | let keyPath3: OptionalKeyPath = "a" => "b" =>? "c"
91 | XCTAssertEqual(keyPath3.keys, [
92 | OptionalKey(key: "a", isRequired: false),
93 | OptionalKey(key: "b", isRequired: true),
94 | OptionalKey(key: "c", isRequired: false)
95 | ])
96 |
97 | let keyPath4: OptionalKeyPath = KeyPath("a") =>? "b" =>? "c"
98 | XCTAssertEqual(keyPath4.keys, [
99 | OptionalKey(key: "a", isRequired: true),
100 | OptionalKey(key: "b", isRequired: false),
101 | OptionalKey(key: "c", isRequired: false)
102 | ])
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/Sources/Castable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Castable.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2015-09-25.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Attempt to cast an `Any` to `T` or throw
12 | ///
13 | /// - throws: `DecodingError.typeMismatch(expected, actual, metadata)`
14 | public func cast(_ object: Any) throws -> T {
15 | guard let result = object as? T else {
16 | let metadata = DecodingError.Metadata(object: object)
17 | throw DecodingError.typeMismatch(expected: T.self, actual: type(of: object), metadata)
18 | }
19 | return result
20 | }
21 |
22 | /// Allows overriding default `decode` function from your app.
23 | ///
24 | /// You likely don't want to conform to this yourself.
25 | public protocol DynamicDecodable {
26 | associatedtype DecodedType
27 |
28 | /// A closure describing how this type should be decoded
29 | ///
30 | /// Types also conforming to `Decodable` call this closure
31 | /// from their `decode` function.
32 | ///
33 | /// - note: This is intended as a set-once thing.
34 | static var decoder: (Any) throws -> DecodedType {get set}
35 | }
36 |
37 | extension Decodable where Self: DynamicDecodable, Self.DecodedType == Self {
38 | public static func decode(_ json: Any) throws -> Self {
39 | return try decoder(json)
40 |
41 | }
42 | }
43 |
44 | extension String: Decodable, DynamicDecodable {
45 | public static var decoder: (Any) throws -> String = { try cast($0) }
46 | }
47 | extension Int: Decodable, DynamicDecodable {
48 | public static var decoder: (Any) throws -> Int = { try cast($0) }
49 | }
50 | extension Double: Decodable, DynamicDecodable {
51 | public static var decoder: (Any) throws -> Double = { try cast($0) }
52 | }
53 | extension Bool: Decodable, DynamicDecodable {
54 | public static var decoder: (Any) throws -> Bool = { try cast($0) }
55 | }
56 |
57 | private let iso8601DateFormatter: DateFormatter = {
58 | let formatter = DateFormatter()
59 | formatter.locale = Locale(identifier: "en_US_POSIX")
60 | formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
61 | return formatter
62 | }()
63 |
64 | extension Date: Decodable, DynamicDecodable {
65 | /// Default decoder is `Date.decoder(using: iso8601DateFormatter)`
66 | public static var decoder: (Any) throws -> Date = Date.decoder(using: iso8601DateFormatter)
67 |
68 | /// Create a decode closure using a given formatter
69 | ///
70 | /// Example usage:
71 | /// ```
72 | /// let formatter = DateFormatter(...)
73 | /// Date.decoder = Date.decoder(using: formatter)
74 | /// ```
75 | public static func decoder(using formatter: DateFormatter) -> (Any) throws -> Date {
76 | return { object in
77 | let string = try String.decode(object)
78 | guard let date = formatter.date(from: string) else {
79 | let metadata = DecodingError.Metadata(object: object)
80 | throw DecodingError.rawRepresentableInitializationError(rawValue: string, metadata)
81 | }
82 | return date
83 | }
84 | }
85 |
86 | }
87 |
88 | extension NSDictionary: Decodable {
89 | public static func decode(_ json: Any) throws -> Self {
90 | return try cast(json)
91 | }
92 | }
93 |
94 | extension NSArray: DynamicDecodable {
95 | public static var decoder: (Any) throws -> NSArray = { try cast($0) }
96 | public static func decode(_ json: Any) throws -> NSArray {
97 | return try decoder(json)
98 | }
99 |
100 | }
101 |
102 |
103 | extension URL: DynamicDecodable, Decodable {
104 | public static var decoder: (Any) throws -> URL = { object in
105 | let string = try String.decode(object)
106 | guard let url = URL(string: string) else {
107 | let metadata = DecodingError.Metadata(object: object)
108 | throw DecodingError.rawRepresentableInitializationError(rawValue: string, metadata)
109 | }
110 | return url
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/Tests/ErrorPathTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorPathTests.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2015-07-16.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import protocol Decodable.Decodable
11 | import enum Decodable.DecodingError
12 | @testable import Decodable
13 |
14 | private struct Color: Decodable {
15 | let name: String
16 |
17 | static func decode(_ json: Any) throws -> Color {
18 | return try Color(name: json => "name")
19 | }
20 | }
21 |
22 | private struct Apple: Decodable {
23 | let id: Int
24 | let color: Color?
25 |
26 | static func decode(_ json: Any) throws -> Apple {
27 | return try Apple(id: json => "id", color: json => "color")
28 | }
29 | }
30 |
31 | private struct Tree: Decodable {
32 | let apples: [Apple]
33 |
34 | static func decode(_ json: Any) throws -> Tree {
35 | return try Tree(apples: json => "apples")
36 | }
37 | }
38 |
39 | class ErrorPathTests: XCTestCase {
40 |
41 | func testMissingKeyErrorPath() {
42 |
43 | let dict: NSDictionary = ["object": ["repo": ["owner": ["id" : 1, "login": "anviking"]]]]
44 |
45 | do {
46 | _ = try dict => "object" => "repo" => "owner" => "oops" as String
47 | } catch DecodingError.missingKey(_ , let metadata) {
48 | XCTAssertEqual(metadata.formattedPath, "object.repo.owner")
49 | } catch let error {
50 | XCTFail("should not throw this exception: \(error)")
51 | }
52 | }
53 |
54 | // FIXME: #
55 | func testNestedUnexpectedNSNull() {
56 | let dict: NSDictionary = ["id": 1, "color": ["name": NSNull()]]
57 | do {
58 | let apple = try Apple.decode(dict)
59 | print(apple)
60 | XCTFail()
61 | } catch DecodingError.typeMismatch(_, _, let metadata) where metadata.object is NSNull {
62 |
63 | } catch let error {
64 | XCTFail("should not throw this exception: \(error)")
65 | }
66 | }
67 |
68 | func testTypeMismatchErrorPath() {
69 |
70 | let dict: NSDictionary = ["object": ["repo": ["owner": ["id" : 1, "login": 0]]]]
71 |
72 | do {
73 | _ = try dict => "object" => "repo" => "owner" => "login" as String
74 | } catch let DecodingError.typeMismatch(_, actual, metadata) {
75 | let typeString = String(describing: actual)
76 | XCTAssertTrue(typeString.contains("Number"), "\(typeString) should contain NSNumber")
77 | XCTAssertEqual(metadata.formattedPath, "object.repo.owner.login")
78 | XCTAssertEqual(metadata.object as? Int, 0)
79 | } catch let error {
80 | XCTFail("should not throw this exception: \(error)")
81 | }
82 | }
83 |
84 | func testNestedObjectTypeMismatchPath() {
85 | let dict: NSDictionary = ["apples": [["id": 2, "color": ["name": "red"]],
86 | ["id": 2, "color": ["name": "green"]],
87 | ["id": 2, "color": ["name": 3]]]]
88 | do {
89 | _ = try Tree.decode(dict)
90 | XCTFail()
91 | } catch let DecodingError.typeMismatch(_, actual, metadata) {
92 | XCTAssertTrue(String(describing: actual).contains("Number"))
93 | XCTAssertEqual(metadata.formattedPath, "apples.color.name")
94 | } catch let error {
95 | XCTFail("should not throw this exception: \(error)")
96 | }
97 | }
98 |
99 |
100 | func testFoo() {
101 | let dictionary: NSDictionary = ["key": ["test": 3]]
102 | let a: Int = try! uppercase(dictionary => "key") => "TEST"
103 | XCTAssertEqual(a, 3)
104 | }
105 |
106 | private func uppercase(_ json: NSDictionary) -> NSDictionary {
107 | var result = [String: Any]()
108 | for (key, value) in json {
109 | result[(key as! String).uppercased()] = value
110 | }
111 | print(result)
112 | return result as NSDictionary
113 | }
114 |
115 | }
116 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/xcshareddata/xcschemes/Decodable-Mac.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
54 |
55 |
65 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
84 |
90 |
91 |
92 |
93 |
95 |
96 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/xcshareddata/xcschemes/Decodable-iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
34 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
54 |
55 |
65 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
84 |
90 |
91 |
92 |
93 |
95 |
96 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/Tests/DecodableExtensionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DecodableExtensionTests.swift
3 | // Decodable
4 | //
5 | // Created by FJBelchi on 13/07/15.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import protocol Decodable.Decodable
11 | import enum Decodable.DecodingError
12 | @testable import Decodable
13 |
14 | class DecodableExtensionTests: XCTestCase {
15 |
16 | // MARK: String
17 | func testStringDecodableSuccess() {
18 | //given
19 | let anyObject = "hello"
20 | //when
21 | let string = try! String.decode(anyObject)
22 | //then
23 | XCTAssertEqual(string, anyObject)
24 | }
25 |
26 | func testStringDecodableFail() {
27 | //given
28 | let anyObject = 0
29 | //when
30 | do {
31 | _ = try String.decode(anyObject)
32 | } catch DecodingError.typeMismatch {
33 | //then
34 | XCTAssertTrue(true)
35 | } catch {
36 | XCTFail("should not throw this exception")
37 | }
38 | }
39 |
40 | // MARK: Int
41 | func testIntDecodable() {
42 | //given
43 | let anyObject = 0
44 | //when
45 | let int = try! Int.decode(anyObject)
46 | //then
47 | XCTAssertEqual(int, anyObject)
48 | }
49 |
50 | func testIntDecodableFail() {
51 | //given
52 | let anyObject = ""
53 | //when
54 | do {
55 | _ = try Int.decode(anyObject)
56 | } catch DecodingError.typeMismatch {
57 | //then
58 | XCTAssertTrue(true)
59 | } catch {
60 | XCTFail("should not throw this exception")
61 | }
62 | }
63 |
64 | // MARK: Double
65 | func testDoubleDecodable() {
66 | //given
67 | let anyObject = 0.5
68 | //when
69 | let double = try! Double.decode(anyObject)
70 | //then
71 | XCTAssertEqual(double, anyObject)
72 | }
73 |
74 | func testDoubleDecodableFail() {
75 | //given
76 | let anyObject = ""
77 | //when
78 | do {
79 | _ = try Double.decode(anyObject)
80 | } catch DecodingError.typeMismatch {
81 | //then
82 | XCTAssertTrue(true)
83 | } catch {
84 | XCTFail("should not throw this exception")
85 | }
86 | }
87 |
88 | // MARK: Bool
89 | func testBoolDecodable() {
90 | //given
91 | let anyObject = true
92 | //when
93 | let bool = try! Bool.decode(anyObject)
94 | //then
95 | XCTAssertEqual(bool, anyObject)
96 | }
97 |
98 | func testBoolDecodableFail() {
99 | //given
100 | let anyObject = ""
101 | //when
102 | do {
103 | _ = try Bool.decode(anyObject)
104 | } catch DecodingError.typeMismatch {
105 | //then
106 | XCTAssertTrue(true)
107 | } catch {
108 | XCTFail("should not throw this exception")
109 | }
110 | }
111 |
112 | // MARK: Date
113 | func testDateDecodable() {
114 | //given
115 | let anyObject = "1970-01-01T00:00:00Z"
116 | //when
117 | let date = try! Date.decode(anyObject)
118 | //then
119 | XCTAssertEqual(date, Date(timeIntervalSince1970: 0))
120 | }
121 |
122 | func testDateDecodableFail() {
123 | //given
124 | let anyObject = ""
125 | //when
126 | do {
127 | _ = try Date.decode(anyObject)
128 | } catch DecodingError.rawRepresentableInitializationError(let rawValue, let metaData) {
129 | //then
130 | XCTAssertEqual(rawValue as! String, "")
131 | XCTAssertEqual(metaData.object as! String, anyObject)
132 | } catch {
133 | XCTFail("should not throw this exception")
134 | }
135 | }
136 |
137 | // MARK: URL
138 | func testURLDecodable() {
139 | //given
140 | let anyObject = "http://www.google.com"
141 | //when
142 | let url = try! URL.decode(anyObject)
143 | //then
144 | XCTAssertEqual(url, URL(string: anyObject))
145 | }
146 |
147 | func testURLDecodableFail() {
148 | //given
149 | let anyObject = ""
150 | //when
151 | do {
152 | _ = try URL.decode(anyObject)
153 | } catch DecodingError.rawRepresentableInitializationError(let rawValue, let metaData) {
154 | //then
155 | XCTAssertEqual(rawValue as! String, "")
156 | XCTAssertEqual(metaData.object as! String, anyObject)
157 | } catch {
158 | XCTFail("should not throw this exception")
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/Sources/DecodingError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DecodingError.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2015-07-17.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public enum DecodingError: Error, Equatable {
12 |
13 | /// `DecodingError.Metadata` provides information about
14 | /// where an `DecodingError` was thrown in the JSON
15 | /// object graph.
16 | public struct Metadata: Equatable {
17 |
18 | public init(path: [String] = [], object: Any, rootObject: Any? = nil) {
19 | self.path = path
20 | self.object = object
21 | self.rootObject = rootObject
22 | }
23 |
24 | /// The JSON key path to the object that failed to be decoded
25 | public var path: [String]
26 |
27 | /// The JSON object that failed to be decoded
28 | public let object: Any
29 |
30 | /// The root JSON object for which the `path` can be used to find `object`
31 | public var rootObject: Any?
32 |
33 | /// Represents the path to the object that failed decoding with "." as a separator.
34 | public var formattedPath: String {
35 | return path.joined(separator: ".")
36 | }
37 | }
38 |
39 | /// Thrown when optional casting from `Any` fails.
40 | ///
41 | /// This can happen both when trying to access a key on a object
42 | /// that isn't a `NSDictionary`, and failing to cast a `Castable`
43 | /// primitive.
44 | case typeMismatch(expected: Any.Type, actual: Any.Type, Metadata)
45 |
46 | /// Thrown when a given, required, key was not found in a dictionary.
47 | case missingKey(String, Metadata)
48 |
49 | /// Thrown from the `RawRepresentable` extension when
50 | /// `init(rawValue:)` returned `nil`.
51 | case rawRepresentableInitializationError(rawValue: Any, Metadata)
52 |
53 | /// When an error is thrown that isn't `DecodingError`, it
54 | /// will be wrapped in `DecodingError.other` in order to also provide
55 | /// metadata about where the error was thrown.
56 | case other(Error, Metadata)
57 |
58 | public var metadata: Metadata {
59 | get {
60 | switch self {
61 | case .typeMismatch(expected: _, actual: _, let metadata):
62 | return metadata
63 | case .missingKey(_, let metadata):
64 | return metadata
65 | case .rawRepresentableInitializationError(_, let metadata):
66 | return metadata
67 | case .other(_, let metadata):
68 | return metadata
69 | }
70 | }
71 |
72 | set {
73 | switch self {
74 | case let .typeMismatch(expected, actual, _):
75 | self = .typeMismatch(expected: expected, actual: actual, newValue)
76 | case let .missingKey(key, _):
77 | self = .missingKey(key, newValue)
78 | case let .rawRepresentableInitializationError(rawValue, _):
79 | self = DecodingError.rawRepresentableInitializationError(rawValue: rawValue, newValue)
80 | case let .other(error, _):
81 | self = .other(error, newValue)
82 | }
83 | }
84 |
85 | }
86 |
87 | public var debugDescription: String {
88 | switch self {
89 | case let .typeMismatch(expected, actual, metadata):
90 | return "typeMismatch expected: \(expected) but \(metadata.object) is of type \(actual) in \(metadata.formattedPath)"
91 | case let .missingKey(key, metadata):
92 | return "missingKey \(key) in \(metadata.formattedPath) \(metadata.object)"
93 | case let .rawRepresentableInitializationError(rawValue, metadata):
94 | return "rawRepresentableInitializationError: \(rawValue) could not be used to initialize \("TYPE"). (path: \(metadata.formattedPath))" // FIXME
95 | case let .other(error, _):
96 | return "\(error)"
97 | }
98 | }
99 |
100 | }
101 |
102 |
103 | // Allow types to be used in pattern matching
104 | // E.g case typeMismatchError(NSNull.self, _, _) but be careful
105 | // You probably rather want to modify the decode-closure
106 | // There are overloads for this
107 | public func ~=(lhs: T.Type, rhs: Any.Type) -> Bool {
108 | return lhs == rhs
109 | }
110 |
111 | // FIXME: I'm not sure about === equality
112 | public func ==(lhs: DecodingError.Metadata, rhs: DecodingError.Metadata) -> Bool {
113 | return lhs.object as AnyObject === rhs.object as AnyObject
114 | && lhs.path == rhs.path
115 | && lhs.rootObject as AnyObject === rhs.rootObject as AnyObject
116 | }
117 |
118 | public func ==(lhs: DecodingError, rhs: DecodingError) -> Bool {
119 | switch (lhs, rhs) {
120 | case let (.typeMismatch(expected, actual, metadata), .typeMismatch(expected2, actual2, metadata2)):
121 | return expected == expected2
122 | && actual == actual2
123 | && metadata == metadata2
124 | case let (.missingKey(key, metadata), .missingKey(key2, metadata2)):
125 | return key == key2
126 | && metadata == metadata2
127 | case let (.rawRepresentableInitializationError(rawValue, metadata), .rawRepresentableInitializationError(rawValue2, metadata2)):
128 | // FIXME: Might be strange
129 | switch (rawValue, rawValue2, metadata == metadata2) {
130 | case let (a as AnyObject, b as AnyObject, true):
131 | return a === b
132 | default:
133 | return false
134 | }
135 | case (.other, .other):
136 | // FIXME: What to do?
137 | print("FIXME: other equality is unimplemented/not supported")
138 | return false
139 | default:
140 | return false
141 | }
142 | }
143 |
144 |
--------------------------------------------------------------------------------
/Tests/ArrayTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArrayTests.swift
3 | // Decodable
4 | //
5 | // Created by Johannes Lund on 2015-07-19.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import protocol Decodable.Decodable
11 | import enum Decodable.DecodingError
12 | import struct Decodable.KeyPath
13 | @testable import Decodable
14 |
15 | class DecodableArrayTests: XCTestCase {
16 |
17 | func testDecodeAnyDecodableArraySuccess() {
18 | // given
19 | let key = "key"
20 | let value: NSArray = ["value1", "value2", "value3"]
21 | let dictionary: NSDictionary = [key: value]
22 | // when
23 | let result = try! dictionary => KeyPath(key) as Array
24 | // then
25 | XCTAssertEqual(result, value as! [String])
26 | }
27 |
28 | func testDecodeOptionalDecodableArraySuccess() {
29 | // given
30 | let key = "key"
31 | let value: NSArray = ["value1", "value2", NSNull(), "value3"]
32 | let dictionary: NSDictionary = [key: value]
33 | // when
34 | let result = try! dictionary => KeyPath(key) as [String?]
35 | // then
36 | XCTAssertEqual(result.count, 4)
37 | XCTAssertEqual(result[0], "value1")
38 | XCTAssertEqual(result[1], "value2")
39 | XCTAssertEqual(result[2], nil)
40 | XCTAssertEqual(result[3], "value3")
41 | }
42 |
43 | func testDecodeOptionalDecodableArrayFailure() {
44 | // given
45 | let key = "key"
46 | let value: NSArray = ["value1", "value2", 0x8BAD, "value3"]
47 | let dictionary: NSDictionary = [key: value]
48 | // when
49 | do {
50 | _ = try dictionary => KeyPath(key) as [String?]
51 | XCTFail("should throw")
52 | } catch DecodingError.typeMismatch {
53 | // Yay
54 | } catch {
55 | XCTFail("should not throw \(error)")
56 | }
57 | }
58 |
59 | func testDecodeNestedDecodableArraySuccess() {
60 | // given
61 | let value: NSArray = ["value1", "value2", "value3"]
62 | let dictionary: NSDictionary = ["key": ["key": value]]
63 | // when
64 | let result = try! dictionary => "key" => "key" as Array
65 | // then
66 | XCTAssertEqual(result, value as! [String])
67 | }
68 |
69 | func testDecodeAnyDecodableOptionalArraySuccess() {
70 | // given
71 | let key = "key"
72 | let value = ["value"]
73 | let dictionary: NSDictionary = [key: value]
74 | // when
75 | let string = try! dictionary => KeyPath(key) as [String]?
76 | // then
77 | XCTAssertEqual(string!, value)
78 | }
79 |
80 | func testDecodeAnyDecodableNestedOptionalArraySuccess() {
81 | // given
82 | let value = ["value"]
83 | let dictionary: NSDictionary = ["key": ["key": value]]
84 | // when
85 | let string = try! dictionary => "key" => "key" as [String]?
86 | // then
87 | XCTAssertEqual(string!, value)
88 | }
89 |
90 | func testDecodeAnyDecodableOptionalArrayNilSuccess() {
91 | // given
92 | let key = "key"
93 | let dictionary: NSDictionary = [key: NSNull()]
94 | // when
95 | let string = try! dictionary => KeyPath(key) as [String]?
96 | // then
97 | XCTAssertNil(string)
98 | }
99 |
100 | func testDecodeAnyDecodableOptionalArrayMissingKeyFailure() {
101 | // given
102 | let key = "key"
103 | let dictionary = NSDictionary()
104 | // when
105 | do {
106 | _ = try dictionary => KeyPath(key) as [String]?
107 | XCTFail()
108 | } catch DecodingError.missingKey {
109 |
110 | } catch {
111 | XCTFail()
112 | }
113 | }
114 |
115 |
116 | // MARK: =>?
117 |
118 | func testDecodeSafeArraySuccess() {
119 | // given
120 | let key = "key"
121 | let value = ["A", "B", "C"]
122 | let dictionary: NSDictionary = [key: value]
123 | // when
124 | let array = try? [String].decode(dictionary => "key")
125 | // then
126 | XCTAssertEqual(array!, value)
127 | }
128 |
129 | func testDecodeSafeArrayCatchTypeExceptionMismatch() {
130 | // given
131 | let key = "key"
132 | let value = ["A", 2, "B"] as [Any]
133 | let dictionary: NSDictionary = [key: value]
134 | // when
135 | let array = try! [String].decode(dictionary => "key", ignoreInvalidObjects: true)
136 | // then
137 | XCTAssertEqual(array, ["A", "B"])
138 | }
139 |
140 | func testDecodeSafeArrayCatchTypeMismatchExceptionInObjects() {
141 | // given
142 | let key = "key"
143 | let value = [["id": "007", "login": "mradams"], ["id": 1, "login": "jenglish"]]
144 | let dictionary: NSDictionary = [key: value]
145 | // when
146 | let array = try! [Owner].decode(dictionary => "key", ignoreInvalidObjects: true)
147 | // then
148 | XCTAssertEqual(array, [Owner(id: 1, login: "jenglish")])
149 | }
150 |
151 | func testDecodeSafeArrayCatchJSONNotObjectException() {
152 | // given
153 | let key = "key"
154 | let value = [["id": 7, "login": "mradams"], 2] as [Any]
155 | let dictionary: NSDictionary = [key: value]
156 | // when
157 | let array = try! [Owner].decode(dictionary => "key", ignoreInvalidObjects: true)
158 | // then
159 | XCTAssertEqual(array, [Owner(id: 7, login: "mradams")])
160 | }
161 |
162 | func testDecodeSafeArrayCatchMissingKeyException() {
163 | // given
164 | let key = "key"
165 | let value = [["login": "mradams"], ["id": 1, "login": "jenglish"]]
166 | let dictionary: NSDictionary = [key: value]
167 | // when
168 | let array = try! [Owner].decode(dictionary => KeyPath(key), ignoreInvalidObjects: true)
169 | // then
170 | XCTAssertEqual(array, [Owner(id: 1, login: "jenglish")])
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/Tests/DecodableTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DecodableTests.swift
3 | // DecodableTests
4 | //
5 | // Created by Johannes Lund on 2015-07-08.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import protocol Decodable.Decodable
11 | import enum Decodable.DecodingError
12 | import struct Decodable.KeyPath
13 | @testable import Decodable
14 |
15 | class DecodableTests: XCTestCase {
16 |
17 | private func readJsonFile(_ file: String) -> NSDictionary {
18 | let filePath = (Bundle(for: object_getClass(self)!).resourcePath! as NSString).appendingPathComponent(file)
19 | let jsonString = try! String(contentsOfFile: filePath)
20 | let jsonData = jsonString.data(using: String.Encoding.utf8)!
21 | return try! JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
22 | }
23 |
24 | func testDecodeRepositoryExampleShouldSuccess() {
25 | // given
26 | let json = readJsonFile("Repository.json")
27 |
28 | // when
29 | do {
30 | let repository = try Repository.decode(json)
31 | // then
32 | XCTAssertEqual(repository.id, json["id"] as? Int)
33 | XCTAssertEqual(repository.name, json["name"] as? String)
34 | XCTAssertEqual(repository.description, json["description"] as? String)
35 | XCTAssertEqual(repository.htmlUrlString, json["html_url"] as? String)
36 |
37 | let owner = repository.owner
38 | let ownerDictionary = json["owner"] as! NSDictionary
39 | XCTAssertEqual(owner.id, ownerDictionary["id"] as? Int)
40 | XCTAssertEqual(owner.login, ownerDictionary["login"] as? String)
41 |
42 | XCTAssertEqual(repository.coverage, json["coverage"] as? Double)
43 | let files = repository.files
44 | XCTAssertEqual(files.count, 3)
45 | let array = json["files"] as! Array
46 | XCTAssertEqual(files[0], array[0])
47 | XCTAssertEqual(files[1], array[1])
48 | XCTAssertEqual(files[2], array[2])
49 | XCTAssertNil(repository.optional)
50 | XCTAssertTrue(repository.active)
51 | XCTAssertNil(repository.optionalActive)
52 | } catch {
53 | XCTFail("it should not throw an exception")
54 | }
55 | }
56 |
57 | private let Count = 500
58 |
59 | func testDecodeArrayOfRepositoriesAndMeasureTime() {
60 | let json = readJsonFile("Repository.json")
61 | let array = NSArray(array: Array(repeating: json, count: Count))
62 |
63 | var result: [Repository] = []
64 | measure {
65 | do {
66 | result = try [Repository].decode(array)
67 | } catch let error {
68 | XCTFail("\(error)")
69 | }
70 | }
71 | XCTAssertEqual(result.count, Count)
72 | }
73 |
74 | func testCustomParseAndMeasureTime() {
75 | let json = readJsonFile("Repository.json")
76 | let array = NSArray(array: Array(repeating: json, count: Count))
77 |
78 | var result: [Repository] = []
79 | measure {
80 | do {
81 | result = try self.customParseRepository(array)
82 | } catch let error {
83 | XCTFail("\(error)")
84 | }
85 | }
86 | XCTAssertEqual(result.count, Count)
87 |
88 | }
89 |
90 | private func customParseRepository(_ json: Any) throws -> [Repository] {
91 | let error = NSError(domain: "test", code: 0, userInfo: nil)
92 | guard let array = json as? [NSDictionary] else {
93 | throw error
94 | }
95 | var result: [Repository] = []
96 | for dict in array {
97 | guard let id = dict["id"] as? Int,
98 | let name = dict["name"] as? String,
99 | let description = dict["description"] as? String,
100 | let htmlUrlString = dict["html_url"] as? String,
101 | let userDict = dict["owner"] as? NSDictionary,
102 | let ownerID = userDict["id"] as? Int,
103 | let ownerLogin = userDict["login"] as? String,
104 | let coverage = dict["coverage"] as? Double,
105 | let files = dict["files"] as? NSArray as? [String],
106 | let active = dict["active"] as? Bool
107 | else {
108 | throw error
109 | }
110 | let optional = dict["optional"] as? String
111 | let optionalActive = dict["optionalActive"] as? Bool
112 |
113 | let owner = Owner(id: ownerID, login: ownerLogin)
114 | let repo = Repository(
115 | id: id,
116 | name: name,
117 | description: description,
118 | htmlUrlString : htmlUrlString,
119 | owner: owner,
120 | coverage: coverage,
121 | files: files,
122 | optional: optional,
123 | active: active,
124 | optionalActive: optionalActive)
125 | result.append(repo)
126 | }
127 | return result
128 | }
129 |
130 | func testDecodeRepositoryExampleShouldThrowMissingKeyException() {
131 | // given
132 | let json = readJsonFile("missingKey.json")
133 |
134 | // when
135 | do {
136 | _ = try Repository.decode(json)
137 | } catch DecodingError.missingKey(let key, _) {
138 | // then
139 | XCTAssertEqual(key, "id")
140 | } catch let error as DecodingError {
141 | XCTFail("it should not throw \(error)")
142 | } catch {
143 | XCTFail("it should not throw this exception")
144 | }
145 | }
146 |
147 | func testDecodeRepositoryExampleShouldThrowTypeMismatchException() {
148 | // given
149 | let json = readJsonFile("typeMismatch.json")
150 |
151 | // when
152 | do {
153 | _ = try Repository.decode(json)
154 | } catch DecodingError.missingKey {
155 | XCTFail("it should not throw this exception")
156 | } catch let DecodingError.typeMismatch(expected, _, metadata) where expected == Int.self {
157 | // then
158 | XCTAssertEqual(metadata.formattedPath, "id")
159 | } catch let error {
160 | XCTFail("should not throw \(error)")
161 | }
162 | }
163 |
164 | func testDecodeRepositoryExampleNestedShouldThrowTypeMismatchException() {
165 | // given
166 | let json: NSDictionary = ["key": readJsonFile("typeMismatch.json")]
167 |
168 | // when
169 | do {
170 | _ = (try parse(json, keyPath: ["key"], decoder: Repository.decode)) as Repository
171 | } catch DecodingError.missingKey {
172 | XCTFail("it should not throw this exception")
173 | } catch let DecodingError.typeMismatch(expected, _, metadata) where expected == Int.self {
174 | // then
175 | XCTAssertEqual(metadata.formattedPath, "key.id")
176 | } catch let error {
177 | XCTFail("should not throw \(error)")
178 | }
179 | }
180 |
181 | func testDecodeRepositoryExampleShouldThrowNoJsonObjectException() {
182 | // given
183 | let filePath = (Bundle(for: object_getClass(self)!).resourcePath! as NSString).appendingPathComponent("NoJsonObject.json")
184 | let jsonString = try! String(contentsOfFile: filePath)
185 |
186 | // when
187 | do {
188 | _ = try Repository.decode(jsonString)
189 | } catch DecodingError.missingKey {
190 | XCTFail("it should not throw this exception")
191 | } catch let DecodingError.typeMismatch(expected, _, metadata) where expected == NSDictionary.self {
192 | XCTAssertEqual(metadata.path, [])
193 | XCTAssertNotNil(metadata.object)
194 | } catch {
195 | XCTFail("should not throw \(error)")
196 | }
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Decodable
2 | Simple and strict, yet powerful object mapping made possible by Swift 2's error handling. Greatly inspired by [Argo](http://github.com/thoughtbot/Argo), but without a bizillion functional operators.
3 |
4 | [](https://github.com/Carthage/Carthage)
5 | [](https://cocoapods.org/pods/Decodable)
6 | [](https://cocoadocs.org/docsets/NSStringMask)
7 | [](https://travis-ci.org/Anviking/Decodable/branches)
8 |
9 |
10 | ```swift
11 |
12 | struct Repository {
13 | let name: String
14 | let description: String
15 | let stargazersCount: Int
16 | let language: String?
17 | let sometimesMissingKey: String?
18 |
19 | let owner: User // Struct conforming to Decodable
20 | let defaultBranch: Branch // Struct NOT conforming to Decodable
21 |
22 | var fullName: String { return "\(owner.login)/\(name)" }
23 | }
24 |
25 | extension Repository: Decodable {
26 | static func decode(j: Any) throws -> Repository {
27 | return try Repository(
28 | name: j => "nested" => "name",
29 | description: j => "description",
30 | stargazersCount: j => "stargazers_count",
31 | language: j => "language",
32 | sometimesMissingKey: j =>? "sometimesMissingKey",
33 | owner: j => "owner",
34 | defaultBranch: Branch(name: j => "default_branch")
35 | )
36 | }
37 | }
38 |
39 | do {
40 | let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
41 | let repo = try [Repository].decode(json)
42 | } catch {
43 | print(error)
44 | }
45 | ```
46 |
47 | ## How does it work?
48 |
49 | ### A protocol
50 | ```swift
51 | public protocol Decodable {
52 | static func decode(json: Any) throws -> Self
53 | }
54 | ```
55 | ### A parse-function
56 | ```swift
57 | public func parse(json: Any, path: [String], decode: (Any throws -> T)) throws -> T
58 | ```
59 |
60 | ### And shameless operator-overloading
61 | The too-many generated overloads, all calling the `parse`-function, can be found in [Overloads.swift](https://github.com/Anviking/Decodable/blob/master/Sources/Overloads.swift). Return types include `T?`, `[T?]`, `[T?]?`, `Any` and `[String: T]?`. When conditional protocol conformance is supported in Swift this won't be necessary, and automagic decoding of infinitly nested generic types (like `[[[[[[[[[A???]]: B]]]?]]?]]`) would work.
62 |
63 | An overload may look like this:
64 | ```swift
65 | public func => (json: Any, keyPath: KeyPath) throws -> T
66 | ```
67 |
68 | ## KeyPaths
69 | Keypaths can be created from string and array literals as well as with explicit initializers. They can also be joined using the operators `=>` and `=>?`. `=>?` is another operator that indicates that `nil` should be returned if the key to the right is missing.
70 |
71 | - When composing `=>` and `=>?` operators in the same keypath, the strictness of `=>` is still honoured.
72 | - Optional key paths (`=>?`) require an optional return type
73 |
74 | ```swift
75 | let a: KeyPath = "a"
76 | let b: KeyPath = ["a", "b"]
77 | let c: KeyPath = "a" => "b" => "c"
78 | let string: String? = json =>? "key1" => "key2" => "key3"`
79 | ^^^^ allowed to be missing
80 | ```
81 | ## Errors
82 | Errors will be caught and rethrown in the decoding process to backpropagate metadata, like the JSON object that failed decoding, the key path to it, and the root JSON object.
83 |
84 | From [DecodingError.swift](https://github.com/anviking/decodable/tree/master/Sources/DecodingError.swift):
85 | ```swift
86 | public enum DecodingError: ErrorProtocol, Equatable {
87 | /// Thrown when optional casting from `Any` fails.
88 | ///
89 | /// This can happen both when trying to access a key on a object
90 | /// that isn't a `NSDictionary`, and failing to cast a `Castable`
91 | /// primitive.
92 | case typeMismatch(expected: Any.Type, actual: Any.Type, Metadata)
93 |
94 | /// Thrown when a given, required, key was not found in a dictionary.
95 | case missingKey(String, Metadata)
96 |
97 | /// Thrown from the `RawRepresentable` extension when
98 | /// `init(rawValue:)` returned `nil`.
99 | case rawRepresentableInitializationError(rawValue: Any, Metadata)
100 |
101 | /// When an error is thrown that isn't `DecodingError`, it
102 | /// will be wrapped in `DecodingError.other` in order to also provide
103 | /// metadata about where the error was thrown.
104 | case other(ErrorProtocol, Metadata)
105 | }
106 | ```
107 |
108 | ```swift
109 | let dict: NSDictionary = ["object": ["repo": ["owner": ["id" : 1, "login": "anviking"]]]]
110 |
111 | do {
112 | let username: String = try dict => "object" => "repo" => "owner" => "name"
113 | } catch let error {
114 | print(error)
115 | }
116 | //
117 | // MissingKeyError at object.repo.owner: name in {
118 | // id = 1;
119 | // login = anviking;
120 | // }
121 | ```
122 |
123 | ## Handling Errors
124 | Expressions like `j => "key"` will throw directly, and `catch`-statements can be used to create the most complex error handling behaviours. This also means that `try?` can be used to return nil if *anything* goes wrong instead of throwing.
125 |
126 | For convenience there is an operator, `=>?`, that only returns nil on missing keys, for APIs that indicate `null` in that manner, and to aid working with different response formats.
127 |
128 | | Overload | Null Behaviour | Missing Key Behavior |Type Mismatch Behaviour | Errors in subobjects |
129 | | ------------- |:-------------:|:-----:|:-----:|:-----:|
130 | | `=> -> T`| throws | throws | throws | uncaught (throws) |
131 | | `=> -> T?`| nil | throws | throws | uncaught (throws) |
132 | | `=>? -> T?`| nil | nil | throws | uncaught (throws) |
133 | | `try? => -> T `| nil | nil | nil | caught (nil) |
134 |
135 | ## Customization
136 | `Int`, `Double`,`String`, `Bool`, `Date` (ISO8601), `NSArray`, and `NSDictionary` types that conform to `DynamicDecodable` with the following declaration:
137 | ```swift
138 | public protocol DynamicDecodable {
139 | associatedtype DecodedType
140 | static var decoder: (Any) throws -> DecodedType {get set}
141 | }
142 | ```
143 | This allows Decodable to implement default decoding closures while allowing you to override them as needed.
144 | ```swift
145 | // Lets extend Bool.decoder so that it accepts certain strings:
146 | Bool.decoder = { json in
147 | switch json {
148 | case let str as String where str == "true":
149 | return true
150 | case let str as String where str == "false":
151 | return false
152 | default:
153 | return try cast(json)
154 | }
155 | }
156 | ```
157 |
158 | Note that when extending new types to conform to `Decodable` there is really no point in conforming to `DynamicDecodable` since you already control the implementation. Also note that the `decoder` properties are intended as "set once". If you need different behaviour on different occations, please create custom decode functions.
159 |
160 | The default `Date.decoder` uses a ISO8601 date formatter. If you don't want to create your own decode closure there's a helper:
161 | ```swift
162 | Date.decoder = Date.decoder(using: formatter)
163 | ```
164 |
165 | ## When `Decodable` isn't enough
166 | Don't be afraid of not conforming to `Decodable`.
167 | ```swift
168 | let array = try NSArray.decode(json => "list").map {
169 | try Contribution(json: $0, repository: repo)
170 | }
171 | ```
172 |
173 | ## Tips
174 | - You can use `Decodable` with classes. Just make sure to either call a `required` initializer on self (e.g `self.init`) and return `Self`, or make your class `final`. ( [This](http://stackoverflow.com/questions/26495586/best-practice-to-implement-a-failable-initializer-in-swift) might be a problem though)
175 | - The `Decodable`-protocol and the `=>`-operator should in no way make you committed to use them everywhere.
176 |
177 | ## Compatibility
178 |
179 | | Swift version | Compatible tag or branch |
180 | | --- | --- |
181 | | Swift 4.0 | `0.6.0` |
182 | | Swift 3.0 | `v0.5` |
183 | | Swift 2.3 | `v0.4.4`|
184 | | Swift 2.2 | `v0.4.3`|
185 |
186 | ## Note on Swift 4.0 usage
187 | Due to collisions with the standard library you will have to import ambiguous symbols specifically, in addition to `Decodable` as a whole.
188 |
189 | This means you likely want the following
190 | ```swift
191 | import Decodable
192 | import protocol Decodable.Decodable
193 | ```
194 | and you can import other symbols, e.g `KeyPath`, `DecodingError`, in a simlilar fashion (using `import struct` and `import enum`)
195 |
--------------------------------------------------------------------------------
/Generator/Generator.swift:
--------------------------------------------------------------------------------
1 | #!/usr/bin/xcrun --sdk macosx swift
2 |
3 | //
4 | // Generator.swift
5 | // Decodable
6 | //
7 | // Created by Johannes Lund on 2016-02-27.
8 | // Copyright © 2016 anviking. All rights reserved.
9 | //
10 |
11 | // For generating overloads
12 |
13 | import Foundation
14 |
15 | // ----------------------------------------------------------------------------------------
16 | // MARK: Documentation
17 | // ----------------------------------------------------------------------------------------
18 |
19 | struct Behaviour {
20 | let throwsIfKeyMissing: Bool
21 | let throwsIfNull: Bool
22 | let throwsFromDecodeClosure: Bool
23 | }
24 |
25 |
26 | let fileManager = FileManager.default
27 | let documentationTemplate = try String(contentsOfFile: fileManager.currentDirectoryPath + "/Templates/Documentation.swift")
28 |
29 | func documentationFromTemplate(path: String, throwsIf: String, returns: String) -> String {
30 | return documentationTemplate
31 | .replacingOccurrences(of: "{path}", with: path)
32 | .replacingOccurrences(of: "{throws}", with: throwsIf)
33 | .replacingOccurrences(of: "{returns}", with: returns)
34 | }
35 |
36 | func generateDocumentationComment(_ behaviour: Behaviour) -> String {
37 | switch (behaviour.throwsIfKeyMissing, behaviour.throwsIfNull) {
38 | case (true, true):
39 | return documentationFromTemplate(
40 | path: "`KeyPath`– can be appended using with `=>` or `=>?`",
41 | throwsIf: "`DecodingError.typeMismatchError`,`.other(error, metadata)` or possible `.missingKeyError` on required keys",
42 | returns: "something"
43 | )
44 | case (true, false):
45 | return documentationFromTemplate(
46 | path: "`KeyPath`– can be appended using with `=>` or `=>?`",
47 | throwsIf: "`DecodingError` if a key is missing or decoding fails.",
48 | returns: "`nil` if the object at `path` is `NSNull`"
49 | )
50 | case (false, false):
51 | return documentationFromTemplate(
52 | path: "`KeyPath`– can be appended using with `=>` or `=>?`",
53 | throwsIf: "`DecodingError.typeMismatch, `.other(error, metadata)` or possible `.missingKeyError` on required keys",
54 | returns: "`nil` if the object at `path` is `NSNull` or if any optional key is missing."
55 | )
56 | case (false, true):
57 | fatalError("This case should never happen, right?")
58 | }
59 | }
60 |
61 | // ----------------------------------------------------------------------------------------
62 | // MARK:
63 | // ----------------------------------------------------------------------------------------
64 |
65 |
66 | class TypeNameProvider {
67 | var names = Array(["A", "B", "C", "D", "E", "F", "G"].reversed())
68 | var takenNames: [Unique: String] = [:]
69 | subscript(key: Unique) -> String {
70 | if let name = takenNames[key] {
71 | return name
72 | }
73 |
74 | let n = names.popLast()!
75 | takenNames[key] = n
76 | return n
77 | }
78 |
79 | }
80 |
81 | struct Unique: Hashable, Equatable {
82 | static var counter = 0
83 | let value: Int
84 | init() {
85 | Unique.counter += 1
86 | value = Unique.counter
87 | }
88 | var hashValue: Int {
89 | return value.hashValue
90 | }
91 | }
92 |
93 | func == (a: Unique, b: Unique) -> Bool {
94 | return a.value == b.value
95 | }
96 |
97 | indirect enum Decodable {
98 | case T(Unique)
99 | // case AnyObject
100 | case Array(Decodable)
101 | case Optional(Decodable)
102 | case Dictionary(Decodable, Decodable)
103 |
104 | func decodeClosure(_ provider: TypeNameProvider) -> String {
105 | switch self {
106 | case .T(let key):
107 | return "\(provider[key]).decode"
108 | // case .AnyObject:
109 | // return "{$0}"
110 | case .Optional(let T):
111 | return "Optional.decoder(\(T.decodeClosure(provider)))"
112 | case .Array(let T):
113 | return "Array.decoder(\(T.decodeClosure(provider)))"
114 | case .Dictionary(let K, let T):
115 | return "Dictionary.decoder(key: \(K.decodeClosure(provider)), value: \(T.decodeClosure(provider)))"
116 | }
117 | }
118 |
119 | func typeString(_ provider: TypeNameProvider) -> String {
120 | switch self {
121 | case .T(let unique):
122 | return provider[unique]
123 | case .Optional(let T):
124 | return "\(T.typeString(provider))?"
125 | case .Array(let T):
126 | return "[\(T.typeString(provider))]"
127 | case .Dictionary(let K, let T):
128 | return "[\(K.typeString(provider)): \(T.typeString(provider))]"
129 | }
130 | }
131 |
132 | func generateAllPossibleChildren(_ deepness: Int) -> [Decodable] {
133 | guard deepness > 0 else { return [.T(Unique())] }
134 |
135 | var array = [Decodable]()
136 | array += generateAllPossibleChildren(deepness - 1).flatMap(filterChainedOptionals)
137 | array += generateAllPossibleChildren(deepness - 1).map { .Array($0) }
138 | array += generateAllPossibleChildren(deepness - 1).map { .Dictionary(.T(Unique()),$0) }
139 | array += [.T(Unique())]
140 | return array
141 | }
142 |
143 | func wrapInOptionalIfNeeded() -> Decodable {
144 | switch self {
145 | case .Optional:
146 | return self
147 | default:
148 | return .Optional(self)
149 | }
150 | }
151 |
152 | var isOptional: Bool {
153 | switch self {
154 | case .Optional:
155 | return true
156 | default:
157 | return false
158 | }
159 | }
160 |
161 | func generateOverloads(_ operatorString: String) -> [String] {
162 | let provider = TypeNameProvider()
163 | let behaviour: Behaviour
164 | let keyPathType: String
165 |
166 | let returnType = typeString(provider)
167 | let overloads = [String]()
168 |
169 | let arguments = provider.takenNames.values.sorted().map { $0 + ": Decodable" }
170 | let generics = arguments.count > 0 ? "<\(arguments.joined(separator: ", "))>" : ""
171 |
172 | switch operatorString {
173 | case "=>":
174 | behaviour = Behaviour(throwsIfKeyMissing: true, throwsIfNull: !isOptional, throwsFromDecodeClosure: true)
175 | keyPathType = "KeyPath"
176 |
177 | /*
178 | // Start again
179 | guard isOptional else { break }
180 | let otherBehaviour = Behaviour(throwsIfKeyMissing: false, throwsIfNull: !isOptional, throwsFromDecodeClosure: true)
181 | let documentation = generateDocumentationComment(otherBehaviour)
182 | overloads.append(documentation + "public func \(operatorString) \(generics)(json: AnyObject, keyPath: OptionalKeyPath) throws -> \(returnType) {\n" +
183 | " return try parse(json, keyPath: keyPath.markingFirst(required: true), decode: \(decodeClosure(provider)))\n" +
184 | "}"
185 | )
186 | */
187 |
188 | case "=>?":
189 | //returnType += "?"
190 | // Never trows if null
191 | behaviour = Behaviour(throwsIfKeyMissing: false, throwsIfNull: false, throwsFromDecodeClosure: true)
192 | keyPathType = "OptionalKeyPath"
193 | default:
194 | fatalError()
195 | }
196 |
197 | let documentation = generateDocumentationComment(behaviour)
198 | return overloads + [documentation + "public func \(operatorString) \(generics)(json: Any, keyPath: \(keyPathType)) throws -> \(returnType) {\n" +
199 | " return try parse(json, keyPath: keyPath, decoder: \(decodeClosure(provider)))\n" +
200 | "}"
201 | ]
202 | }
203 | }
204 |
205 | func filterChainedOptionals(type: Decodable) -> Decodable? {
206 | switch type {
207 | case .Optional:
208 | return nil
209 | default:
210 | return .Optional(type)
211 | }
212 | }
213 |
214 | func filterOptionals(type: Decodable) -> Decodable? {
215 | switch type {
216 | case .Optional:
217 | return nil
218 | default:
219 | return type
220 | }
221 | }
222 |
223 |
224 | let file = "Overloads.swift"
225 | let sourcesDirectory = fileManager.currentDirectoryPath + "/../Sources"
226 |
227 |
228 | let filename = "Overloads.swift"
229 | let path = sourcesDirectory + "/" + filename
230 |
231 | var dateFormatter = DateFormatter()
232 | dateFormatter.dateStyle = .short
233 |
234 | let date = dateFormatter.string(from: Date())
235 |
236 | let overloads = Decodable.T(Unique()).generateAllPossibleChildren(4)
237 | let types = overloads.map { $0.typeString(TypeNameProvider()) }
238 | let all = overloads.flatMap { $0.generateOverloads("=>") } + overloads.flatMap(filterOptionals).map{ $0.wrapInOptionalIfNeeded() }.flatMap { $0.generateOverloads("=>?") }
239 |
240 | do {
241 | var template = try String(contentsOfFile: fileManager.currentDirectoryPath + "/Templates/Header.swift")
242 | template = template.replacingOccurrences(of: "{filename}", with: filename)
243 | template = template.replacingOccurrences(of: "{by}", with: "Generator.swift")
244 | template = template.replacingOccurrences(of: "{overloads}", with: types.joined(separator: ", "))
245 | template = template.replacingOccurrences(of: "{count}", with: "\(all.count)")
246 | let text = (template as String) + "\n" + all.joined(separator: "\n\n")
247 | try text.write(toFile: sourcesDirectory + "/Overloads.swift", atomically: false, encoding: String.Encoding.utf8)
248 | }
249 | catch {
250 | print(error)
251 | }
252 |
--------------------------------------------------------------------------------
/Tests/DecodableOperatorsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DecodableOperatorsTests.swift
3 | // Decodable
4 | //
5 | // Created by FJBelchi on 13/07/15.
6 | // Copyright © 2015 anviking. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import protocol Decodable.Decodable
11 | import enum Decodable.DecodingError
12 | @testable import Decodable
13 |
14 | class DecodableOperatorsTests: XCTestCase {
15 |
16 | // MARK: =>
17 |
18 | func testDecodeAnyDecodableSuccess() {
19 | // given
20 | let key = "key"
21 | let value = "value"
22 | let dictionary: NSDictionary = [key: value]
23 | // when
24 | let string = try! dictionary => KeyPath(key) as String
25 | // then
26 | XCTAssertEqual(string, value)
27 | }
28 |
29 | func testDecodeAnyDecodableDictionarySuccess() {
30 | // given
31 | let key = "key"
32 | let value: NSDictionary = [key : "value"]
33 | let dictionary: NSDictionary = [key: value]
34 | // when
35 | let result: NSDictionary = try! dictionary => KeyPath(key) as! NSDictionary
36 | // then
37 | XCTAssertEqual(result, value)
38 | }
39 |
40 | func testDecodeDictOfArraysSucess() {
41 | // given
42 | let key = "key"
43 | let value: NSDictionary = ["list": [1, 2, 3]]
44 | let dictionary: NSDictionary = [key: value]
45 | // when
46 | let result: [String: [Int]] = try! dictionary => KeyPath(key)
47 | // then
48 | XCTAssertEqual(result as NSDictionary, value)
49 | }
50 |
51 | // MARK: - Nested keys
52 |
53 | func testDecodeNestedDictionarySuccess() {
54 | // given
55 | let value: NSDictionary = ["aKey" : "value"]
56 | let dictionary: NSDictionary = ["key": ["key": value]]
57 | // when
58 | let result = try! dictionary => "key" => "key"
59 | // then
60 | XCTAssertEqual(result as? NSDictionary, value)
61 | }
62 |
63 | func testDecodeNestedDictionaryOptionalSuccess() {
64 | // given
65 | let value: NSDictionary = ["aKey" : "value"]
66 | let dictionary: NSDictionary = ["key": ["key": value]]
67 | // when
68 | let result = try! dictionary => "key" => "key" as! [String : Any]
69 | // then
70 | XCTAssertEqual(result as NSDictionary, value)
71 | }
72 |
73 | // TODO: this does not compile with Swift 3
74 | // func testDecodeNestedIntSuccess() {
75 | // // given
76 | // let value = 4
77 | // let dictionary: NSDictionary = ["key1": ["key2": ["key3": value]]]
78 | // // when
79 | // let result: Int = try! dictionary => "key1" => "key2" => "key3"
80 | // // then
81 | // XCTAssertEqual(result, value)
82 | // }
83 |
84 | func testDecodeNestedDictionaryCastingSuccess() {
85 | // given
86 |
87 | let value: NSDictionary = ["aKey" : "value"]
88 | let dictionary: NSDictionary = ["key": ["key": value]]
89 | // when
90 | let result = try! dictionary => "key" => "key" as! [String: String]
91 | // then
92 | XCTAssertEqual(result, value as! [String : String])
93 | }
94 |
95 | func testDecodeAnyDecodableOptionalSuccess() {
96 | // given
97 | let key = "key"
98 | let value = "value"
99 | let dictionary: NSDictionary = [key: value]
100 | // when
101 | let string = try! dictionary => KeyPath(key) as String?
102 | // then
103 | XCTAssertEqual(string!, value)
104 | }
105 |
106 | func testDecodeAnyDecodableOptionalNilSuccess() {
107 | // given
108 | let key = "key"
109 | let dictionary: NSDictionary = [key: NSNull()]
110 | // when
111 | let string = try! dictionary => KeyPath(key) as String?
112 | // then
113 | XCTAssertNil(string)
114 | }
115 |
116 | func testDecodeAnyDecodableOptionalTypeMismatchFailure() {
117 | // given
118 | let key = "key"
119 | let dictionary: NSDictionary = [key: 2]
120 | // when
121 | do {
122 | let a = try dictionary => KeyPath(key) as String?
123 | print(a as Any)
124 | XCTFail()
125 | } catch let DecodingError.typeMismatch(_, actual, _) {
126 | let typeString = String(describing: actual)
127 | XCTAssertTrue(typeString.contains("Number"), "\(typeString) should contain NSNumber")
128 | } catch let error {
129 | XCTFail("should not throw \(error)")
130 | }
131 | }
132 |
133 | // MARK: - Nested =>? operators
134 |
135 | // Should throw on typemismatch with correct metadata
136 | func testDecodeNestedTypeMismatchFailure() {
137 | let json: NSDictionary = ["user": ["followers": "not_an_integer"]]
138 | do {
139 | let _ : Int? = try json =>? "user" => "followers"
140 | XCTFail("should throw")
141 | } catch let DecodingError.typeMismatch(_, _, metadata) {
142 | XCTAssertEqual(metadata.formattedPath, "user.followers")
143 | } catch {
144 | XCTFail("should not throw \(error)")
145 | }
146 | }
147 |
148 | // Should currently (though really it shoult not) treat all keys as either optional or non-optional
149 | func testDecodeNestedTypeReturnNilForSubobjectMissingKey() {
150 | let json: NSDictionary = ["user": ["something_else": "test"]]
151 | try! XCTAssertEqual(json =>? "user" =>? "followers", Optional.none)
152 | }
153 |
154 | // Sanity check
155 | func testDecodeNestedTypeSuccess() {
156 | let json: NSDictionary = ["user": ["followers": 3]]
157 | try! XCTAssertEqual(json =>? "user" => "followers", 3)
158 | }
159 |
160 |
161 | // MARK: => Errors
162 |
163 | /* //
164 | func testDecodeNestedDictionaryCastingFailure() {
165 | // given
166 | let value: NSDictionary = ["apple" : 2]
167 | let dictionary: NSDictionary = ["firstKey": ["secondKey": value]]
168 | // when
169 | do {
170 | _ = try dictionary => "firstKey" => "secondKey" as [String: String]
171 | XCTFail()
172 | } catch DecodingError.typeMismatchError(_, Dictionary.self, let info) {
173 | // then
174 | XCTAssertEqual(info.formattedPath, "firstKey.secondKey")
175 | XCTAssertEqual(info.object as? NSDictionary, value)
176 | } catch let error {
177 | XCTFail("should not throw \(error)")
178 | }
179 | }
180 | */
181 |
182 | func testDecodeAnyDecodableThrowMissingKeyException() {
183 | // given
184 | let key = "key"
185 | let value = "value"
186 | let dictionary: NSDictionary = [key: value]
187 | // when
188 | do {
189 | _ = try dictionary => "nokey" as String
190 | } catch let DecodingError.missingKey(key, metadata) {
191 | // then
192 | XCTAssertEqual(key, "nokey")
193 | XCTAssertEqual(metadata.path, [])
194 | XCTAssertEqual(metadata.formattedPath, "")
195 | XCTAssertEqual(metadata.object as? NSDictionary, dictionary)
196 | } catch let error {
197 | XCTFail("should not throw \(error)")
198 | }
199 | }
200 |
201 | func testDecodeAnyDecodableThrowNoJsonObjectException() {
202 | // given
203 | let key = "key"
204 | let noDictionary: NSString = "hello"
205 | // when
206 | do {
207 | _ = try noDictionary => KeyPath(key) as String
208 | } catch let DecodingError.typeMismatch(expected, actual, metadata) where expected == NSDictionary.self {
209 | // then
210 | XCTAssertTrue(true)
211 | XCTAssertTrue(String(describing: actual).contains("String"))
212 | XCTAssertEqual(metadata.formattedPath, "")
213 | XCTAssertEqual(metadata.object as? NSString, (noDictionary))
214 | } catch let error {
215 | XCTFail("should not throw \(error)")
216 | }
217 | }
218 |
219 | func testDecodeAnyDecodableDictionaryThrowMissingKeyException() {
220 | // given
221 | let key = "key"
222 | let value: NSDictionary = [key : "value"]
223 | let dictionary: NSDictionary = [key: value]
224 | // when
225 | do {
226 | _ = try dictionary => "nokey"
227 | } catch let DecodingError.missingKey(key, metadata) {
228 | // then
229 | XCTAssertEqual(key, "nokey")
230 | XCTAssertEqual(metadata.formattedPath, "")
231 | XCTAssertEqual(metadata.path, [])
232 | XCTAssertEqual(metadata.object as? NSDictionary, dictionary)
233 | } catch let error {
234 | XCTFail("should not throw \(error)")
235 | }
236 | }
237 |
238 | func testDecodeAnyDecodableDictionaryThrowJSONNotObjectException() {
239 | // given
240 | let key = "key"
241 | let noDictionary: NSString = "noDictionary"
242 | // when
243 | do {
244 | _ = try noDictionary => KeyPath(key)
245 | } catch let DecodingError.typeMismatch(expected, actual, metadata) where expected == NSDictionary.self {
246 | // then
247 | XCTAssertTrue(true)
248 | XCTAssertEqual(String(describing: actual), "__NSCFString")
249 | XCTAssertEqual(metadata.formattedPath, "")
250 | XCTAssertEqual(metadata.object as? NSString, noDictionary)
251 | } catch let error {
252 | XCTFail("should not throw \(error)")
253 | }
254 | }
255 |
256 | func testDecodeAnyDecodableDictionaryThrowTypeMismatchException() {
257 | // given
258 | let key = "key"
259 | let dictionary: NSDictionary = [key: "value"]
260 | // when
261 | do {
262 | _ = try dictionary => KeyPath(key)
263 | } catch DecodingError.typeMismatch {
264 | // then
265 | XCTAssertTrue(true)
266 | } catch let error {
267 | XCTFail("should not throw \(error)")
268 | }
269 | }
270 | }
271 |
--------------------------------------------------------------------------------
/Decodable.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 17FB81011B530FED0012F106 /* Decodable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17FB80F71B530FED0012F106 /* Decodable.framework */; };
11 | 17FB810E1B5311840012F106 /* Decodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE7B57C1B4CA01400837609 /* Decodable.swift */; };
12 | 17FB810F1B5311870012F106 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F956D1E1B4D6FF700243072 /* Operators.swift */; };
13 | 57FCDE5B1BA283C900130C48 /* DecodingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F87BCC31B592F0E00E53A8C /* DecodingError.swift */; };
14 | 57FCDE5C1BA283C900130C48 /* Parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FFAB8111B7CFA9500E2D724 /* Parse.swift */; };
15 | 57FCDE5D1BA283C900130C48 /* Decodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE7B57C1B4CA01400837609 /* Decodable.swift */; };
16 | 57FCDE5E1BA283C900130C48 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F956D1E1B4D6FF700243072 /* Operators.swift */; };
17 | 651A8C971C29AC5F00DE4D53 /* RawRepresentableDecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651A8C961C29AC5E00DE4D53 /* RawRepresentableDecodableTests.swift */; };
18 | 651A8C981C29AC5F00DE4D53 /* RawRepresentableDecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651A8C961C29AC5E00DE4D53 /* RawRepresentableDecodableTests.swift */; };
19 | 65DB18B01C29AC0E003BDA5C /* RawRepresentableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DB18AF1C29AC0E003BDA5C /* RawRepresentableDecodable.swift */; };
20 | 65DB18B11C29AC0E003BDA5C /* RawRepresentableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DB18AF1C29AC0E003BDA5C /* RawRepresentableDecodable.swift */; };
21 | 65DB18B21C29AC0E003BDA5C /* RawRepresentableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DB18AF1C29AC0E003BDA5C /* RawRepresentableDecodable.swift */; };
22 | 65DB18B31C29AC0E003BDA5C /* RawRepresentableDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DB18AF1C29AC0E003BDA5C /* RawRepresentableDecodable.swift */; };
23 | 8F00623F1C81EF61007BCF48 /* Overloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F00623E1C81EF61007BCF48 /* Overloads.swift */; };
24 | 8F0062401C81EF61007BCF48 /* Overloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F00623E1C81EF61007BCF48 /* Overloads.swift */; };
25 | 8F0062411C81EF61007BCF48 /* Overloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F00623E1C81EF61007BCF48 /* Overloads.swift */; };
26 | 8F0062421C81EF61007BCF48 /* Overloads.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F00623E1C81EF61007BCF48 /* Overloads.swift */; };
27 | 8F012EF51BB5A920007D0B5C /* Castable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F012EF41BB5A920007D0B5C /* Castable.swift */; };
28 | 8F012EF61BB5A920007D0B5C /* Castable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F012EF41BB5A920007D0B5C /* Castable.swift */; };
29 | 8F012EF71BB5A920007D0B5C /* Castable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F012EF41BB5A920007D0B5C /* Castable.swift */; };
30 | 8F012EF81BB5A928007D0B5C /* Castable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F012EF41BB5A920007D0B5C /* Castable.swift */; };
31 | 8F3E459A1D31362B00FB71FC /* OptionalKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E45991D31362B00FB71FC /* OptionalKeyPath.swift */; };
32 | 8F3E459B1D31362B00FB71FC /* OptionalKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E45991D31362B00FB71FC /* OptionalKeyPath.swift */; };
33 | 8F3E459C1D31362B00FB71FC /* OptionalKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E45991D31362B00FB71FC /* OptionalKeyPath.swift */; };
34 | 8F3E459D1D31362B00FB71FC /* OptionalKeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E45991D31362B00FB71FC /* OptionalKeyPath.swift */; };
35 | 8F3E459F1D313A7000FB71FC /* KeyPathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E459E1D313A7000FB71FC /* KeyPathTests.swift */; };
36 | 8F3E45A01D313A7000FB71FC /* KeyPathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E459E1D313A7000FB71FC /* KeyPathTests.swift */; };
37 | 8F3E45A41D327E4500FB71FC /* Decoders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E45A31D327E4500FB71FC /* Decoders.swift */; };
38 | 8F3E45A51D327E4500FB71FC /* Decoders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E45A31D327E4500FB71FC /* Decoders.swift */; };
39 | 8F3E45A61D327E4500FB71FC /* Decoders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E45A31D327E4500FB71FC /* Decoders.swift */; };
40 | 8F3E45A71D327E4500FB71FC /* Decoders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E45A31D327E4500FB71FC /* Decoders.swift */; };
41 | 8F3E45B81D32884700FB71FC /* Documentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E45AE1D32853F00FB71FC /* Documentation.swift */; };
42 | 8F4453EF1D369FF200C19099 /* ParseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F4453EE1D369FF200C19099 /* ParseTests.swift */; };
43 | 8F4453F01D369FF200C19099 /* ParseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F4453EE1D369FF200C19099 /* ParseTests.swift */; };
44 | 8F4B52651B5BAA5700FDCBA7 /* ArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F4B52641B5BAA5700FDCBA7 /* ArrayTests.swift */; };
45 | 8F4B52661B5BAA5700FDCBA7 /* ArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F4B52641B5BAA5700FDCBA7 /* ArrayTests.swift */; };
46 | 8F53521D1BE4112900E3563A /* DictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F53521C1BE4112900E3563A /* DictionaryTests.swift */; };
47 | 8F53521E1BE4112900E3563A /* DictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F53521C1BE4112900E3563A /* DictionaryTests.swift */; };
48 | 8F6FCF5F1D4B39FC00838CE4 /* DynamicDecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F6FCF5E1D4B39FC00838CE4 /* DynamicDecodableTests.swift */; };
49 | 8F6FCF601D4B39FC00838CE4 /* DynamicDecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F6FCF5E1D4B39FC00838CE4 /* DynamicDecodableTests.swift */; };
50 | 8F72DC561C3CB8C500A39E10 /* NSValueCastable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F72DC551C3CB8C500A39E10 /* NSValueCastable.swift */; };
51 | 8F72DC571C3CB8C800A39E10 /* NSValueCastable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F72DC551C3CB8C500A39E10 /* NSValueCastable.swift */; };
52 | 8F72DC581C3CB8C900A39E10 /* NSValueCastable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F72DC551C3CB8C500A39E10 /* NSValueCastable.swift */; };
53 | 8F72DC591C3CB8C900A39E10 /* NSValueCastable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F72DC551C3CB8C500A39E10 /* NSValueCastable.swift */; };
54 | 8F84E70F1C3CB92D001EA4CE /* NSValueDecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F84E70E1C3CB92D001EA4CE /* NSValueDecodableTests.swift */; };
55 | 8F84E7101C3CB92D001EA4CE /* NSValueDecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F84E70E1C3CB92D001EA4CE /* NSValueDecodableTests.swift */; };
56 | 8F85E7781E13DA16000D6989 /* NSNullTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F85E7771E13DA16000D6989 /* NSNullTests.swift */; };
57 | 8F85E7791E13DA16000D6989 /* NSNullTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F85E7771E13DA16000D6989 /* NSNullTests.swift */; };
58 | 8F87BCBB1B580CE200E53A8C /* ErrorPathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F87BCBA1B580CE200E53A8C /* ErrorPathTests.swift */; };
59 | 8F87BCBC1B580CE200E53A8C /* ErrorPathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F87BCBA1B580CE200E53A8C /* ErrorPathTests.swift */; };
60 | 8F87BCC41B592F0E00E53A8C /* DecodingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F87BCC31B592F0E00E53A8C /* DecodingError.swift */; };
61 | 8F87BCC51B592F0E00E53A8C /* DecodingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F87BCC31B592F0E00E53A8C /* DecodingError.swift */; };
62 | 8F956D1F1B4D6FF700243072 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F956D1E1B4D6FF700243072 /* Operators.swift */; };
63 | 8FA733591D328D13003A90A7 /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F3E45AF1D32853F00FB71FC /* Header.swift */; };
64 | 8FB48ECA1D306C4700BC50A1 /* KeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FB48EC91D306C4700BC50A1 /* KeyPath.swift */; };
65 | 8FB48ECB1D306C4700BC50A1 /* KeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FB48EC91D306C4700BC50A1 /* KeyPath.swift */; };
66 | 8FB48ECC1D306C4700BC50A1 /* KeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FB48EC91D306C4700BC50A1 /* KeyPath.swift */; };
67 | 8FB48ECD1D306C4700BC50A1 /* KeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FB48EC91D306C4700BC50A1 /* KeyPath.swift */; };
68 | 8FD3D92F1C270A2D00D1AF4E /* MissingKeyOperatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FD3D92E1C270A2D00D1AF4E /* MissingKeyOperatorTests.swift */; };
69 | 8FD3D9301C270A2D00D1AF4E /* MissingKeyOperatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FD3D92E1C270A2D00D1AF4E /* MissingKeyOperatorTests.swift */; };
70 | 8FE7B5661B4C9FB900837609 /* Decodable.h in Headers */ = {isa = PBXBuildFile; fileRef = 8FE7B5651B4C9FB900837609 /* Decodable.h */; settings = {ATTRIBUTES = (Public, ); }; };
71 | 8FE7B56D1B4C9FB900837609 /* Decodable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8FE7B5621B4C9FB900837609 /* Decodable.framework */; };
72 | 8FE7B5721B4C9FB900837609 /* DecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE7B5711B4C9FB900837609 /* DecodableTests.swift */; };
73 | 8FE7B57E1B4CA01400837609 /* Decodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE7B57C1B4CA01400837609 /* Decodable.swift */; };
74 | 8FFAB8121B7CFA9500E2D724 /* Parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FFAB8111B7CFA9500E2D724 /* Parse.swift */; };
75 | 8FFAB8131B7CFA9500E2D724 /* Parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FFAB8111B7CFA9500E2D724 /* Parse.swift */; };
76 | 8FFAB8141B7CFA9500E2D724 /* Parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FFAB8111B7CFA9500E2D724 /* Parse.swift */; };
77 | 9E2DA75C1CBC77AB00CAF3DF /* DecodeAsOneOfTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DA75B1CBC77AB00CAF3DF /* DecodeAsOneOfTests.swift */; };
78 | 9E2DA75D1CBC77AB00CAF3DF /* DecodeAsOneOfTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DA75B1CBC77AB00CAF3DF /* DecodeAsOneOfTests.swift */; };
79 | 9E2DA75F1CBC784700CAF3DF /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DA75E1CBC784700CAF3DF /* Vehicle.swift */; };
80 | 9E2DA7601CBC784700CAF3DF /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DA75E1CBC784700CAF3DF /* Vehicle.swift */; };
81 | 9E2DA7651CBC802200CAF3DF /* Vehicle.json in Resources */ = {isa = PBXBuildFile; fileRef = 9E2DA7641CBC802200CAF3DF /* Vehicle.json */; };
82 | 9E2DA7661CBC802200CAF3DF /* Vehicle.json in Resources */ = {isa = PBXBuildFile; fileRef = 9E2DA7641CBC802200CAF3DF /* Vehicle.json */; };
83 | D0DC54771B78150900F79CB0 /* Decodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE7B57C1B4CA01400837609 /* Decodable.swift */; };
84 | D0DC54781B78150900F79CB0 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F956D1E1B4D6FF700243072 /* Operators.swift */; };
85 | D0DC547A1B78150900F79CB0 /* DecodingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F87BCC31B592F0E00E53A8C /* DecodingError.swift */; };
86 | FF0060981B5453C600D8CB77 /* DecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FE7B5711B4C9FB900837609 /* DecodableTests.swift */; };
87 | FF00609E1B5454F400D8CB77 /* DecodableExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF00609D1B5454F400D8CB77 /* DecodableExtensionTests.swift */; };
88 | FF00609F1B5454F400D8CB77 /* DecodableExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF00609D1B5454F400D8CB77 /* DecodableExtensionTests.swift */; };
89 | FF0060A41B5464FD00D8CB77 /* MissingKey.json in Resources */ = {isa = PBXBuildFile; fileRef = FF0060A01B5464FD00D8CB77 /* MissingKey.json */; };
90 | FF0060A51B5464FD00D8CB77 /* MissingKey.json in Resources */ = {isa = PBXBuildFile; fileRef = FF0060A01B5464FD00D8CB77 /* MissingKey.json */; };
91 | FF0060A61B5464FD00D8CB77 /* NoJsonObject.json in Resources */ = {isa = PBXBuildFile; fileRef = FF0060A11B5464FD00D8CB77 /* NoJsonObject.json */; };
92 | FF0060A71B5464FD00D8CB77 /* NoJsonObject.json in Resources */ = {isa = PBXBuildFile; fileRef = FF0060A11B5464FD00D8CB77 /* NoJsonObject.json */; };
93 | FF0060A81B5464FD00D8CB77 /* Repository.json in Resources */ = {isa = PBXBuildFile; fileRef = FF0060A21B5464FD00D8CB77 /* Repository.json */; };
94 | FF0060A91B5464FD00D8CB77 /* Repository.json in Resources */ = {isa = PBXBuildFile; fileRef = FF0060A21B5464FD00D8CB77 /* Repository.json */; };
95 | FF0060AA1B5464FD00D8CB77 /* TypeMismatch.json in Resources */ = {isa = PBXBuildFile; fileRef = FF0060A31B5464FD00D8CB77 /* TypeMismatch.json */; };
96 | FF0060AB1B5464FD00D8CB77 /* TypeMismatch.json in Resources */ = {isa = PBXBuildFile; fileRef = FF0060A31B5464FD00D8CB77 /* TypeMismatch.json */; };
97 | FF0060B01B546FB100D8CB77 /* DecodableOperatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0060AF1B546FB100D8CB77 /* DecodableOperatorsTests.swift */; };
98 | FF0060B11B546FB100D8CB77 /* DecodableOperatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF0060AF1B546FB100D8CB77 /* DecodableOperatorsTests.swift */; };
99 | FFE77E211B5396FB00E52F28 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE77E1E1B5391AD00E52F28 /* Repository.swift */; };
100 | FFE77E221B5396FC00E52F28 /* Repository.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFE77E1E1B5391AD00E52F28 /* Repository.swift */; };
101 | /* End PBXBuildFile section */
102 |
103 | /* Begin PBXContainerItemProxy section */
104 | 17FB81021B530FED0012F106 /* PBXContainerItemProxy */ = {
105 | isa = PBXContainerItemProxy;
106 | containerPortal = 8FE7B5591B4C9FB900837609 /* Project object */;
107 | proxyType = 1;
108 | remoteGlobalIDString = 17FB80F61B530FED0012F106;
109 | remoteInfo = "Decodable-Mac";
110 | };
111 | 8FE7B56E1B4C9FB900837609 /* PBXContainerItemProxy */ = {
112 | isa = PBXContainerItemProxy;
113 | containerPortal = 8FE7B5591B4C9FB900837609 /* Project object */;
114 | proxyType = 1;
115 | remoteGlobalIDString = 8FE7B5611B4C9FB900837609;
116 | remoteInfo = Decodable;
117 | };
118 | /* End PBXContainerItemProxy section */
119 |
120 | /* Begin PBXFileReference section */
121 | 17FB80F71B530FED0012F106 /* Decodable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Decodable.framework; sourceTree = BUILT_PRODUCTS_DIR; };
122 | 17FB81001B530FED0012F106 /* DecodableTests-Mac.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "DecodableTests-Mac.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
123 | 57FCDE651BA283C900130C48 /* Decodable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Decodable.framework; sourceTree = BUILT_PRODUCTS_DIR; };
124 | 651A8C961C29AC5E00DE4D53 /* RawRepresentableDecodableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawRepresentableDecodableTests.swift; sourceTree = ""; };
125 | 65DB18AF1C29AC0E003BDA5C /* RawRepresentableDecodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawRepresentableDecodable.swift; sourceTree = ""; };
126 | 8F00623E1C81EF61007BCF48 /* Overloads.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Overloads.swift; sourceTree = ""; };
127 | 8F0062481C81F4A2007BCF48 /* Generator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generator.swift; sourceTree = ""; };
128 | 8F012EEF1BB414D4007D0B5C /* Playground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Playground.playground; sourceTree = ""; };
129 | 8F012EF41BB5A920007D0B5C /* Castable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Castable.swift; sourceTree = ""; };
130 | 8F3E45991D31362B00FB71FC /* OptionalKeyPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptionalKeyPath.swift; sourceTree = ""; };
131 | 8F3E459E1D313A7000FB71FC /* KeyPathTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyPathTests.swift; sourceTree = ""; };
132 | 8F3E45A31D327E4500FB71FC /* Decoders.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Decoders.swift; sourceTree = ""; };
133 | 8F3E45AE1D32853F00FB71FC /* Documentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Documentation.swift; sourceTree = ""; };
134 | 8F3E45AF1D32853F00FB71FC /* Header.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Header.swift; sourceTree = ""; };
135 | 8F4453EE1D369FF200C19099 /* ParseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseTests.swift; sourceTree = ""; };
136 | 8F4B52641B5BAA5700FDCBA7 /* ArrayTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrayTests.swift; sourceTree = ""; };
137 | 8F53521C1BE4112900E3563A /* DictionaryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DictionaryTests.swift; sourceTree = ""; };
138 | 8F6FCF5E1D4B39FC00838CE4 /* DynamicDecodableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicDecodableTests.swift; sourceTree = ""; };
139 | 8F72DC551C3CB8C500A39E10 /* NSValueCastable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSValueCastable.swift; sourceTree = ""; };
140 | 8F84E70E1C3CB92D001EA4CE /* NSValueDecodableTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSValueDecodableTests.swift; sourceTree = ""; };
141 | 8F85E7771E13DA16000D6989 /* NSNullTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSNullTests.swift; sourceTree = ""; };
142 | 8F87BCBA1B580CE200E53A8C /* ErrorPathTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorPathTests.swift; sourceTree = ""; };
143 | 8F87BCC31B592F0E00E53A8C /* DecodingError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecodingError.swift; sourceTree = ""; };
144 | 8F956D1E1B4D6FF700243072 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; };
145 | 8FB48EC91D306C4700BC50A1 /* KeyPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyPath.swift; sourceTree = ""; };
146 | 8FD3D92E1C270A2D00D1AF4E /* MissingKeyOperatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MissingKeyOperatorTests.swift; sourceTree = ""; };
147 | 8FE7B5621B4C9FB900837609 /* Decodable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Decodable.framework; sourceTree = BUILT_PRODUCTS_DIR; };
148 | 8FE7B5651B4C9FB900837609 /* Decodable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Decodable.h; sourceTree = ""; };
149 | 8FE7B5671B4C9FB900837609 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
150 | 8FE7B56C1B4C9FB900837609 /* DecodableTests-iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "DecodableTests-iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
151 | 8FE7B5711B4C9FB900837609 /* DecodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodableTests.swift; sourceTree = ""; };
152 | 8FE7B5731B4C9FB900837609 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
153 | 8FE7B57C1B4CA01400837609 /* Decodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Decodable.swift; sourceTree = ""; };
154 | 8FFAB8111B7CFA9500E2D724 /* Parse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Parse.swift; sourceTree = ""; };
155 | 9E2DA75B1CBC77AB00CAF3DF /* DecodeAsOneOfTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecodeAsOneOfTests.swift; sourceTree = ""; };
156 | 9E2DA75E1CBC784700CAF3DF /* Vehicle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = ""; };
157 | 9E2DA7641CBC802200CAF3DF /* Vehicle.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = Vehicle.json; path = JSONExamples/Vehicle.json; sourceTree = ""; };
158 | D0DC546F1B7814D200F79CB0 /* Decodable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Decodable.framework; sourceTree = BUILT_PRODUCTS_DIR; };
159 | FF00609D1B5454F400D8CB77 /* DecodableExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecodableExtensionTests.swift; sourceTree = ""; };
160 | FF0060A01B5464FD00D8CB77 /* MissingKey.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = MissingKey.json; path = JSONExamples/MissingKey.json; sourceTree = ""; };
161 | FF0060A11B5464FD00D8CB77 /* NoJsonObject.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = NoJsonObject.json; path = JSONExamples/NoJsonObject.json; sourceTree = ""; };
162 | FF0060A21B5464FD00D8CB77 /* Repository.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = Repository.json; path = JSONExamples/Repository.json; sourceTree = ""; };
163 | FF0060A31B5464FD00D8CB77 /* TypeMismatch.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = TypeMismatch.json; path = JSONExamples/TypeMismatch.json; sourceTree = ""; };
164 | FF0060AF1B546FB100D8CB77 /* DecodableOperatorsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecodableOperatorsTests.swift; sourceTree = ""; };
165 | FFE77E1E1B5391AD00E52F28 /* Repository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Repository.swift; sourceTree = ""; };
166 | /* End PBXFileReference section */
167 |
168 | /* Begin PBXFrameworksBuildPhase section */
169 | 17FB80F31B530FED0012F106 /* Frameworks */ = {
170 | isa = PBXFrameworksBuildPhase;
171 | buildActionMask = 2147483647;
172 | files = (
173 | );
174 | runOnlyForDeploymentPostprocessing = 0;
175 | };
176 | 17FB80FD1B530FED0012F106 /* Frameworks */ = {
177 | isa = PBXFrameworksBuildPhase;
178 | buildActionMask = 2147483647;
179 | files = (
180 | 17FB81011B530FED0012F106 /* Decodable.framework in Frameworks */,
181 | );
182 | runOnlyForDeploymentPostprocessing = 0;
183 | };
184 | 57FCDE5F1BA283C900130C48 /* Frameworks */ = {
185 | isa = PBXFrameworksBuildPhase;
186 | buildActionMask = 2147483647;
187 | files = (
188 | );
189 | runOnlyForDeploymentPostprocessing = 0;
190 | };
191 | 8FE7B55E1B4C9FB900837609 /* Frameworks */ = {
192 | isa = PBXFrameworksBuildPhase;
193 | buildActionMask = 2147483647;
194 | files = (
195 | );
196 | runOnlyForDeploymentPostprocessing = 0;
197 | };
198 | 8FE7B5691B4C9FB900837609 /* Frameworks */ = {
199 | isa = PBXFrameworksBuildPhase;
200 | buildActionMask = 2147483647;
201 | files = (
202 | 8FE7B56D1B4C9FB900837609 /* Decodable.framework in Frameworks */,
203 | );
204 | runOnlyForDeploymentPostprocessing = 0;
205 | };
206 | D0DC546B1B7814D200F79CB0 /* Frameworks */ = {
207 | isa = PBXFrameworksBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | );
211 | runOnlyForDeploymentPostprocessing = 0;
212 | };
213 | /* End PBXFrameworksBuildPhase section */
214 |
215 | /* Begin PBXGroup section */
216 | 8F0062471C81F4A2007BCF48 /* Code Generation */ = {
217 | isa = PBXGroup;
218 | children = (
219 | 8F0062481C81F4A2007BCF48 /* Generator.swift */,
220 | 8F3E45AD1D32853F00FB71FC /* Templates */,
221 | );
222 | name = "Code Generation";
223 | path = Generator;
224 | sourceTree = "";
225 | };
226 | 8F3E45AD1D32853F00FB71FC /* Templates */ = {
227 | isa = PBXGroup;
228 | children = (
229 | 8F3E45AE1D32853F00FB71FC /* Documentation.swift */,
230 | 8F3E45AF1D32853F00FB71FC /* Header.swift */,
231 | );
232 | path = Templates;
233 | sourceTree = "";
234 | };
235 | 8FE7B5581B4C9FB900837609 = {
236 | isa = PBXGroup;
237 | children = (
238 | 8FE7B5641B4C9FB900837609 /* Decodable */,
239 | 8F0062471C81F4A2007BCF48 /* Code Generation */,
240 | 8FE7B5701B4C9FB900837609 /* DecodableTests */,
241 | 8FE7B5631B4C9FB900837609 /* Products */,
242 | );
243 | sourceTree = "";
244 | };
245 | 8FE7B5631B4C9FB900837609 /* Products */ = {
246 | isa = PBXGroup;
247 | children = (
248 | 8FE7B5621B4C9FB900837609 /* Decodable.framework */,
249 | 8FE7B56C1B4C9FB900837609 /* DecodableTests-iOS.xctest */,
250 | 17FB80F71B530FED0012F106 /* Decodable.framework */,
251 | 17FB81001B530FED0012F106 /* DecodableTests-Mac.xctest */,
252 | D0DC546F1B7814D200F79CB0 /* Decodable.framework */,
253 | 57FCDE651BA283C900130C48 /* Decodable.framework */,
254 | );
255 | name = Products;
256 | sourceTree = "";
257 | };
258 | 8FE7B5641B4C9FB900837609 /* Decodable */ = {
259 | isa = PBXGroup;
260 | children = (
261 | 8FE7B5651B4C9FB900837609 /* Decodable.h */,
262 | 8F012EEF1BB414D4007D0B5C /* Playground.playground */,
263 | 65DB18AF1C29AC0E003BDA5C /* RawRepresentableDecodable.swift */,
264 | 8F72DC551C3CB8C500A39E10 /* NSValueCastable.swift */,
265 | 8F3E45A31D327E4500FB71FC /* Decoders.swift */,
266 | 8FB48EC91D306C4700BC50A1 /* KeyPath.swift */,
267 | 8F3E45991D31362B00FB71FC /* OptionalKeyPath.swift */,
268 | 8F956D1E1B4D6FF700243072 /* Operators.swift */,
269 | 8F00623E1C81EF61007BCF48 /* Overloads.swift */,
270 | 8FE7B57C1B4CA01400837609 /* Decodable.swift */,
271 | 8F012EF41BB5A920007D0B5C /* Castable.swift */,
272 | 8FFAB8111B7CFA9500E2D724 /* Parse.swift */,
273 | 8F87BCC31B592F0E00E53A8C /* DecodingError.swift */,
274 | 8FE7B5671B4C9FB900837609 /* Info.plist */,
275 | );
276 | name = Decodable;
277 | path = Sources;
278 | sourceTree = "";
279 | };
280 | 8FE7B5701B4C9FB900837609 /* DecodableTests */ = {
281 | isa = PBXGroup;
282 | children = (
283 | FF0060991B54544A00D8CB77 /* JSONExamples */,
284 | 8FE7B5711B4C9FB900837609 /* DecodableTests.swift */,
285 | 8F4453EE1D369FF200C19099 /* ParseTests.swift */,
286 | 8F6FCF5E1D4B39FC00838CE4 /* DynamicDecodableTests.swift */,
287 | 8F3E459E1D313A7000FB71FC /* KeyPathTests.swift */,
288 | FF00609D1B5454F400D8CB77 /* DecodableExtensionTests.swift */,
289 | 9E2DA75B1CBC77AB00CAF3DF /* DecodeAsOneOfTests.swift */,
290 | 8F84E70E1C3CB92D001EA4CE /* NSValueDecodableTests.swift */,
291 | FF0060AF1B546FB100D8CB77 /* DecodableOperatorsTests.swift */,
292 | 8F85E7771E13DA16000D6989 /* NSNullTests.swift */,
293 | 8FD3D92E1C270A2D00D1AF4E /* MissingKeyOperatorTests.swift */,
294 | 651A8C961C29AC5E00DE4D53 /* RawRepresentableDecodableTests.swift */,
295 | 8F4B52641B5BAA5700FDCBA7 /* ArrayTests.swift */,
296 | 8F53521C1BE4112900E3563A /* DictionaryTests.swift */,
297 | 8F87BCBA1B580CE200E53A8C /* ErrorPathTests.swift */,
298 | FFE77E1E1B5391AD00E52F28 /* Repository.swift */,
299 | 9E2DA75E1CBC784700CAF3DF /* Vehicle.swift */,
300 | 8FE7B5731B4C9FB900837609 /* Info.plist */,
301 | );
302 | name = DecodableTests;
303 | path = Tests;
304 | sourceTree = "";
305 | };
306 | FF0060991B54544A00D8CB77 /* JSONExamples */ = {
307 | isa = PBXGroup;
308 | children = (
309 | FF0060A01B5464FD00D8CB77 /* MissingKey.json */,
310 | FF0060A11B5464FD00D8CB77 /* NoJsonObject.json */,
311 | FF0060A21B5464FD00D8CB77 /* Repository.json */,
312 | FF0060A31B5464FD00D8CB77 /* TypeMismatch.json */,
313 | 9E2DA7641CBC802200CAF3DF /* Vehicle.json */,
314 | );
315 | name = JSONExamples;
316 | sourceTree = "";
317 | };
318 | /* End PBXGroup section */
319 |
320 | /* Begin PBXHeadersBuildPhase section */
321 | 17FB80F41B530FED0012F106 /* Headers */ = {
322 | isa = PBXHeadersBuildPhase;
323 | buildActionMask = 2147483647;
324 | files = (
325 | );
326 | runOnlyForDeploymentPostprocessing = 0;
327 | };
328 | 57FCDE601BA283C900130C48 /* Headers */ = {
329 | isa = PBXHeadersBuildPhase;
330 | buildActionMask = 2147483647;
331 | files = (
332 | );
333 | runOnlyForDeploymentPostprocessing = 0;
334 | };
335 | 8FE7B55F1B4C9FB900837609 /* Headers */ = {
336 | isa = PBXHeadersBuildPhase;
337 | buildActionMask = 2147483647;
338 | files = (
339 | 8FE7B5661B4C9FB900837609 /* Decodable.h in Headers */,
340 | );
341 | runOnlyForDeploymentPostprocessing = 0;
342 | };
343 | D0DC546C1B7814D200F79CB0 /* Headers */ = {
344 | isa = PBXHeadersBuildPhase;
345 | buildActionMask = 2147483647;
346 | files = (
347 | );
348 | runOnlyForDeploymentPostprocessing = 0;
349 | };
350 | /* End PBXHeadersBuildPhase section */
351 |
352 | /* Begin PBXNativeTarget section */
353 | 17FB80F61B530FED0012F106 /* Decodable-Mac */ = {
354 | isa = PBXNativeTarget;
355 | buildConfigurationList = 17FB81081B530FED0012F106 /* Build configuration list for PBXNativeTarget "Decodable-Mac" */;
356 | buildPhases = (
357 | 8F0062451C81F348007BCF48 /* ShellScript */,
358 | 17FB80F21B530FED0012F106 /* Sources */,
359 | 17FB80F31B530FED0012F106 /* Frameworks */,
360 | 17FB80F41B530FED0012F106 /* Headers */,
361 | 17FB80F51B530FED0012F106 /* Resources */,
362 | );
363 | buildRules = (
364 | );
365 | dependencies = (
366 | );
367 | name = "Decodable-Mac";
368 | productName = "Decodable-Mac";
369 | productReference = 17FB80F71B530FED0012F106 /* Decodable.framework */;
370 | productType = "com.apple.product-type.framework";
371 | };
372 | 17FB80FF1B530FED0012F106 /* DecodableTests-Mac */ = {
373 | isa = PBXNativeTarget;
374 | buildConfigurationList = 17FB810B1B530FED0012F106 /* Build configuration list for PBXNativeTarget "DecodableTests-Mac" */;
375 | buildPhases = (
376 | 17FB80FC1B530FED0012F106 /* Sources */,
377 | 17FB80FD1B530FED0012F106 /* Frameworks */,
378 | 17FB80FE1B530FED0012F106 /* Resources */,
379 | );
380 | buildRules = (
381 | );
382 | dependencies = (
383 | 17FB81031B530FED0012F106 /* PBXTargetDependency */,
384 | );
385 | name = "DecodableTests-Mac";
386 | productName = "Decodable-MacTests";
387 | productReference = 17FB81001B530FED0012F106 /* DecodableTests-Mac.xctest */;
388 | productType = "com.apple.product-type.bundle.unit-test";
389 | };
390 | 57FCDE591BA283C900130C48 /* Decodable-tvOS */ = {
391 | isa = PBXNativeTarget;
392 | buildConfigurationList = 57FCDE621BA283C900130C48 /* Build configuration list for PBXNativeTarget "Decodable-tvOS" */;
393 | buildPhases = (
394 | 8F00624B1C81F6F1007BCF48 /* ShellScript */,
395 | 57FCDE5A1BA283C900130C48 /* Sources */,
396 | 57FCDE5F1BA283C900130C48 /* Frameworks */,
397 | 57FCDE601BA283C900130C48 /* Headers */,
398 | 57FCDE611BA283C900130C48 /* Resources */,
399 | );
400 | buildRules = (
401 | );
402 | dependencies = (
403 | );
404 | name = "Decodable-tvOS";
405 | productName = "Decodable-watchOS";
406 | productReference = 57FCDE651BA283C900130C48 /* Decodable.framework */;
407 | productType = "com.apple.product-type.framework";
408 | };
409 | 8FE7B5611B4C9FB900837609 /* Decodable-iOS */ = {
410 | isa = PBXNativeTarget;
411 | buildConfigurationList = 8FE7B5761B4C9FB900837609 /* Build configuration list for PBXNativeTarget "Decodable-iOS" */;
412 | buildPhases = (
413 | 8F0062441C81F26B007BCF48 /* ShellScript */,
414 | 8FE7B55D1B4C9FB900837609 /* Sources */,
415 | 8FE7B55E1B4C9FB900837609 /* Frameworks */,
416 | 8FE7B55F1B4C9FB900837609 /* Headers */,
417 | 8FE7B5601B4C9FB900837609 /* Resources */,
418 | );
419 | buildRules = (
420 | );
421 | dependencies = (
422 | );
423 | name = "Decodable-iOS";
424 | productName = Decodable;
425 | productReference = 8FE7B5621B4C9FB900837609 /* Decodable.framework */;
426 | productType = "com.apple.product-type.framework";
427 | };
428 | 8FE7B56B1B4C9FB900837609 /* DecodableTests-iOS */ = {
429 | isa = PBXNativeTarget;
430 | buildConfigurationList = 8FE7B5791B4C9FB900837609 /* Build configuration list for PBXNativeTarget "DecodableTests-iOS" */;
431 | buildPhases = (
432 | 8FE7B5681B4C9FB900837609 /* Sources */,
433 | 8FE7B5691B4C9FB900837609 /* Frameworks */,
434 | 8FE7B56A1B4C9FB900837609 /* Resources */,
435 | );
436 | buildRules = (
437 | );
438 | dependencies = (
439 | 8FE7B56F1B4C9FB900837609 /* PBXTargetDependency */,
440 | );
441 | name = "DecodableTests-iOS";
442 | productName = DecodableTests;
443 | productReference = 8FE7B56C1B4C9FB900837609 /* DecodableTests-iOS.xctest */;
444 | productType = "com.apple.product-type.bundle.unit-test";
445 | };
446 | D0DC546E1B7814D200F79CB0 /* Decodable-watchOS */ = {
447 | isa = PBXNativeTarget;
448 | buildConfigurationList = D0DC54761B7814D200F79CB0 /* Build configuration list for PBXNativeTarget "Decodable-watchOS" */;
449 | buildPhases = (
450 | 8F0062461C81F350007BCF48 /* ShellScript */,
451 | D0DC546A1B7814D200F79CB0 /* Sources */,
452 | D0DC546B1B7814D200F79CB0 /* Frameworks */,
453 | D0DC546C1B7814D200F79CB0 /* Headers */,
454 | D0DC546D1B7814D200F79CB0 /* Resources */,
455 | );
456 | buildRules = (
457 | );
458 | dependencies = (
459 | );
460 | name = "Decodable-watchOS";
461 | productName = "Decodable-watchOS";
462 | productReference = D0DC546F1B7814D200F79CB0 /* Decodable.framework */;
463 | productType = "com.apple.product-type.framework";
464 | };
465 | /* End PBXNativeTarget section */
466 |
467 | /* Begin PBXProject section */
468 | 8FE7B5591B4C9FB900837609 /* Project object */ = {
469 | isa = PBXProject;
470 | attributes = {
471 | LastSwiftUpdateCheck = 0700;
472 | LastUpgradeCheck = 0900;
473 | ORGANIZATIONNAME = anviking;
474 | TargetAttributes = {
475 | 17FB80F61B530FED0012F106 = {
476 | CreatedOnToolsVersion = 7.0;
477 | };
478 | 17FB80FF1B530FED0012F106 = {
479 | CreatedOnToolsVersion = 7.0;
480 | };
481 | 8FE7B5611B4C9FB900837609 = {
482 | CreatedOnToolsVersion = 7.0;
483 | LastSwiftMigration = 0900;
484 | };
485 | 8FE7B56B1B4C9FB900837609 = {
486 | CreatedOnToolsVersion = 7.0;
487 | LastSwiftMigration = 0900;
488 | };
489 | D0DC546E1B7814D200F79CB0 = {
490 | CreatedOnToolsVersion = 7.0;
491 | LastSwiftMigration = 0800;
492 | };
493 | };
494 | };
495 | buildConfigurationList = 8FE7B55C1B4C9FB900837609 /* Build configuration list for PBXProject "Decodable" */;
496 | compatibilityVersion = "Xcode 3.2";
497 | developmentRegion = English;
498 | hasScannedForEncodings = 0;
499 | knownRegions = (
500 | en,
501 | );
502 | mainGroup = 8FE7B5581B4C9FB900837609;
503 | productRefGroup = 8FE7B5631B4C9FB900837609 /* Products */;
504 | projectDirPath = "";
505 | projectRoot = "";
506 | targets = (
507 | 8FE7B5611B4C9FB900837609 /* Decodable-iOS */,
508 | 8FE7B56B1B4C9FB900837609 /* DecodableTests-iOS */,
509 | 17FB80F61B530FED0012F106 /* Decodable-Mac */,
510 | 17FB80FF1B530FED0012F106 /* DecodableTests-Mac */,
511 | D0DC546E1B7814D200F79CB0 /* Decodable-watchOS */,
512 | 57FCDE591BA283C900130C48 /* Decodable-tvOS */,
513 | );
514 | };
515 | /* End PBXProject section */
516 |
517 | /* Begin PBXResourcesBuildPhase section */
518 | 17FB80F51B530FED0012F106 /* Resources */ = {
519 | isa = PBXResourcesBuildPhase;
520 | buildActionMask = 2147483647;
521 | files = (
522 | );
523 | runOnlyForDeploymentPostprocessing = 0;
524 | };
525 | 17FB80FE1B530FED0012F106 /* Resources */ = {
526 | isa = PBXResourcesBuildPhase;
527 | buildActionMask = 2147483647;
528 | files = (
529 | FF0060A51B5464FD00D8CB77 /* MissingKey.json in Resources */,
530 | FF0060AB1B5464FD00D8CB77 /* TypeMismatch.json in Resources */,
531 | 9E2DA7661CBC802200CAF3DF /* Vehicle.json in Resources */,
532 | FF0060A91B5464FD00D8CB77 /* Repository.json in Resources */,
533 | FF0060A71B5464FD00D8CB77 /* NoJsonObject.json in Resources */,
534 | );
535 | runOnlyForDeploymentPostprocessing = 0;
536 | };
537 | 57FCDE611BA283C900130C48 /* Resources */ = {
538 | isa = PBXResourcesBuildPhase;
539 | buildActionMask = 2147483647;
540 | files = (
541 | );
542 | runOnlyForDeploymentPostprocessing = 0;
543 | };
544 | 8FE7B5601B4C9FB900837609 /* Resources */ = {
545 | isa = PBXResourcesBuildPhase;
546 | buildActionMask = 2147483647;
547 | files = (
548 | );
549 | runOnlyForDeploymentPostprocessing = 0;
550 | };
551 | 8FE7B56A1B4C9FB900837609 /* Resources */ = {
552 | isa = PBXResourcesBuildPhase;
553 | buildActionMask = 2147483647;
554 | files = (
555 | FF0060A41B5464FD00D8CB77 /* MissingKey.json in Resources */,
556 | FF0060AA1B5464FD00D8CB77 /* TypeMismatch.json in Resources */,
557 | 9E2DA7651CBC802200CAF3DF /* Vehicle.json in Resources */,
558 | FF0060A81B5464FD00D8CB77 /* Repository.json in Resources */,
559 | FF0060A61B5464FD00D8CB77 /* NoJsonObject.json in Resources */,
560 | );
561 | runOnlyForDeploymentPostprocessing = 0;
562 | };
563 | D0DC546D1B7814D200F79CB0 /* Resources */ = {
564 | isa = PBXResourcesBuildPhase;
565 | buildActionMask = 2147483647;
566 | files = (
567 | );
568 | runOnlyForDeploymentPostprocessing = 0;
569 | };
570 | /* End PBXResourcesBuildPhase section */
571 |
572 | /* Begin PBXShellScriptBuildPhase section */
573 | 8F0062441C81F26B007BCF48 /* ShellScript */ = {
574 | isa = PBXShellScriptBuildPhase;
575 | buildActionMask = 2147483647;
576 | files = (
577 | );
578 | inputPaths = (
579 | );
580 | outputPaths = (
581 | );
582 | runOnlyForDeploymentPostprocessing = 0;
583 | shellPath = /bin/sh;
584 | shellScript = "if [ -z \"$CI\" ]; then\n cd \"${SRCROOT}/Generator\"\n ./Generator.swift\nfi";
585 | };
586 | 8F0062451C81F348007BCF48 /* ShellScript */ = {
587 | isa = PBXShellScriptBuildPhase;
588 | buildActionMask = 2147483647;
589 | files = (
590 | );
591 | inputPaths = (
592 | );
593 | outputPaths = (
594 | );
595 | runOnlyForDeploymentPostprocessing = 0;
596 | shellPath = /bin/sh;
597 | shellScript = "if [ -z \"$CI\" ]; then\n cd \"${SRCROOT}/Generator\"\n ./Generator.swift\nfi";
598 | };
599 | 8F0062461C81F350007BCF48 /* ShellScript */ = {
600 | isa = PBXShellScriptBuildPhase;
601 | buildActionMask = 2147483647;
602 | files = (
603 | );
604 | inputPaths = (
605 | );
606 | outputPaths = (
607 | );
608 | runOnlyForDeploymentPostprocessing = 0;
609 | shellPath = /bin/sh;
610 | shellScript = "if [ -z \"$CI\" ]; then\n cd \"${SRCROOT}/Generator\"\n ./Generator.swift\nfi";
611 | };
612 | 8F00624B1C81F6F1007BCF48 /* ShellScript */ = {
613 | isa = PBXShellScriptBuildPhase;
614 | buildActionMask = 2147483647;
615 | files = (
616 | );
617 | inputPaths = (
618 | );
619 | outputPaths = (
620 | );
621 | runOnlyForDeploymentPostprocessing = 0;
622 | shellPath = /bin/sh;
623 | shellScript = "if [ -z \"$CI\" ]; then\n cd \"${SRCROOT}/Generator\"\n ./Generator.swift\nfi";
624 | };
625 | /* End PBXShellScriptBuildPhase section */
626 |
627 | /* Begin PBXSourcesBuildPhase section */
628 | 17FB80F21B530FED0012F106 /* Sources */ = {
629 | isa = PBXSourcesBuildPhase;
630 | buildActionMask = 2147483647;
631 | files = (
632 | 8F87BCC51B592F0E00E53A8C /* DecodingError.swift in Sources */,
633 | 8FFAB8131B7CFA9500E2D724 /* Parse.swift in Sources */,
634 | 8F012EF61BB5A920007D0B5C /* Castable.swift in Sources */,
635 | 8FB48ECB1D306C4700BC50A1 /* KeyPath.swift in Sources */,
636 | 17FB810E1B5311840012F106 /* Decodable.swift in Sources */,
637 | 17FB810F1B5311870012F106 /* Operators.swift in Sources */,
638 | 8F3E459B1D31362B00FB71FC /* OptionalKeyPath.swift in Sources */,
639 | 8F0062401C81EF61007BCF48 /* Overloads.swift in Sources */,
640 | 65DB18B11C29AC0E003BDA5C /* RawRepresentableDecodable.swift in Sources */,
641 | 8F72DC571C3CB8C800A39E10 /* NSValueCastable.swift in Sources */,
642 | 8F3E45A51D327E4500FB71FC /* Decoders.swift in Sources */,
643 | );
644 | runOnlyForDeploymentPostprocessing = 0;
645 | };
646 | 17FB80FC1B530FED0012F106 /* Sources */ = {
647 | isa = PBXSourcesBuildPhase;
648 | buildActionMask = 2147483647;
649 | files = (
650 | 9E2DA7601CBC784700CAF3DF /* Vehicle.swift in Sources */,
651 | 8F87BCBC1B580CE200E53A8C /* ErrorPathTests.swift in Sources */,
652 | 8F6FCF601D4B39FC00838CE4 /* DynamicDecodableTests.swift in Sources */,
653 | 651A8C981C29AC5F00DE4D53 /* RawRepresentableDecodableTests.swift in Sources */,
654 | FF00609F1B5454F400D8CB77 /* DecodableExtensionTests.swift in Sources */,
655 | 8FD3D9301C270A2D00D1AF4E /* MissingKeyOperatorTests.swift in Sources */,
656 | 8F53521E1BE4112900E3563A /* DictionaryTests.swift in Sources */,
657 | FF0060B11B546FB100D8CB77 /* DecodableOperatorsTests.swift in Sources */,
658 | 8F4453F01D369FF200C19099 /* ParseTests.swift in Sources */,
659 | 8F84E7101C3CB92D001EA4CE /* NSValueDecodableTests.swift in Sources */,
660 | 8F4B52661B5BAA5700FDCBA7 /* ArrayTests.swift in Sources */,
661 | 8F85E7791E13DA16000D6989 /* NSNullTests.swift in Sources */,
662 | FFE77E211B5396FB00E52F28 /* Repository.swift in Sources */,
663 | FF0060981B5453C600D8CB77 /* DecodableTests.swift in Sources */,
664 | 9E2DA75D1CBC77AB00CAF3DF /* DecodeAsOneOfTests.swift in Sources */,
665 | 8F3E45A01D313A7000FB71FC /* KeyPathTests.swift in Sources */,
666 | );
667 | runOnlyForDeploymentPostprocessing = 0;
668 | };
669 | 57FCDE5A1BA283C900130C48 /* Sources */ = {
670 | isa = PBXSourcesBuildPhase;
671 | buildActionMask = 2147483647;
672 | files = (
673 | 57FCDE5B1BA283C900130C48 /* DecodingError.swift in Sources */,
674 | 57FCDE5C1BA283C900130C48 /* Parse.swift in Sources */,
675 | 8F012EF81BB5A928007D0B5C /* Castable.swift in Sources */,
676 | 8FB48ECD1D306C4700BC50A1 /* KeyPath.swift in Sources */,
677 | 57FCDE5D1BA283C900130C48 /* Decodable.swift in Sources */,
678 | 57FCDE5E1BA283C900130C48 /* Operators.swift in Sources */,
679 | 8F3E459D1D31362B00FB71FC /* OptionalKeyPath.swift in Sources */,
680 | 8F0062421C81EF61007BCF48 /* Overloads.swift in Sources */,
681 | 65DB18B31C29AC0E003BDA5C /* RawRepresentableDecodable.swift in Sources */,
682 | 8F72DC591C3CB8C900A39E10 /* NSValueCastable.swift in Sources */,
683 | 8F3E45A71D327E4500FB71FC /* Decoders.swift in Sources */,
684 | );
685 | runOnlyForDeploymentPostprocessing = 0;
686 | };
687 | 8FE7B55D1B4C9FB900837609 /* Sources */ = {
688 | isa = PBXSourcesBuildPhase;
689 | buildActionMask = 2147483647;
690 | files = (
691 | 8F87BCC41B592F0E00E53A8C /* DecodingError.swift in Sources */,
692 | 8FA733591D328D13003A90A7 /* Header.swift in Sources */,
693 | 8FFAB8121B7CFA9500E2D724 /* Parse.swift in Sources */,
694 | 8F012EF51BB5A920007D0B5C /* Castable.swift in Sources */,
695 | 8FB48ECA1D306C4700BC50A1 /* KeyPath.swift in Sources */,
696 | 8FE7B57E1B4CA01400837609 /* Decodable.swift in Sources */,
697 | 8F72DC561C3CB8C500A39E10 /* NSValueCastable.swift in Sources */,
698 | 8F3E459A1D31362B00FB71FC /* OptionalKeyPath.swift in Sources */,
699 | 8F00623F1C81EF61007BCF48 /* Overloads.swift in Sources */,
700 | 8F3E45B81D32884700FB71FC /* Documentation.swift in Sources */,
701 | 8F956D1F1B4D6FF700243072 /* Operators.swift in Sources */,
702 | 65DB18B01C29AC0E003BDA5C /* RawRepresentableDecodable.swift in Sources */,
703 | 8F3E45A41D327E4500FB71FC /* Decoders.swift in Sources */,
704 | );
705 | runOnlyForDeploymentPostprocessing = 0;
706 | };
707 | 8FE7B5681B4C9FB900837609 /* Sources */ = {
708 | isa = PBXSourcesBuildPhase;
709 | buildActionMask = 2147483647;
710 | files = (
711 | 9E2DA75F1CBC784700CAF3DF /* Vehicle.swift in Sources */,
712 | 8F87BCBB1B580CE200E53A8C /* ErrorPathTests.swift in Sources */,
713 | 8F6FCF5F1D4B39FC00838CE4 /* DynamicDecodableTests.swift in Sources */,
714 | 651A8C971C29AC5F00DE4D53 /* RawRepresentableDecodableTests.swift in Sources */,
715 | FF00609E1B5454F400D8CB77 /* DecodableExtensionTests.swift in Sources */,
716 | 8FD3D92F1C270A2D00D1AF4E /* MissingKeyOperatorTests.swift in Sources */,
717 | 8F53521D1BE4112900E3563A /* DictionaryTests.swift in Sources */,
718 | FF0060B01B546FB100D8CB77 /* DecodableOperatorsTests.swift in Sources */,
719 | 8F4453EF1D369FF200C19099 /* ParseTests.swift in Sources */,
720 | 8F84E70F1C3CB92D001EA4CE /* NSValueDecodableTests.swift in Sources */,
721 | 8F4B52651B5BAA5700FDCBA7 /* ArrayTests.swift in Sources */,
722 | 8F85E7781E13DA16000D6989 /* NSNullTests.swift in Sources */,
723 | 8FE7B5721B4C9FB900837609 /* DecodableTests.swift in Sources */,
724 | FFE77E221B5396FC00E52F28 /* Repository.swift in Sources */,
725 | 9E2DA75C1CBC77AB00CAF3DF /* DecodeAsOneOfTests.swift in Sources */,
726 | 8F3E459F1D313A7000FB71FC /* KeyPathTests.swift in Sources */,
727 | );
728 | runOnlyForDeploymentPostprocessing = 0;
729 | };
730 | D0DC546A1B7814D200F79CB0 /* Sources */ = {
731 | isa = PBXSourcesBuildPhase;
732 | buildActionMask = 2147483647;
733 | files = (
734 | D0DC547A1B78150900F79CB0 /* DecodingError.swift in Sources */,
735 | 8FFAB8141B7CFA9500E2D724 /* Parse.swift in Sources */,
736 | 8F012EF71BB5A920007D0B5C /* Castable.swift in Sources */,
737 | 8FB48ECC1D306C4700BC50A1 /* KeyPath.swift in Sources */,
738 | D0DC54771B78150900F79CB0 /* Decodable.swift in Sources */,
739 | D0DC54781B78150900F79CB0 /* Operators.swift in Sources */,
740 | 8F3E459C1D31362B00FB71FC /* OptionalKeyPath.swift in Sources */,
741 | 8F0062411C81EF61007BCF48 /* Overloads.swift in Sources */,
742 | 65DB18B21C29AC0E003BDA5C /* RawRepresentableDecodable.swift in Sources */,
743 | 8F72DC581C3CB8C900A39E10 /* NSValueCastable.swift in Sources */,
744 | 8F3E45A61D327E4500FB71FC /* Decoders.swift in Sources */,
745 | );
746 | runOnlyForDeploymentPostprocessing = 0;
747 | };
748 | /* End PBXSourcesBuildPhase section */
749 |
750 | /* Begin PBXTargetDependency section */
751 | 17FB81031B530FED0012F106 /* PBXTargetDependency */ = {
752 | isa = PBXTargetDependency;
753 | target = 17FB80F61B530FED0012F106 /* Decodable-Mac */;
754 | targetProxy = 17FB81021B530FED0012F106 /* PBXContainerItemProxy */;
755 | };
756 | 8FE7B56F1B4C9FB900837609 /* PBXTargetDependency */ = {
757 | isa = PBXTargetDependency;
758 | target = 8FE7B5611B4C9FB900837609 /* Decodable-iOS */;
759 | targetProxy = 8FE7B56E1B4C9FB900837609 /* PBXContainerItemProxy */;
760 | };
761 | /* End PBXTargetDependency section */
762 |
763 | /* Begin XCBuildConfiguration section */
764 | 17FB81091B530FED0012F106 /* Debug */ = {
765 | isa = XCBuildConfiguration;
766 | buildSettings = {
767 | APPLICATION_EXTENSION_API_ONLY = YES;
768 | COMBINE_HIDPI_IMAGES = YES;
769 | DEFINES_MODULE = YES;
770 | DYLIB_COMPATIBILITY_VERSION = 1;
771 | DYLIB_CURRENT_VERSION = 1;
772 | DYLIB_INSTALL_NAME_BASE = "@rpath";
773 | ENABLE_BITCODE = NO;
774 | FRAMEWORK_VERSION = A;
775 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
776 | INFOPLIST_FILE = Sources/Info.plist;
777 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
778 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
779 | PRODUCT_BUNDLE_IDENTIFIER = anviking.Decodable;
780 | PRODUCT_NAME = Decodable;
781 | SDKROOT = macosx;
782 | SKIP_INSTALL = YES;
783 | };
784 | name = Debug;
785 | };
786 | 17FB810A1B530FED0012F106 /* Release */ = {
787 | isa = XCBuildConfiguration;
788 | buildSettings = {
789 | APPLICATION_EXTENSION_API_ONLY = YES;
790 | COMBINE_HIDPI_IMAGES = YES;
791 | DEFINES_MODULE = YES;
792 | DYLIB_COMPATIBILITY_VERSION = 1;
793 | DYLIB_CURRENT_VERSION = 1;
794 | DYLIB_INSTALL_NAME_BASE = "@rpath";
795 | ENABLE_BITCODE = NO;
796 | FRAMEWORK_VERSION = A;
797 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
798 | INFOPLIST_FILE = Sources/Info.plist;
799 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
800 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
801 | PRODUCT_BUNDLE_IDENTIFIER = anviking.Decodable;
802 | PRODUCT_NAME = Decodable;
803 | SDKROOT = macosx;
804 | SKIP_INSTALL = YES;
805 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
806 | };
807 | name = Release;
808 | };
809 | 17FB810C1B530FED0012F106 /* Debug */ = {
810 | isa = XCBuildConfiguration;
811 | buildSettings = {
812 | APPLICATION_EXTENSION_API_ONLY = NO;
813 | COMBINE_HIDPI_IMAGES = YES;
814 | ENABLE_BITCODE = NO;
815 | INFOPLIST_FILE = Tests/Info.plist;
816 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
817 | MACOSX_DEPLOYMENT_TARGET = 10.10;
818 | PRODUCT_BUNDLE_IDENTIFIER = anviking.DecodableTests;
819 | PRODUCT_NAME = "$(TARGET_NAME)";
820 | SDKROOT = macosx;
821 | };
822 | name = Debug;
823 | };
824 | 17FB810D1B530FED0012F106 /* Release */ = {
825 | isa = XCBuildConfiguration;
826 | buildSettings = {
827 | APPLICATION_EXTENSION_API_ONLY = NO;
828 | COMBINE_HIDPI_IMAGES = YES;
829 | ENABLE_BITCODE = NO;
830 | INFOPLIST_FILE = Tests/Info.plist;
831 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
832 | MACOSX_DEPLOYMENT_TARGET = 10.10;
833 | PRODUCT_BUNDLE_IDENTIFIER = anviking.DecodableTests;
834 | PRODUCT_NAME = "$(TARGET_NAME)";
835 | SDKROOT = macosx;
836 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
837 | };
838 | name = Release;
839 | };
840 | 57FCDE631BA283C900130C48 /* Debug */ = {
841 | isa = XCBuildConfiguration;
842 | buildSettings = {
843 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
844 | DEFINES_MODULE = YES;
845 | DYLIB_COMPATIBILITY_VERSION = 1;
846 | DYLIB_CURRENT_VERSION = 1;
847 | DYLIB_INSTALL_NAME_BASE = "@rpath";
848 | INFOPLIST_FILE = Sources/Info.plist;
849 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
850 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
851 | PRODUCT_BUNDLE_IDENTIFIER = anviking.Decodable;
852 | PRODUCT_NAME = Decodable;
853 | SDKROOT = appletvos;
854 | SKIP_INSTALL = YES;
855 | TARGETED_DEVICE_FAMILY = 3;
856 | };
857 | name = Debug;
858 | };
859 | 57FCDE641BA283C900130C48 /* Release */ = {
860 | isa = XCBuildConfiguration;
861 | buildSettings = {
862 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
863 | DEFINES_MODULE = YES;
864 | DYLIB_COMPATIBILITY_VERSION = 1;
865 | DYLIB_CURRENT_VERSION = 1;
866 | DYLIB_INSTALL_NAME_BASE = "@rpath";
867 | INFOPLIST_FILE = Sources/Info.plist;
868 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
869 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
870 | PRODUCT_BUNDLE_IDENTIFIER = anviking.Decodable;
871 | PRODUCT_NAME = Decodable;
872 | SDKROOT = appletvos;
873 | SKIP_INSTALL = YES;
874 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
875 | TARGETED_DEVICE_FAMILY = 3;
876 | };
877 | name = Release;
878 | };
879 | 8FE7B5741B4C9FB900837609 /* Debug */ = {
880 | isa = XCBuildConfiguration;
881 | buildSettings = {
882 | ALWAYS_SEARCH_USER_PATHS = NO;
883 | APPLICATION_EXTENSION_API_ONLY = YES;
884 | BITCODE_GENERATION_MODE = bitcode;
885 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
886 | CLANG_CXX_LIBRARY = "libc++";
887 | CLANG_ENABLE_MODULES = YES;
888 | CLANG_ENABLE_OBJC_ARC = YES;
889 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
890 | CLANG_WARN_BOOL_CONVERSION = YES;
891 | CLANG_WARN_COMMA = YES;
892 | CLANG_WARN_CONSTANT_CONVERSION = YES;
893 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
894 | CLANG_WARN_EMPTY_BODY = YES;
895 | CLANG_WARN_ENUM_CONVERSION = YES;
896 | CLANG_WARN_INFINITE_RECURSION = YES;
897 | CLANG_WARN_INT_CONVERSION = YES;
898 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
899 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
900 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
901 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
902 | CLANG_WARN_STRICT_PROTOTYPES = YES;
903 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
904 | CLANG_WARN_UNREACHABLE_CODE = YES;
905 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
906 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
907 | COPY_PHASE_STRIP = NO;
908 | CURRENT_PROJECT_VERSION = 1;
909 | DEBUG_INFORMATION_FORMAT = dwarf;
910 | ENABLE_BITCODE = YES;
911 | ENABLE_STRICT_OBJC_MSGSEND = YES;
912 | ENABLE_TESTABILITY = YES;
913 | GCC_C_LANGUAGE_STANDARD = gnu99;
914 | GCC_DYNAMIC_NO_PIC = NO;
915 | GCC_NO_COMMON_BLOCKS = YES;
916 | GCC_OPTIMIZATION_LEVEL = fast;
917 | GCC_PREPROCESSOR_DEFINITIONS = (
918 | "DEBUG=1",
919 | "$(inherited)",
920 | );
921 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
922 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
923 | GCC_WARN_UNDECLARED_SELECTOR = YES;
924 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
925 | GCC_WARN_UNUSED_FUNCTION = YES;
926 | GCC_WARN_UNUSED_VARIABLE = YES;
927 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
928 | MACOSX_DEPLOYMENT_TARGET = 10.10;
929 | MTL_ENABLE_DEBUG_INFO = YES;
930 | ONLY_ACTIVE_ARCH = YES;
931 | SDKROOT = iphoneos;
932 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
933 | SWIFT_VERSION = 3.0;
934 | TARGETED_DEVICE_FAMILY = "1,2";
935 | TVOS_DEPLOYMENT_TARGET = 9.0;
936 | VERSIONING_SYSTEM = "apple-generic";
937 | VERSION_INFO_PREFIX = "";
938 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
939 | };
940 | name = Debug;
941 | };
942 | 8FE7B5751B4C9FB900837609 /* Release */ = {
943 | isa = XCBuildConfiguration;
944 | buildSettings = {
945 | ALWAYS_SEARCH_USER_PATHS = NO;
946 | APPLICATION_EXTENSION_API_ONLY = YES;
947 | BITCODE_GENERATION_MODE = bitcode;
948 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
949 | CLANG_CXX_LIBRARY = "libc++";
950 | CLANG_ENABLE_MODULES = YES;
951 | CLANG_ENABLE_OBJC_ARC = YES;
952 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
953 | CLANG_WARN_BOOL_CONVERSION = YES;
954 | CLANG_WARN_COMMA = YES;
955 | CLANG_WARN_CONSTANT_CONVERSION = YES;
956 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
957 | CLANG_WARN_EMPTY_BODY = YES;
958 | CLANG_WARN_ENUM_CONVERSION = YES;
959 | CLANG_WARN_INFINITE_RECURSION = YES;
960 | CLANG_WARN_INT_CONVERSION = YES;
961 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
962 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
963 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
964 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
965 | CLANG_WARN_STRICT_PROTOTYPES = YES;
966 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
967 | CLANG_WARN_UNREACHABLE_CODE = YES;
968 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
969 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
970 | COPY_PHASE_STRIP = NO;
971 | CURRENT_PROJECT_VERSION = 1;
972 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
973 | ENABLE_BITCODE = YES;
974 | ENABLE_NS_ASSERTIONS = NO;
975 | ENABLE_STRICT_OBJC_MSGSEND = YES;
976 | GCC_C_LANGUAGE_STANDARD = gnu99;
977 | GCC_NO_COMMON_BLOCKS = YES;
978 | GCC_OPTIMIZATION_LEVEL = fast;
979 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
980 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
981 | GCC_WARN_UNDECLARED_SELECTOR = YES;
982 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
983 | GCC_WARN_UNUSED_FUNCTION = YES;
984 | GCC_WARN_UNUSED_VARIABLE = YES;
985 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
986 | MACOSX_DEPLOYMENT_TARGET = 10.10;
987 | MTL_ENABLE_DEBUG_INFO = NO;
988 | SDKROOT = iphoneos;
989 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
990 | SWIFT_VERSION = 3.0;
991 | TARGETED_DEVICE_FAMILY = "1,2";
992 | TVOS_DEPLOYMENT_TARGET = 9.0;
993 | VALIDATE_PRODUCT = YES;
994 | VERSIONING_SYSTEM = "apple-generic";
995 | VERSION_INFO_PREFIX = "";
996 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
997 | };
998 | name = Release;
999 | };
1000 | 8FE7B5771B4C9FB900837609 /* Debug */ = {
1001 | isa = XCBuildConfiguration;
1002 | buildSettings = {
1003 | APPLICATION_EXTENSION_API_ONLY = YES;
1004 | CLANG_ENABLE_MODULES = YES;
1005 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
1006 | DEFINES_MODULE = YES;
1007 | DYLIB_COMPATIBILITY_VERSION = 1;
1008 | DYLIB_CURRENT_VERSION = 1;
1009 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1010 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
1011 | INFOPLIST_FILE = Sources/Info.plist;
1012 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1013 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1014 | PRODUCT_BUNDLE_IDENTIFIER = anviking.Decodable;
1015 | PRODUCT_NAME = Decodable;
1016 | SKIP_INSTALL = YES;
1017 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1018 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
1019 | SWIFT_VERSION = 4.0;
1020 | };
1021 | name = Debug;
1022 | };
1023 | 8FE7B5781B4C9FB900837609 /* Release */ = {
1024 | isa = XCBuildConfiguration;
1025 | buildSettings = {
1026 | APPLICATION_EXTENSION_API_ONLY = YES;
1027 | CLANG_ENABLE_MODULES = YES;
1028 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
1029 | DEFINES_MODULE = YES;
1030 | DYLIB_COMPATIBILITY_VERSION = 1;
1031 | DYLIB_CURRENT_VERSION = 1;
1032 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1033 | GCC_GENERATE_TEST_COVERAGE_FILES = YES;
1034 | INFOPLIST_FILE = Sources/Info.plist;
1035 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1036 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1037 | PRODUCT_BUNDLE_IDENTIFIER = anviking.Decodable;
1038 | PRODUCT_NAME = Decodable;
1039 | SKIP_INSTALL = YES;
1040 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
1041 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
1042 | SWIFT_VERSION = 4.0;
1043 | };
1044 | name = Release;
1045 | };
1046 | 8FE7B57A1B4C9FB900837609 /* Debug */ = {
1047 | isa = XCBuildConfiguration;
1048 | buildSettings = {
1049 | APPLICATION_EXTENSION_API_ONLY = NO;
1050 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
1051 | INFOPLIST_FILE = Tests/Info.plist;
1052 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1053 | PRODUCT_BUNDLE_IDENTIFIER = anviking.DecodableTests;
1054 | PRODUCT_NAME = "$(TARGET_NAME)";
1055 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
1056 | SWIFT_VERSION = 4.0;
1057 | };
1058 | name = Debug;
1059 | };
1060 | 8FE7B57B1B4C9FB900837609 /* Release */ = {
1061 | isa = XCBuildConfiguration;
1062 | buildSettings = {
1063 | APPLICATION_EXTENSION_API_ONLY = NO;
1064 | GCC_GENERATE_TEST_COVERAGE_FILES = NO;
1065 | INFOPLIST_FILE = Tests/Info.plist;
1066 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1067 | PRODUCT_BUNDLE_IDENTIFIER = anviking.DecodableTests;
1068 | PRODUCT_NAME = "$(TARGET_NAME)";
1069 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
1070 | SWIFT_SWIFT3_OBJC_INFERENCE = On;
1071 | SWIFT_VERSION = 4.0;
1072 | };
1073 | name = Release;
1074 | };
1075 | D0DC54741B7814D200F79CB0 /* Debug */ = {
1076 | isa = XCBuildConfiguration;
1077 | buildSettings = {
1078 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
1079 | DEFINES_MODULE = YES;
1080 | DYLIB_COMPATIBILITY_VERSION = 1;
1081 | DYLIB_CURRENT_VERSION = 1;
1082 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1083 | INFOPLIST_FILE = Sources/Info.plist;
1084 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1085 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1086 | PRODUCT_BUNDLE_IDENTIFIER = anviking.Decodable;
1087 | PRODUCT_NAME = Decodable;
1088 | SDKROOT = watchos;
1089 | SKIP_INSTALL = YES;
1090 | TARGETED_DEVICE_FAMILY = 4;
1091 | };
1092 | name = Debug;
1093 | };
1094 | D0DC54751B7814D200F79CB0 /* Release */ = {
1095 | isa = XCBuildConfiguration;
1096 | buildSettings = {
1097 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
1098 | DEFINES_MODULE = YES;
1099 | DYLIB_COMPATIBILITY_VERSION = 1;
1100 | DYLIB_CURRENT_VERSION = 1;
1101 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1102 | INFOPLIST_FILE = Sources/Info.plist;
1103 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1104 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1105 | PRODUCT_BUNDLE_IDENTIFIER = anviking.Decodable;
1106 | PRODUCT_NAME = Decodable;
1107 | SDKROOT = watchos;
1108 | SKIP_INSTALL = YES;
1109 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
1110 | TARGETED_DEVICE_FAMILY = 4;
1111 | };
1112 | name = Release;
1113 | };
1114 | /* End XCBuildConfiguration section */
1115 |
1116 | /* Begin XCConfigurationList section */
1117 | 17FB81081B530FED0012F106 /* Build configuration list for PBXNativeTarget "Decodable-Mac" */ = {
1118 | isa = XCConfigurationList;
1119 | buildConfigurations = (
1120 | 17FB81091B530FED0012F106 /* Debug */,
1121 | 17FB810A1B530FED0012F106 /* Release */,
1122 | );
1123 | defaultConfigurationIsVisible = 0;
1124 | defaultConfigurationName = Release;
1125 | };
1126 | 17FB810B1B530FED0012F106 /* Build configuration list for PBXNativeTarget "DecodableTests-Mac" */ = {
1127 | isa = XCConfigurationList;
1128 | buildConfigurations = (
1129 | 17FB810C1B530FED0012F106 /* Debug */,
1130 | 17FB810D1B530FED0012F106 /* Release */,
1131 | );
1132 | defaultConfigurationIsVisible = 0;
1133 | defaultConfigurationName = Release;
1134 | };
1135 | 57FCDE621BA283C900130C48 /* Build configuration list for PBXNativeTarget "Decodable-tvOS" */ = {
1136 | isa = XCConfigurationList;
1137 | buildConfigurations = (
1138 | 57FCDE631BA283C900130C48 /* Debug */,
1139 | 57FCDE641BA283C900130C48 /* Release */,
1140 | );
1141 | defaultConfigurationIsVisible = 0;
1142 | defaultConfigurationName = Release;
1143 | };
1144 | 8FE7B55C1B4C9FB900837609 /* Build configuration list for PBXProject "Decodable" */ = {
1145 | isa = XCConfigurationList;
1146 | buildConfigurations = (
1147 | 8FE7B5741B4C9FB900837609 /* Debug */,
1148 | 8FE7B5751B4C9FB900837609 /* Release */,
1149 | );
1150 | defaultConfigurationIsVisible = 0;
1151 | defaultConfigurationName = Release;
1152 | };
1153 | 8FE7B5761B4C9FB900837609 /* Build configuration list for PBXNativeTarget "Decodable-iOS" */ = {
1154 | isa = XCConfigurationList;
1155 | buildConfigurations = (
1156 | 8FE7B5771B4C9FB900837609 /* Debug */,
1157 | 8FE7B5781B4C9FB900837609 /* Release */,
1158 | );
1159 | defaultConfigurationIsVisible = 0;
1160 | defaultConfigurationName = Release;
1161 | };
1162 | 8FE7B5791B4C9FB900837609 /* Build configuration list for PBXNativeTarget "DecodableTests-iOS" */ = {
1163 | isa = XCConfigurationList;
1164 | buildConfigurations = (
1165 | 8FE7B57A1B4C9FB900837609 /* Debug */,
1166 | 8FE7B57B1B4C9FB900837609 /* Release */,
1167 | );
1168 | defaultConfigurationIsVisible = 0;
1169 | defaultConfigurationName = Release;
1170 | };
1171 | D0DC54761B7814D200F79CB0 /* Build configuration list for PBXNativeTarget "Decodable-watchOS" */ = {
1172 | isa = XCConfigurationList;
1173 | buildConfigurations = (
1174 | D0DC54741B7814D200F79CB0 /* Debug */,
1175 | D0DC54751B7814D200F79CB0 /* Release */,
1176 | );
1177 | defaultConfigurationIsVisible = 0;
1178 | defaultConfigurationName = Release;
1179 | };
1180 | /* End XCConfigurationList section */
1181 | };
1182 | rootObject = 8FE7B5591B4C9FB900837609 /* Project object */;
1183 | }
1184 |
--------------------------------------------------------------------------------