├── .circleci
└── config.yml
├── .codebeatignore
├── .gitattributes
├── .gitignore
├── Cartfile
├── Cartfile.resolved
├── LICENSE
├── Package.swift
├── Package@swift-5.0.swift
├── Package@swift-5.1.swift
├── Package@swift-5.2.swift
├── README.md
├── Serpent.podspec
├── Serpent
├── Example
│ ├── SerpentExample.xcodeproj
│ │ ├── project.pbxproj
│ │ └── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ └── WorkspaceSettings.xcsettings
│ └── SerpentExample
│ │ ├── Classes
│ │ ├── AppDelegate.swift
│ │ ├── Managers
│ │ │ └── ConnectionManager.swift
│ │ ├── Models
│ │ │ ├── Identification.swift
│ │ │ ├── Location.swift
│ │ │ ├── LoginInfo.swift
│ │ │ ├── NameInfo.swift
│ │ │ ├── ProfilePicture.swift
│ │ │ └── User.swift
│ │ └── User Interface
│ │ │ ├── Cells
│ │ │ ├── UserListCell.swift
│ │ │ └── UserListCell.xib
│ │ │ ├── HomeVC.swift
│ │ │ └── UserDetailsVC.swift
│ │ ├── Resources
│ │ └── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-29@2x.png
│ │ │ ├── Icon-29@3x.png
│ │ │ ├── Icon-40@2x.png
│ │ │ ├── Icon-40@3x.png
│ │ │ ├── Icon-60@2x.png
│ │ │ └── Icon-60@3x.png
│ │ │ ├── Contents.json
│ │ │ ├── Logo.imageset
│ │ │ ├── Contents.json
│ │ │ ├── Logo.png
│ │ │ ├── Logo@2x.png
│ │ │ └── Logo@3x.png
│ │ │ └── LogoText.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LogoText.png
│ │ │ ├── LogoText@2x.png
│ │ │ └── LogoText@3x.png
│ │ ├── Storyboards
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ └── Supporting Files
│ │ └── Info.plist
├── Serpent.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── Serpent OSX.xcscheme
│ │ ├── Serpent iOS.xcscheme
│ │ ├── Serpent tvOS.xcscheme
│ │ └── Serpent watchOS.xcscheme
├── Serpent
│ ├── Classes
│ │ ├── Extensions
│ │ │ ├── AlamofireExtension.swift
│ │ │ ├── CashierExtension.swift
│ │ │ └── Extensions.swift
│ │ ├── Operators
│ │ │ └── Operator.swift
│ │ ├── Other
│ │ │ └── BridgingBox.swift
│ │ └── Serpent.swift
│ └── Supporting Files
│ │ ├── Info.plist
│ │ └── Serpent.h
└── SerpentTests
│ ├── Info.plist
│ ├── Models
│ ├── CustomOperatorsTest.json
│ ├── CustomOperatorsTestModel.swift
│ ├── DecodableModel.swift
│ ├── DecodableTest.json
│ ├── EnumsTest.json
│ ├── EnumsTestModel.swift
│ ├── HexInitializableTest.json
│ ├── HexInitializableTestModel.swift
│ ├── NetworkTestModel.swift
│ ├── NilTestModel.swift
│ ├── PerformanceSmallTest.json
│ ├── PerformanceTest.json
│ ├── PerformanceTestModel.swift
│ ├── PerformanceTestSmallModel.swift
│ ├── PrimitivesTest.json
│ ├── PrimitivesTestModel.swift
│ ├── SerializableEntityTest.json
│ ├── SerializableEntityTestModel.swift
│ ├── SimpleModel.json
│ ├── SimpleModel.swift
│ ├── StringInitializableTest.json
│ └── StringInitializableTestModel.swift
│ ├── TestEndpoint
│ ├── ArrayTest.json
│ ├── ArrayTestOneObject.json
│ ├── BadArrayTest.json
│ ├── Empty.json
│ ├── NestedArrayTest.json
│ ├── NetworkModel.json
│ └── NetworkModelBad.json
│ └── Tests
│ ├── AlamofireExtensionTests.swift
│ ├── BridgingBoxTests.swift
│ ├── CashierExtensionTests.swift
│ ├── CustomOperatorsTests.swift
│ ├── DecodableTests.swift
│ ├── HexInitalizableTests.swift
│ ├── SerializableEntityTests.swift
│ ├── SerializableEnumsTests.swift
│ ├── SerializableNilEntitiesTests.swift
│ ├── SerializablePerformanceTest.swift
│ ├── SerializablePrimitivesTests.swift
│ └── StringInitializableTests.swift
├── Serpent_icon.png
├── Sources
└── Serpent
│ ├── BridgingBox.swift
│ ├── Extensions.swift
│ ├── Operator.swift
│ └── Serpent.swift
└── codecov.yml
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # iOS CircleCI 2.0 configuration file
2 | #
3 | # Check https://circleci.com/docs/2.0/ios-migrating-from-1-2/ for more details
4 | #
5 |
6 | ### Serpent config file ###
7 | version: 2
8 | jobs:
9 | build:
10 |
11 | # Specify the Xcode version to use
12 | macos:
13 | xcode: "9.4.1"
14 |
15 | steps:
16 | - checkout
17 |
18 | - run:
19 | name: Install Carthage
20 | command: carthage bootstrap --no-use-binaries
21 |
22 | # Build the app and run tests
23 | - run:
24 | name: Build and run tests
25 | command: fastlane scan --project "Serpent/Serpent.xcodeproj"
26 | environment:
27 | SCAN_DEVICE: iPhone 6
28 | SCAN_SCHEME: Serpent iOS
29 |
30 | # Collect XML test results data to show in the UI,
31 | # and save the same XML files under test-results folder
32 | # in the Artifacts tab
33 | - store_test_results:
34 | path: test_output/report.xml
35 | - store_artifacts:
36 | path: /tmp/test-results
37 | destination: scan-test-results
38 | - store_artifacts:
39 | path: ~/Library/Logs/scan
40 | destination: scan-logs
41 |
42 |
43 | # workflows:
44 | # version: 2
45 | # build-and-test:
46 | # jobs:
47 | # - build
--------------------------------------------------------------------------------
/.codebeatignore:
--------------------------------------------------------------------------------
1 | Serpent/Example/**
2 | Serpent/SerpentTests/**
3 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | Serpent.podspec linguist-documentation
2 | Serpent/Serpent/Supporting[[:space:]]Files/Serpent.h linguist-documentation
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 | .DS_Store
9 |
10 | ## Various settings
11 | *.pbxuser
12 | !default.pbxuser
13 | *.mode1v3
14 | !default.mode1v3
15 | *.mode2v3
16 | !default.mode2v3
17 | *.perspectivev3
18 | !default.perspectivev3
19 | xcuserdata
20 |
21 | ## Other
22 | *.xccheckout
23 | *.moved-aside
24 | *.xcuserstate
25 | *.xcscmblueprint
26 |
27 | ## Obj-C/Swift specific
28 | *.hmap
29 | *.ipa
30 |
31 | ## Playgrounds
32 | timeline.xctimeline
33 | playground.xcworkspace
34 |
35 | # Swift Package Manager
36 | #
37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
38 | # Packages/
39 | .build/
40 |
41 | # CocoaPods
42 | #
43 | # We recommend against adding the Pods directory to your .gitignore. However
44 | # you should judge for yourself, the pros and cons are mentioned at:
45 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
46 | #
47 | # Pods/
48 |
49 | # Carthage
50 | #
51 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
52 | # Carthage/Checkouts
53 | Carthage/
54 | #Carthage/Build
55 |
56 | # fastlane
57 | #
58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
59 | # screenshots whenever they are needed.
60 | # For more information about the recommended setup visit:
61 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
62 |
63 | fastlane/report.xml
64 | fastlane/screenshots
65 |
--------------------------------------------------------------------------------
/Cartfile:
--------------------------------------------------------------------------------
1 | github "Alamofire/Alamofire" ~> 4.1.0
2 | github "nodes-ios/Cashier" ~> 2.0
3 |
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "Alamofire/Alamofire" "4.8.1"
2 | github "nodes-ios/Cashier" "1.3"
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Nodes Agency - iOS
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "Serpent",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "Serpent",
12 | targets: ["Serpent"]),
13 | ],
14 | dependencies: [
15 | // Dependencies declare other packages that this package depends on.
16 | // .package(url: /* package url */, from: "1.0.0"),
17 | ],
18 | targets: [
19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
21 | .target(
22 | name: "Serpent",
23 | dependencies: []),
24 | .testTarget(
25 | name: "SerpentTests",
26 | dependencies: ["Serpent"],
27 | path: "Serpent/SerpentTests"),
28 | ]
29 | )
30 |
--------------------------------------------------------------------------------
/Package@swift-5.0.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.0
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "Serpent",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "Serpent",
12 | targets: ["Serpent"]),
13 | ],
14 | dependencies: [
15 | // Dependencies declare other packages that this package depends on.
16 | // .package(url: /* package url */, from: "1.0.0"),
17 | ],
18 | targets: [
19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
21 | .target(
22 | name: "Serpent",
23 | dependencies: []),
24 | .testTarget(
25 | name: "SerpentTests",
26 | dependencies: ["Serpent"],
27 | path: "Serpent/SerpentTests"),
28 | ]
29 | )
30 |
--------------------------------------------------------------------------------
/Package@swift-5.1.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "Serpent",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "Serpent",
12 | targets: ["Serpent"]),
13 | ],
14 | dependencies: [
15 | // Dependencies declare other packages that this package depends on.
16 | // .package(url: /* package url */, from: "1.0.0"),
17 | ],
18 | targets: [
19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
21 | .target(
22 | name: "Serpent",
23 | dependencies: []),
24 | .testTarget(
25 | name: "SerpentTests",
26 | dependencies: ["Serpent"],
27 | path: "Serpent/SerpentTests"),
28 | ]
29 | )
30 |
--------------------------------------------------------------------------------
/Package@swift-5.2.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.2
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "Serpent",
8 | products: [
9 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
10 | .library(
11 | name: "Serpent",
12 | targets: ["Serpent"]),
13 | ],
14 | dependencies: [
15 | // Dependencies declare other packages that this package depends on.
16 | // .package(url: /* package url */, from: "1.0.0"),
17 | ],
18 | targets: [
19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
21 | .target(
22 | name: "Serpent",
23 | dependencies: []),
24 | .testTarget(
25 | name: "SerpentTests",
26 | dependencies: ["Serpent"],
27 | path: "Serpent/SerpentTests"),
28 | ]
29 | )
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### This library has been deprecated and the repo has been archived.
2 | ### The code is still here and you can still clone it, however the library will not receive any more updates or support.
3 |
4 |

5 |
6 | [](https://circleci.com/gh/nodes-ios/Serpent)
7 | [](https://codecov.io/github/nodes-ios/Serpent)
8 | [](https://codebeat.co/projects/github-com-nodes-ios-serpent)
9 | [](https://github.com/Carthage/Carthage)
10 | [](https://cocoapods.org/pods/Serpent)
11 | 
12 | [](https://github.com/nodes-ios/Serpent/blob/master/LICENSE)
13 |
14 | **Serpent** *(previously known as Serializable)* is a framework made for creating model objects or structs that can be easily serialized and deserialized from/to JSON. It's easily expandable and handles all common data types used when consuming a REST API, as well as recursive parsing of custom objects. Designed for use with Alamofire.
15 |
16 | It's designed to be used together with our helper app, the [](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler), making model creation a breeze.
17 |
18 | Serpent is implemented using protocol extensions and static typing.
19 |
20 | ## 📑 Table of Contents
21 |
22 | - [🐍 Why Serpent?](#-why-serpent)
23 | - [📝 Requirements](#-requirements)
24 | - [📦 Installation](#-installation)
25 | - [Carthage](#carthage)
26 | - [CocoaPods](#cocoapods)
27 | - [Swift Package Manager](#swift-package-manager)
28 | - [🔧 Setup](#-setup)
29 | - [💻 Usage](#-usage)
30 | - [Getting started](#getting-started)
31 | - [Using Serpent models](#using-serpent-models)
32 | - [More complex examples](#more-complex-examples)
33 | - [Using with Alamofire](#using-with-alamofire)
34 | - [Date parsing](#date-parsing)
35 | - [👥 Credits](#-credits)
36 | - [📄 License](#-license)
37 |
38 | ## 🐍 Why Serpent?
39 | There are plenty of other Encoding and Decoding frameworks available. Why should you use Serpent?
40 |
41 | * [Performance](https://github.com/nodes-ios/SerpentPerformanceComparison). Serpent is fast, up to 4x faster than similar frameworks.
42 | * [Features](https://github.com/nodes-ios/SerpentPerformanceComparison#-feature-comparison). Serpent can parse anything you throw at it. Nested objects, Enums, URLs, UIColor, you name it!
43 | * [](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler). Every framework of this kind requires tedious boilerplate code that takes forever to write. [](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler) generates it for you instantly.
44 | * [Alamofire Integration](). Using the included Alamofire extensions makes implementing an API call returning parsed model data as simple as doing a one-liner!
45 | * [Expandability](). Parsing into other datatypes can easily be added.
46 | * [Persisting](). Combined with our caching framework [Cashier](https://github.com/nodes-ios/Cashier), Serpent objects can be very easily persisted to disk.
47 | *
Serpent Xcode File Template makes it easier to create the model files in Xcode.
48 |
49 | ## 📝 Requirements
50 |
51 | * iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
52 | * Swift 3.0+
53 | *(Swift 2.2 & Swift 2.3 supported in older versions)*
54 |
55 | ## 📦 Installation
56 |
57 | ### Carthage
58 | ~~~bash
59 | github "nodes-ios/Serpent" ~> 1.0
60 | ~~~
61 |
62 | > Last versions compatible with lower Swift versions:
63 | >
64 | > **Swift 2.3**
65 | > `github "nodes-ios/Serpent" == 0.13.2`
66 | >
67 | > **Swift 2.2**
68 | > `github "nodes-ios/Serpent" == 0.11.2`
69 | >
70 | > **NOTE:** Serpent was previously known as **Serializable**.
71 |
72 | ### CocoaPods
73 |
74 | Choose one of the following, add it to your `Podfile` and run `pod install`:
75 |
76 | ~~~ruby
77 | pod 'Serpent', '~> 1.0' # Just core
78 | pod 'Serpent/Extensions', '~> 1.0' # Includes core and all extensions
79 | pod 'Serpent/AlamofireExtension', '~> 1.0' # Includes core and Alamofire extension
80 | pod 'Serpent/CashierExtension', '~> 1.0' # Includes core and Cashier extension
81 | ~~~
82 |
83 | > **NOTE:** CocoaPods only supports Serpent using Swift version 3.0 and higher.
84 |
85 | ### Swift Package Manager
86 |
87 | To use Serpent as a [Swift Package Manager](https://swift.org/package-manager/) package just add the following to your `Package.swift` file.
88 |
89 | ~~~swift
90 | import PackageDescription
91 |
92 | let package = Package(
93 | name: "YourPackage",
94 | dependencies: [
95 | .Package(url: "https://github.com/nodes-ios/Serpent.git", majorVersion: 1)
96 | ]
97 | )
98 | ~~~
99 |
100 |
101 | ## 🔧 Setup
102 |
103 | We **highly** recommend you use our [](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler) to assist with generating the code needed to conform to Serpent. Instructions for installation and usage can be found at the [Model Boiler GitHub repository](https://github.com/nodes-ios/ModelBoiler).
104 |
105 | ## 💻 Usage
106 |
107 | ### Getting started
108 |
109 | Serpent supports all primitive types, `enum`, `URL`, `Date`, `UIColor`, other `Serpent` model, and `Array` of all of the aforementioned types. Your variable declarations can have a default value or be optional.
110 |
111 | Primitive types do not need to have an explicit type, if Swift is able to infer it normally. `var name: String = ""` works just as well as `var name = ""`. Optionals will of course need an explicit type.
112 |
113 | > **NOTE:** Enums you create must conform to `RawRepresentable`, meaning they must have an explicit type. Otherwise, the parser won't know what to do with the incoming data it receives.
114 |
115 |
116 | #### Create your model struct or class:
117 |
118 | ~~~swift
119 | struct Foo {
120 | var id = 0
121 | var name = ""
122 | var address: String?
123 | }
124 | ~~~
125 |
126 | > **NOTE:** Classes must be marked `final`.
127 |
128 | #### Add the required methods for `Encodable` and `Decodable`:
129 |
130 | ~~~swift
131 | extension Foo: Serializable {
132 | init(dictionary: NSDictionary?) {
133 | id <== (self, dictionary, "id")
134 | name <== (self, dictionary, "name")
135 | address <== (self, dictionary, "address")
136 | }
137 |
138 | func encodableRepresentation() -> NSCoding {
139 | let dict = NSMutableDictionary()
140 | (dict, "id") <== id
141 | (dict, "name") <== name
142 | (dict, "address") <== address
143 | return dict
144 | }
145 | }
146 | ~~~
147 |
148 | > **NOTE:** You can add conformance to `Serializable` which is a type alias for both `Encodable` and `Decodable`.
149 |
150 | And thats it! If you're using the [](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler), this extension will be generated for you, so that you don't need to type it all out for every model you have.
151 |
152 | ### Using Serpent models
153 |
154 | New instances of your model can be created with a dictionary, e.g. from parsed JSON.
155 |
156 | ~~~swift
157 | let dictionary = try JSONSerialization.jsonObject(with: someData, options: .allowFragments) as? NSDictionary
158 | let newModel = Foo(dictionary: dictionary)
159 | ~~~
160 |
161 | You can generate a dictionary version of your model by calling `encodableRepresentation()`:
162 |
163 | ~~~swift
164 | let encodedDictionary = newModel.encodableRepresentation()
165 | ~~~
166 |
167 | ### More complex examples
168 |
169 | In this example, we have two models, Student and School.
170 |
171 | ~~~swift
172 | struct Student {
173 | enum Gender: String {
174 | case male = "male"
175 | case female = "female"
176 | case unspecified = "unspecified"
177 | }
178 |
179 | var name = ""
180 | var age: Int = 0
181 | var gender: Gender?
182 | }
183 |
184 | struct School {
185 | enum Sport: Int {
186 | case football
187 | case basketball
188 | case tennis
189 | case swimming
190 | }
191 |
192 | var name = ""
193 | var location = ""
194 | var website: URL?
195 | var students: [Student] = []
196 | var sports: [Sport]?
197 | }
198 | ~~~
199 |
200 |
201 | You can get as complicated as you like, and the syntax will always remain the same. The extensions will be:
202 |
203 | ~~~swift
204 | extension Student: Serializable {
205 | init(dictionary: NSDictionary?) {
206 | name <== (self, dictionary, "name")
207 | age <== (self, dictionary, "age")
208 | gender <== (self, dictionary, "gender")
209 | }
210 |
211 | func encodableRepresentation() -> NSCoding {
212 | let dict = NSMutableDictionary()
213 | (dict, "name") <== name
214 | (dict, "age") <== age
215 | (dict, "gender") <== gender
216 | return dict
217 | }
218 | }
219 |
220 | extension School: Serializable {
221 | init(dictionary: NSDictionary?) {
222 | name <== (self, dictionary, "name")
223 | location <== (self, dictionary, "location")
224 | website <== (self, dictionary, "website")
225 | students <== (self, dictionary, "students")
226 | sports <== (self, dictionary, "sports")
227 | }
228 |
229 | func encodableRepresentation() -> NSCoding {
230 | let dict = NSMutableDictionary()
231 | (dict, "name") <== name
232 | (dict, "location") <== location
233 | (dict, "website") <== website
234 | (dict, "students") <== students
235 | (dict, "sports") <== sports
236 | return dict
237 | }
238 | }
239 | ~~~
240 | Again, the [](https://github.com/nodes-ios/ModelBoiler) [Model Boiler](https://github.com/nodes-ios/ModelBoiler) generates all of this code for you in less than a second!
241 |
242 | ### Using with Alamofire
243 |
244 | Serpent comes integrated with Alamofire out of the box, through an extension on Alamofire's `Request` construct, that adds the function `responseSerializable(completion:unwrapper)`
245 |
246 | The extension uses Alamofire's familiar `Response` type to hold the returned data, and uses its generic associated type to automatically parse the data.
247 |
248 | Consider an endpoint returning a single `school` structure matching the struct from the example above. To implement the call, simply add a function to your shared connection manager or where ever you like to put it:
249 |
250 | ~~~swift
251 | func requestSchool(completion: @escaping (DataResponse) -> Void) {
252 | request("http://somewhere.com/school/1", method: .get).responseSerializable(completion)
253 | }
254 | ~~~
255 |
256 | In the consuming method you use it like this:
257 |
258 | ~~~swift
259 | requestSchool() { (response) in
260 | switch response.result {
261 | case .success(let school):
262 | //Use your new school object!
263 |
264 | case .failure(let error):
265 | //Handle the error object, or check your Response for more detail
266 | }
267 | }
268 | ~~~
269 |
270 | For an array of objects, use the same technique:
271 |
272 | ~~~swift
273 | static func requestStudents(completion: @escaping (DataResponse<[Student]>) -> Void) {
274 | request("http://somewhere.com/school/1/students", method: .get).responseSerializable(completion)
275 | }
276 | ~~~
277 |
278 | Some APIs wrap their data in containers. Use the `unwrapper` closure for that. Let's say your `/students` endpoint returns the data wrapped in a `students` object:
279 |
280 | ~~~json
281 | {
282 | "students" : [
283 | {
284 | "..." : "..."
285 | },
286 | {
287 | "..." : "..."
288 | }
289 | ]
290 | }
291 | ~~~
292 |
293 | The `unwrapper` closure has 2 input arguments: The `sourceDictionary` (the JSON Response Dictionary) and the `expectedType` (the *type* of the target Serpent). Return the object that will serve as the input for the Serializable initializer.
294 |
295 | ~~~swift
296 | static func requestStudents(completion: (DataResponse<[Student]>) -> Void) {
297 | request("http://somewhere.com/school/1/students", method: .get).responseSerializable(completion, unwrapper: { $0.0["students"] })
298 | }
299 | ~~~
300 |
301 | If you need to unwrap the response data in every call, you can install a default unwrapper using
302 |
303 | ~~~swift
304 | Parser.defaultWrapper = { sourceDictionary, expectedType in
305 | // You custom unwrapper here...
306 | return sourceDictionary
307 | }
308 | ~~~
309 |
310 | The `expectedType` can be used to dynamically determine the key based on the type name using reflection. This is especially useful when handling paginated data.
311 |
312 | See [here](https://github.com/nodes-ios/Nodes) for an example on how we use this in our projects at Nodes.
313 |
314 | ***NOTE:*** `responseSerializable` Internally calls `validate().responseJSON()` on the request, so you don't have to do that.
315 |
316 | ### Date parsing
317 | Serpent can create `Date` objects from the date strings in the JSON. By default, Serpent can parse the date strings from the following formats: `yyyy-MM-dd'T'HH:mm:ssZZZZZ`, `yyyy-MM-dd'T'HH:mm:ss`, `yyyy-MM-dd`. If you need to parse other date formats, you can do it by adding this line to your code (for example, in `AppDelegate`'s `didFinishLaunchingWithOptions:`:
318 |
319 | ~~~swift
320 | Date.customDateFormats = ["yyyyMMddHHmm", "yyyyMMdd"] // add the custom date formats you need here
321 | ~~~
322 |
323 | The custom date formats won't replace the default ones, they will be still supported.
324 |
325 | ## 👥 Credits
326 | Made with ❤️ at [Nodes](http://nodesagency.com).
327 |
328 | ## 📄 License
329 | **Serpent** is available under the MIT license. See the [LICENSE](https://github.com/nodes-ios/Serpent/blob/master/LICENSE) file for more info.
330 |
--------------------------------------------------------------------------------
/Serpent.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod spec lint Serpent.podspec' to ensure this is a
3 | # valid spec and to remove all comments including this before submitting the spec.
4 | #
5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html
6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
7 | #
8 |
9 | Pod::Spec.new do |s|
10 |
11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
12 | #
13 | # These will help people to find your library, and whilst it
14 | # can feel like a chore to fill in it's definitely to your advantage. The
15 | # summary should be tweet-length, and the description more in depth.
16 | #
17 |
18 | s.name = "Serpent"
19 | s.version = "2.0.3"
20 | s.summary = "A protocol to serialize Swift structs and classes for encoding and decoding."
21 | s.homepage = "https://github.com/nodes-ios/Serpent"
22 | s.description = <<-DESC
23 | Serpent is a framework made for creating model objects or structs that can be easily serialized and deserialized from/to JSON. It's easily expandable and handles all common data types used when consuming a REST API, as well as recursive parsing of custom objects.
24 | It's designed to be used together with our helper app, the Model Boiler, making model creation a breeze. Serpent is implemented using protocol extensions and static typing.
25 | Also provides extensions for Alamofire and Cashier.
26 | DESC
27 |
28 |
29 | s.swift_version = '5'
30 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '5' }
31 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
32 | #
33 |
34 | # Permissive license
35 | s.license = "MIT"
36 |
37 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
38 | #
39 | # Specify the authors of the library, with email addresses. Email addresses
40 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also
41 | # accepts just a name if you'd rather not provide an email address.
42 | #
43 | # Specify a social_media_url where others can refer to, for example a twitter
44 | # profile URL.
45 | #
46 |
47 | s.author = { "Nodes Agency - iOS" => "ios@nodes.dk" }
48 | s.social_media_url = "http://twitter.com/nodes_ios"
49 |
50 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
51 | #
52 | # If this Pod runs only on iOS or OS X, then specify the platform and
53 | # the deployment target. You can optionally include the target after the platform.
54 | #
55 |
56 | s.platforms = { :ios => "8.0", :osx => "10.10", :watchos => "2.0", :tvos => "9.0" }
57 |
58 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
59 | #
60 | # Specify the location from where the source should be retrieved.
61 | # Supports git, hg, bzr, svn and HTTP.
62 | #
63 |
64 | s.source = { :git => "https://github.com/nodes-ios/Serpent.git", :tag => s.version }
65 |
66 | # ――― Subspecs ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
67 | #
68 |
69 | s.default_subspecs = 'Core'
70 |
71 | # Main subspec
72 | s.subspec 'Core' do |core|
73 | # Add all files
74 | core.source_files = "Serpent/Serpent/Classes/**/*"
75 |
76 | # Exclude extensions by default
77 | core.exclude_files = "Serpent/Serpent/Classes/Extensions/CashierExtension.swift", "Serpent/Serpent/Classes/Extensions/AlamofireExtension.swift"
78 | end
79 |
80 | # Subspec for all extensions
81 | s.subspec 'Extensions' do |ext|
82 | ext.dependency 'Serpent/Core'
83 | ext.dependency 'Serpent/AlamofireExtension'
84 | ext.dependency 'Serpent/CashierExtension'
85 | end
86 |
87 | # Subspec for Alamofire extension
88 | s.subspec 'AlamofireExtension' do |alamo|
89 | alamo.dependency 'Serpent/Core'
90 | alamo.dependency 'Alamofire', '~> 4.1'
91 | alamo.source_files = "Serpent/Serpent/Classes/Extensions/AlamofireExtension.swift"
92 | end
93 |
94 | # Subspec for Cashier extension
95 | s.subspec 'CashierExtension' do |cashier|
96 | cashier.dependency 'Serpent/Core'
97 | cashier.dependency 'Cashier', '~> 1.2.1'
98 | cashier.source_files = "Serpent/Serpent/Classes/Extensions/CashierExtension.swift"
99 | end
100 |
101 | end
102 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Serpent Example
4 | //
5 | // Created by Dominik Hádl on 17/04/16.
6 | // Copyright © 2016 Nodes ApS. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Serpent
11 | import Alamofire
12 |
13 | @UIApplicationMain
14 | class AppDelegate: UIResponder, UIApplicationDelegate {
15 |
16 | var window: UIWindow?
17 |
18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
19 |
20 | // Unwrap data from nested dictionary
21 | Parser.defaultUnwrapper = { (sourceDictionary, type) in
22 | if let nestedObject: Any = sourceDictionary.object(forKey: "results") {
23 | return nestedObject
24 | }
25 |
26 | return sourceDictionary
27 | }
28 |
29 | return true
30 | }
31 |
32 | func applicationWillResignActive(_ application: UIApplication) {
33 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
34 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
35 | }
36 |
37 | func applicationDidEnterBackground(_ application: UIApplication) {
38 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
39 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
40 | }
41 |
42 | func applicationWillEnterForeground(_ application: UIApplication) {
43 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
44 | }
45 |
46 | func applicationDidBecomeActive(_ application: UIApplication) {
47 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
48 | }
49 |
50 | func applicationWillTerminate(_ application: UIApplication) {
51 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
52 | }
53 |
54 |
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/Managers/ConnectionManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ConnectionManager.swift
3 | // SerpentExample
4 | //
5 | // Created by Dominik Hádl on 17/04/16.
6 | // Copyright © 2016 Nodes ApS. All rights reserved.
7 | //
8 |
9 | import Serpent
10 | import Alamofire
11 | import UIKit
12 |
13 | // MARK: - Basic Setup -
14 |
15 | struct ConnectionManager {
16 | static let manager = SessionManager(configuration: ConnectionManager.configuration())
17 |
18 | static func configuration() -> URLSessionConfiguration {
19 | let configuration = SessionManager.default.session.configuration
20 |
21 | configuration.timeoutIntervalForRequest = 20
22 | configuration.httpAdditionalHeaders = ["Accept" : "application/json"]
23 |
24 | return configuration
25 | }
26 | }
27 |
28 | extension ConnectionManager {
29 |
30 | static func fetchRandomUsers(userCount count: Int = 150, completion: @escaping (DataResponse<[User]>) -> Void) {
31 | let params: [String: AnyObject] = ["results" : count as AnyObject]
32 |
33 | manager.request("https://randomuser.me/api", method: .get,
34 | parameters: params, headers: nil).responseSerializable(completion)
35 | }
36 | }
37 |
38 |
39 | // MARK: - Extensions -
40 |
41 | extension UIImageView {
42 |
43 | // Taken from http://stackoverflow.com/a/30591853/1001803
44 | public func imageFromUrl(_ url: URL?) {
45 | if let url = url {
46 | let task = URLSession.shared.dataTask(with: url) { data, response, error in
47 | guard let data = data, error == nil else { return }
48 |
49 | DispatchQueue.main.sync() {
50 | self.image = UIImage(data: data)
51 | }
52 | }
53 | task.resume()
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/Models/Identification.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Identification.swift
3 | // SerpentExample
4 | //
5 | // Created by Dominik Hádl on 17/04/16.
6 | // Copyright © 2016 Nodes ApS. All rights reserved.
7 | //
8 |
9 | import Serpent
10 |
11 | struct Identification {
12 | var name = ""
13 | var value = ""
14 | }
15 |
16 | extension Identification: Serializable {
17 | init(dictionary: NSDictionary?) {
18 | name <== (self, dictionary, "name")
19 | value <== (self, dictionary, "value")
20 | }
21 |
22 | func encodableRepresentation() -> NSCoding {
23 | let dict = NSMutableDictionary()
24 | (dict, "name") <== name
25 | (dict, "value") <== value
26 | return dict
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/Models/Location.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Location.swift
3 | // SerpentExample
4 | //
5 | // Created by Dominik Hádl on 17/04/16.
6 | // Copyright © 2016 Nodes ApS. All rights reserved.
7 | //
8 |
9 | import Serpent
10 |
11 | struct Location {
12 | var street = ""
13 | var city = ""
14 | var state = ""
15 | var postcode = 0
16 | }
17 |
18 | extension Location: Serializable {
19 | init(dictionary: NSDictionary?) {
20 | street <== (self, dictionary, "street")
21 | city <== (self, dictionary, "city")
22 | state <== (self, dictionary, "state")
23 | postcode <== (self, dictionary, "postcode")
24 | }
25 |
26 | func encodableRepresentation() -> NSCoding {
27 | let dict = NSMutableDictionary()
28 | (dict, "street") <== street
29 | (dict, "city") <== city
30 | (dict, "state") <== state
31 | (dict, "postcode") <== postcode
32 | return dict
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/Models/LoginInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginInfo.swift
3 | // SerpentExample
4 | //
5 | // Created by Dominik Hádl on 17/04/16.
6 | // Copyright © 2016 Nodes ApS. All rights reserved.
7 | //
8 |
9 | import Serpent
10 |
11 | struct LoginInfo {
12 | var username = ""
13 | var password = ""
14 | var salt = ""
15 | var md5 = ""
16 | var sha1 = ""
17 | var sha256 = ""
18 | }
19 |
20 | extension LoginInfo: Serializable {
21 | init(dictionary: NSDictionary?) {
22 | username <== (self, dictionary, "username")
23 | password <== (self, dictionary, "password")
24 | salt <== (self, dictionary, "salt")
25 | md5 <== (self, dictionary, "md5")
26 | sha1 <== (self, dictionary, "sha1")
27 | sha256 <== (self, dictionary, "sha256")
28 | }
29 |
30 | func encodableRepresentation() -> NSCoding {
31 | let dict = NSMutableDictionary()
32 | (dict, "username") <== username
33 | (dict, "password") <== password
34 | (dict, "salt") <== salt
35 | (dict, "md5") <== md5
36 | (dict, "sha1") <== sha1
37 | (dict, "sha256") <== sha256
38 | return dict
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/Models/NameInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NameInfo.swift
3 | // SerpentExample
4 | //
5 | // Created by Dominik Hádl on 17/04/16.
6 | // Copyright © 2016 Nodes ApS. All rights reserved.
7 | //
8 |
9 | import Serpent
10 |
11 | struct NameInfo {
12 | var title = ""
13 | var first = ""
14 | var last = ""
15 | }
16 |
17 | extension NameInfo: Serializable {
18 | init(dictionary: NSDictionary?) {
19 | title <== (self, dictionary, "title")
20 | first <== (self, dictionary, "first")
21 | last <== (self, dictionary, "last")
22 | }
23 |
24 | func encodableRepresentation() -> NSCoding {
25 | let dict = NSMutableDictionary()
26 | (dict, "title") <== title
27 | (dict, "first") <== first
28 | (dict, "last") <== last
29 | return dict
30 | }
31 | }
32 |
33 | extension NameInfo {
34 | var nameString: String {
35 | return "\(first.capitalized) \(last.capitalized)"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/Models/ProfilePicture.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProfilePicture.swift
3 | // SerpentExample
4 | //
5 | // Created by Dominik Hádl on 17/04/16.
6 | // Copyright © 2016 Nodes ApS. All rights reserved.
7 | //
8 |
9 | import Serpent
10 |
11 | struct ProfilePicture {
12 | var thumbnail: URL?
13 | var medium: URL?
14 | var large: URL?
15 | }
16 |
17 | extension ProfilePicture: Serializable {
18 | init(dictionary: NSDictionary?) {
19 | thumbnail <== (self, dictionary, "thumbnail")
20 | medium <== (self, dictionary, "medium")
21 | large <== (self, dictionary, "large")
22 | }
23 |
24 | func encodableRepresentation() -> NSCoding {
25 | let dict = NSMutableDictionary()
26 | (dict, "thumbnail") <== thumbnail
27 | (dict, "medium") <== medium
28 | (dict, "large") <== large
29 | return dict
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/Models/User.swift:
--------------------------------------------------------------------------------
1 | //
2 | // User.swift
3 | // SerpentExample
4 | //
5 | // Created by Dominik Hádl on 17/04/16.
6 | // Copyright © 2016 Nodes ApS. All rights reserved.
7 | //
8 |
9 | import Serpent
10 |
11 | enum Gender: String {
12 | case Male = "male"
13 | case Female = "female"
14 | }
15 |
16 | struct User {
17 | var gender = Gender.Male
18 | var name = NameInfo()
19 |
20 | var location = Location()
21 |
22 | var email = ""
23 | var loginInfo = LoginInfo() // <-login
24 |
25 | var registrationDate = 0 // <-registered
26 | var birthDate = 0 // <-dob
27 |
28 | var phoneNumber = "" // <-phone
29 | var cellNumber = "" // <-cell
30 |
31 | var identification = Identification() // <-id
32 |
33 | var picture = ProfilePicture()
34 | }
35 |
36 | extension User: Serializable {
37 | init(dictionary: NSDictionary?) {
38 | gender <== (self, dictionary, "gender")
39 | name <== (self, dictionary, "name")
40 | location <== (self, dictionary, "location")
41 | email <== (self, dictionary, "email")
42 | loginInfo <== (self, dictionary, "login")
43 | registrationDate <== (self, dictionary, "registered")
44 | birthDate <== (self, dictionary, "dob")
45 | phoneNumber <== (self, dictionary, "phone")
46 | cellNumber <== (self, dictionary, "cell")
47 | identification <== (self, dictionary, "id")
48 | picture <== (self, dictionary, "picture")
49 | }
50 |
51 | func encodableRepresentation() -> NSCoding {
52 | let dict = NSMutableDictionary()
53 | (dict, "gender") <== gender
54 | (dict, "name") <== name
55 | (dict, "location") <== location
56 | (dict, "email") <== email
57 | (dict, "login") <== loginInfo
58 | (dict, "registered") <== registrationDate
59 | (dict, "dob") <== birthDate
60 | (dict, "phone") <== phoneNumber
61 | (dict, "cell") <== cellNumber
62 | (dict, "id") <== identification
63 | (dict, "picture") <== picture
64 | return dict
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/User Interface/Cells/UserListCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserListCell.swift
3 | // SerpentExample
4 | //
5 | // Created by Dominik Hádl on 17/04/16.
6 | // Copyright © 2016 Nodes ApS. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Alamofire
11 |
12 | class UserListCell: UITableViewCell {
13 |
14 | static let reuseIdentifier = "UserListCell"
15 | static let staticHeight: CGFloat = 72.0
16 |
17 | @IBOutlet weak var profileImageView: UIImageView!
18 | @IBOutlet weak var nameLabel: UILabel!
19 | @IBOutlet weak var emailLabel: UILabel!
20 |
21 | override func awakeFromNib() {
22 | super.awakeFromNib()
23 |
24 | profileImageView.layer.cornerRadius = 2
25 | profileImageView.layer.masksToBounds = true
26 | }
27 |
28 | override func prepareForReuse() {
29 | super.prepareForReuse()
30 | profileImageView.image = nil
31 | nameLabel.text = nil
32 | emailLabel.text = nil
33 | }
34 | }
35 |
36 | // MARK: - Populate -
37 |
38 | extension UserListCell {
39 | func populateWithUser(_ user: User) {
40 | // Set image
41 | if let thumbnail = user.picture.thumbnail {
42 | profileImageView.imageFromUrl(thumbnail)
43 | }
44 |
45 | // Set name and email
46 | nameLabel.text = user.name.nameString
47 | emailLabel.text = user.email
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/User Interface/Cells/UserListCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/User Interface/HomeVC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomeVC.swift
3 | // Serpent Example
4 | //
5 | // Created by Dominik Hádl on 17/04/16.
6 | // Copyright © 2016 Nodes ApS. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class HomeVC: UIViewController, UITableViewDataSource, UITableViewDelegate {
12 |
13 | @IBOutlet weak var tableView: UITableView!
14 | var users: [User] = []
15 |
16 | override func viewDidLoad() {
17 | super.viewDidLoad()
18 |
19 | tableView.register(UINib(nibName: UserListCell.reuseIdentifier, bundle: nil),
20 | forCellReuseIdentifier: UserListCell.reuseIdentifier)
21 |
22 | reloadData()
23 | }
24 |
25 | // MARK: - Callbacks -
26 |
27 | func reloadData() {
28 | ConnectionManager.fetchRandomUsers { (response) in
29 | switch response.result {
30 | case .success(let users):
31 | self.users = users
32 | self.tableView.reloadData()
33 | case .failure(let error):
34 | let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
35 | alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
36 | self.present(alert, animated: true, completion: nil)
37 | }
38 | }
39 | }
40 |
41 | @IBAction func refreshPressed(_ sender: UIBarButtonItem) {
42 | reloadData()
43 | }
44 |
45 | // MARK: - UITableView Data Source & Delegate -
46 |
47 | func numberOfSections(in tableView: UITableView) -> Int {
48 | return 1
49 | }
50 |
51 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
52 | return users.count
53 | }
54 |
55 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
56 | return UserListCell.staticHeight
57 | }
58 |
59 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
60 | let cell = tableView.dequeueReusableCell(withIdentifier: UserListCell.reuseIdentifier, for: indexPath) as? UserListCell ?? UserListCell()
61 | cell.populateWithUser(users[indexPath.row])
62 | return cell
63 | }
64 |
65 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
66 | tableView.deselectRow(at: indexPath, animated: true)
67 | performSegue(withIdentifier: "ShowUserDetails", sender: indexPath)
68 | }
69 |
70 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
71 | if segue.identifier == "ShowUserDetails" {
72 | if let userVC = segue.destination as? UserDetailsVC, let indexPath = sender as? IndexPath {
73 | userVC.user = users[indexPath.row]
74 | }
75 | }
76 | }
77 | }
78 |
79 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Classes/User Interface/UserDetailsVC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserDetailsVC.swift
3 | // SerpentExample
4 | //
5 | // Created by Pablo Surfate on 4/18/16.
6 | // Copyright © 2016 Nodes ApS. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class UserDetailsVC: UIViewController {
12 |
13 | @IBOutlet weak var profileImageView: UIImageView!
14 | @IBOutlet weak var usernameLabel: UILabel!
15 | @IBOutlet weak var locationLabel: UILabel!
16 | @IBOutlet weak var genderLabel: UILabel!
17 | @IBOutlet weak var emailLabel: UILabel!
18 | @IBOutlet weak var phoneLabel: UILabel!
19 |
20 | var user = User()
21 |
22 | override func viewWillAppear(_ animated: Bool) {
23 | super.viewWillAppear(animated)
24 | updateUI()
25 | }
26 |
27 | fileprivate func updateUI() {
28 | // Set Labels
29 | usernameLabel.text = user.name.nameString
30 | genderLabel.text = user.gender.encodableRepresentation()
31 | locationLabel.text = user.location.city + ", " + user.location.state
32 | emailLabel.text = user.email
33 | phoneLabel.text = user.phoneNumber
34 |
35 | // Set image
36 | if let picture = user.picture.large {
37 | profileImageView.imageFromUrl(picture)
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "size" : "29x29",
15 | "idiom" : "iphone",
16 | "filename" : "Icon-29@2x.png",
17 | "scale" : "2x"
18 | },
19 | {
20 | "size" : "29x29",
21 | "idiom" : "iphone",
22 | "filename" : "Icon-29@3x.png",
23 | "scale" : "3x"
24 | },
25 | {
26 | "size" : "40x40",
27 | "idiom" : "iphone",
28 | "filename" : "Icon-40@2x.png",
29 | "scale" : "2x"
30 | },
31 | {
32 | "size" : "40x40",
33 | "idiom" : "iphone",
34 | "filename" : "Icon-40@3x.png",
35 | "scale" : "3x"
36 | },
37 | {
38 | "size" : "60x60",
39 | "idiom" : "iphone",
40 | "filename" : "Icon-60@2x.png",
41 | "scale" : "2x"
42 | },
43 | {
44 | "size" : "60x60",
45 | "idiom" : "iphone",
46 | "filename" : "Icon-60@3x.png",
47 | "scale" : "3x"
48 | }
49 | ],
50 | "info" : {
51 | "version" : 1,
52 | "author" : "xcode"
53 | }
54 | }
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/Logo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Logo.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Logo@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Logo@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/Logo.imageset/Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/Logo.imageset/Logo.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/Logo.imageset/Logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/Logo.imageset/Logo@2x.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/Logo.imageset/Logo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/Logo.imageset/Logo@3x.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/LogoText.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LogoText.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LogoText@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LogoText@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/LogoText.imageset/LogoText.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/LogoText.imageset/LogoText.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/LogoText.imageset/LogoText@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/LogoText.imageset/LogoText@2x.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Resources/Assets.xcassets/LogoText.imageset/LogoText@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/Example/SerpentExample/Resources/Assets.xcassets/LogoText.imageset/LogoText@3x.png
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Storyboards/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Storyboards/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
99 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
121 |
127 |
133 |
139 |
145 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
--------------------------------------------------------------------------------
/Serpent/Example/SerpentExample/Supporting Files/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Serpent
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UIRequiredDeviceCapabilities
32 |
33 | armv7
34 |
35 | UISupportedInterfaceOrientations
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationLandscapeLeft
39 | UIInterfaceOrientationLandscapeRight
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/Serpent/Serpent.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Serpent/Serpent.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Serpent/Serpent.xcodeproj/xcshareddata/xcschemes/Serpent OSX.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/Serpent/Serpent.xcodeproj/xcshareddata/xcschemes/Serpent iOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/Serpent/Serpent.xcodeproj/xcshareddata/xcschemes/Serpent tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
82 |
83 |
89 |
90 |
91 |
92 |
94 |
95 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/Serpent/Serpent.xcodeproj/xcshareddata/xcschemes/Serpent 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 |
--------------------------------------------------------------------------------
/Serpent/Serpent/Classes/Extensions/AlamofireExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AlamofireExtension.swift
3 | // Serializable
4 | //
5 | // Created by Kasper Welner on 08/03/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 |
12 | public struct Parser {
13 | public static let APICallSucceededNotification = "APICallSucceededNotification"
14 | }
15 |
16 | public extension Parser {
17 |
18 | /**
19 | Parse any generic object using the parsing handler.
20 | */
21 |
22 | internal static func serializer(_ parsingHandler: (( _ data: Any? ) -> T?)?) -> DataResponseSerializer {
23 | return DataResponseSerializer { (request, response, data, error) -> Result in
24 |
25 | //If we have an error here - pass it on
26 | if let error = error {
27 | return .failure(error)
28 | }
29 |
30 | let result = Request.serializeResponseJSON(options: .allowFragments, response: response, data: data, error: error)
31 |
32 | switch result {
33 | case let .success(value):
34 | if let parsedObject: T = parsingHandler?( value ) {
35 | NotificationCenter.default.post(name: Notification.Name(rawValue: APICallSucceededNotification), object: nil)
36 | return .success(parsedObject)
37 | } else {
38 | return .failure(NSError(domain: "Serializable.Parser", code: 2048, userInfo: [ NSLocalizedDescriptionKey : "Parsing block failed!", "JSONResponse" : value]))
39 | }
40 |
41 | case .failure(let error):
42 | //TODO: Add stubbed request for testing response not being empty
43 | var responseDict = [NSLocalizedDescriptionKey : "Serialization failed!"]
44 | responseDict["error"] = "\(error.localizedDescription)"
45 | responseDict["response"] = String(describing: response)
46 | return .failure(NSError(domain: "Serializable.Parser", code: 2048, userInfo: responseDict))
47 | }
48 | }
49 | }
50 |
51 | typealias Unwrapper = (_ sourceDictionary: NSDictionary, _ expectedType:Any) -> Any?
52 |
53 | /**
54 | The default unwrapper. Default implementation just passes data straight through.
55 | */
56 |
57 | static var defaultUnwrapper: Unwrapper = { a, b in return a }
58 | }
59 |
60 |
61 | //MARK: - Wrapper methods
62 |
63 | public extension Alamofire.DataRequest
64 | {
65 | /**
66 | Adds a handler that attempts to parse the result of the request into a **Decodable**
67 |
68 | - parameter completionHandler:A closure that is invoked when the request is finished
69 |
70 | - parameter unwrapper: A closure that extracts the data to be parsed from the JSON response data.
71 | The default implementation checks for a "data" field in the JSON response, then checks for a field
72 | with the same name as the target model. If not found, it passes the JSON response straight through.
73 |
74 | - returns: The request
75 | */
76 | @discardableResult
77 | func responseSerializable(_ completionHandler: @escaping (DataResponse) -> Void,
78 | unwrapper:@escaping Parser.Unwrapper = Parser.defaultUnwrapper) -> Self {
79 | let serializer = Parser.serializer { (data: Any?) -> T? in
80 |
81 | if let sourceDictionary = data as? NSDictionary {
82 | let unwrappedDictionary = unwrapper(sourceDictionary, T.self) as? NSDictionary ?? sourceDictionary
83 | return T(dictionary: unwrappedDictionary) as T?
84 | } else if let array = data as? NSArray, array.count == 1, let dictionary = array[0] as? NSDictionary {
85 | let unwrapped = unwrapper(dictionary, T.self) as? NSDictionary ?? dictionary
86 | return T(dictionary: unwrapped) as T?
87 | }
88 |
89 | return nil
90 | }
91 |
92 | return validate().response(responseSerializer: serializer, completionHandler: completionHandler)
93 | }
94 |
95 | /**
96 | Adds a handler that attempts to parse the result of the request into an array of **Decodable**
97 |
98 | - parameter completionHandler:A closure that is invoked when the request is finished
99 |
100 | - parameter unwrapper: A closure that extracts the data to be parsed from the JSON response data.
101 | The default implementation checks for a "data" field in the JSON response, then checks for a field
102 | with the same name as the target model. If not found, it passes the JSON response straight through.
103 |
104 | - returns: The request
105 | */
106 | @discardableResult
107 | func responseSerializable(_ completionHandler: @escaping (DataResponse<[T]>) -> Void,
108 | unwrapper:@escaping Parser.Unwrapper = Parser.defaultUnwrapper) -> Self {
109 |
110 | let serializer = Parser.serializer { (data: Any?) -> [T]? in
111 | if let dataDict = data as? NSDictionary, let unwrappedArray = unwrapper(dataDict, T.self) as? NSArray {
112 | return T.array(unwrappedArray)
113 | } else if let array = data as? NSArray {
114 | return T.array(array)
115 | }
116 |
117 | return nil
118 | }
119 |
120 | return validate().response(responseSerializer: serializer, completionHandler: completionHandler)
121 | }
122 |
123 | /**
124 | Convenience method for a handler that does not need to parse the results of the network request.
125 |
126 | - parameter completionHandler:A closure that is invoked when the request is finished
127 |
128 | - returns: The request
129 | */
130 |
131 | @discardableResult
132 | func responseSerializable(_ completionHandler: @escaping (DataResponse) -> Void) -> Self {
133 | return validate().responseJSON(completionHandler: completionHandler)
134 | }
135 |
136 | }
137 | // Convenience type for network requests with no response data
138 | public typealias NilSerializable = Any
139 |
--------------------------------------------------------------------------------
/Serpent/Serpent/Classes/Extensions/CashierExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CashierExtension.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 12/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Cashier
11 |
12 | public extension Cashier {
13 |
14 | /**
15 | Set an `object` to the `BridgingBox` and link it with a `key`.
16 |
17 | - Parameters:
18 | - object: Generic type which confroms with `Serializable`.
19 | - key: Key as a `String`.
20 | */
21 | func setSerializable(_ object: T, forKey key: String) where T: Serializable {
22 | let box = BridgingBox(object)
23 | self.setObject(box, forKey: key)
24 | BridgingBox.sharedBoxCache[self.id+key] = object
25 | }
26 |
27 | /**
28 | Set an `array` to the `BridgingBox` and link it with a `key`.
29 |
30 | - Parameters:
31 | - object: Generic type which confroms with `_ArrayType`.
32 | - key: Key as a `String`.
33 | */
34 | func setSerializable(_ object: T, forKey key: String) where T: Sequence, T.Iterator.Element: Serializable {
35 | let boxedArray = object.map { BridgingBox($0) }
36 | self.setObject(boxedArray, forKey: key)
37 | BridgingBox.sharedBoxCache[self.id+key] = object
38 | }
39 |
40 | /**
41 | Check first if an object with the `key` from `BridgingBox sharedBoxCache` exists and if the `key` is valid.
42 | If an object for that `key` exisits and the `key` is valid, return the onject of `BridgingBox sharedBoxCache` for that key, if not return nil.
43 |
44 | If the object for that `key` of `BridgingBox sharedBoxCache` does not exist check if an `objectForKey` as `BridgingBox` with that `key` exists. Else return nil.
45 | If the object exisits get the value of that `BridgingBox` and assign it to `BridgingBox.sharedBoxCache` and return the object.
46 |
47 | - Parameter key: `Key` for `BridgingBox` or `objectForKey` as `String`.
48 | - Returns: Generic type stored value/object that conforms with `Serializable` or return `nil`.
49 | */
50 | func serializableForKey(_ key: String) -> T? where T:Serializable {
51 | if let cachedSerializable = BridgingBox.sharedBoxCache[self.id+key] as? T {
52 | return self.object(forKeyIsValid: key) ? cachedSerializable : nil
53 | }
54 |
55 | guard let box:BridgingBox = self.object(forKey: key) as? BridgingBox else {
56 | return nil
57 | }
58 |
59 | let finalVal:T? = box.value()
60 | BridgingBox.sharedBoxCache[self.id+key] = finalVal
61 | return finalVal
62 | }
63 |
64 |
65 | /**
66 | Check first if an array with the `key` of `BridgingBox sharedBoxCache` exists and if the `key` is valid.
67 | If an array exisits for that `key` and the `key` is valid return the `BridgingBox sharedBoxCache` for that key, if not it will return nil.
68 |
69 | If the array for that `key` of `BridgingBox sharedBoxCache` does not exists check if an `objectForKey` as an `array` with that `key` exists that holds objects of `BridgingBox`. Else return nil.
70 | If the array exists get the values of that `BridgingBox` and append them to a `returnArray` and assign the array to `BridgingBox.sharedBoxCache` with the defined `key` and return the `array`.
71 |
72 | - Parameter key: `Key` for `BridgingBox` as `String`.
73 | - Returns: Array that holds object that conforms with `Serializable` or return `nil`.
74 | */
75 | func serializableForKey(_ key: String) -> [T]? where T:Serializable {
76 | if let cachedSerializable = BridgingBox.sharedBoxCache[self.id+key] as? [T] {
77 | return self.object(forKeyIsValid: key) ? cachedSerializable : nil
78 | }
79 |
80 | guard let box = self.object(forKey: key) as? [BridgingBox] else {
81 | return nil
82 | }
83 |
84 | /// Why did I not just use the Map() function? Because it results in a Segmentation fault 11 from the compiler! Thanks, Jobs!!! :-(
85 |
86 | var returnArray = [T]()
87 | for boxval in box {
88 | if let val:T = boxval.value() {
89 | returnArray.append(val)
90 | }
91 | }
92 |
93 | BridgingBox.sharedBoxCache[self.id+key] = returnArray
94 | return returnArray
95 | }
96 |
97 | /**
98 | Clears all saved disk data and optionally purges all memory caches.
99 |
100 | - parameter purgeMemoryCaches: If set to `true`, it will also purge all memory caches, including
101 | the shared bridging box cache for objects adhering to `Serializable`.
102 | */
103 | func clearAllData(_ purgeMemoryCaches: Bool) {
104 | self.clearAllData()
105 |
106 | if purgeMemoryCaches {
107 | BridgingBox.sharedBoxCache.removeAll()
108 | }
109 | }
110 |
111 | /**
112 | Removes a serializable value from saved disk, and optionally the memory cache.
113 |
114 | - parameter key: `Key` for `BridgingBox` as `String`.
115 | - parameter purgeMemoryCache: If set to `true`, it will also purge the memory cache.
116 | */
117 | func deleteSerializableForKey(_ key: String, purgeMemoryCache purge: Bool = true) {
118 | if purge {
119 | BridgingBox.sharedBoxCache.removeValue(forKey: self.id+key)
120 | }
121 | self.deleteObject(forKey: key)
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/Serpent/Serpent/Classes/Extensions/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Extensions.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 16/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - Protocols -
12 | // MARK: String Initializable
13 |
14 | public protocol StringInitializable {
15 | static func fromString(_ string: String) -> T?
16 | func stringRepresentation() -> String
17 | }
18 |
19 | extension URL: StringInitializable {
20 | public static func fromString(_ string: String) -> T? {
21 | return self.init(string: string) as? T
22 | }
23 |
24 | public func stringRepresentation() -> String {
25 | return self.absoluteString
26 | }
27 | }
28 |
29 | extension Date: StringInitializable {
30 | static fileprivate let internalDateFormatter = DateFormatter()
31 | static fileprivate let allowedDateFormats = ["yyyy-MM-dd'T'HH:mm:ssZZZZZ", "yyyy-MM-dd'T'HH:mm:ss", "yyyy-MM-dd"]
32 | static public var customDateFormats: [String] = []
33 |
34 | public static func fromString(_ string: String) -> T? {
35 | for format in allowedDateFormats + customDateFormats {
36 | internalDateFormatter.dateFormat = format
37 | if let date = internalDateFormatter.date(from: string) as? T {
38 | return date
39 | }
40 | }
41 |
42 | return nil
43 | }
44 |
45 | public func stringRepresentation() -> String {
46 | Date.internalDateFormatter.dateFormat = Date.allowedDateFormats.first
47 | return Date.internalDateFormatter.string(from: self)
48 | }
49 | }
50 |
51 | // MARK: Hex Initializable
52 | #if os(OSX)
53 | import Cocoa
54 | typealias Color = NSColor
55 | #else
56 | import UIKit
57 | typealias Color = UIColor
58 | #endif
59 |
60 | public protocol HexInitializable {
61 | static func fromHexString(_ hexString: String) -> T?
62 | }
63 |
64 | extension Color: HexInitializable {
65 |
66 | public static func fromHexString(_ hexString: String) -> T? {
67 | let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
68 | var int = UInt32()
69 | let a, r, g, b: UInt32
70 |
71 | guard Scanner(string: hex).scanHexInt32(&int) else {
72 | return nil
73 | }
74 |
75 | switch hex.count {
76 | // RGB (12-bit)
77 | case 3:
78 | (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
79 | // RRGGBB (24-bit)
80 | case 6:
81 | (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
82 | // ARGB (32-bit)
83 | case 8:
84 | (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
85 | default:
86 | return nil
87 | }
88 |
89 | return self.init(red: CGFloat(r) / 255,
90 | green: CGFloat(g) / 255,
91 | blue: CGFloat(b) / 255,
92 | alpha: CGFloat(a) / 255) as? T
93 | }
94 | }
95 |
96 | // MARK: - Extensions -
97 | // MARK: SequenceType
98 |
99 | public extension Sequence where Iterator.Element:Encodable {
100 | func encodableRepresentation() -> [NSCoding] {
101 | return self.map { element in return element.encodableRepresentation() }
102 | }
103 | }
104 |
105 | // MARK: RawRepresentable
106 |
107 | public extension RawRepresentable {
108 | func encodableRepresentation() -> RawValue {
109 | return self.rawValue
110 | }
111 | }
112 |
113 |
114 |
--------------------------------------------------------------------------------
/Serpent/Serpent/Classes/Operators/Operator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Operator.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 16/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | // MARK: - Custom Operators -
13 |
14 | precedencegroup SerializablePrecedence {
15 | associativity: left
16 | }
17 |
18 | infix operator <== : SerializablePrecedence
19 |
20 | // For Encodable
21 | public func <==(left: (dict: NSMutableDictionary, key: String), right: T?) {
22 | left.dict.setValue(right, forKey: left.key)
23 | }
24 |
25 | public func <==(left: (dict: NSMutableDictionary, key: String), right: T?) {
26 | left.dict.setValue(right?.encodableRepresentation(), forKey: left.key)
27 | }
28 |
29 | public func <==(left: (dict: NSMutableDictionary, key: String), right: T?) {
30 | left.dict.setValue(right?.stringRepresentation(), forKey: left.key)
31 | }
32 |
33 | public func <==(left: (dict: NSMutableDictionary, key: String), right: T?) where T:Sequence, T.Iterator.Element: Encodable {
34 | left.dict.setValue(right?.encodableRepresentation(), forKey: left.key)
35 | }
36 |
37 | public func <==(left: (dict: NSMutableDictionary, key: String), right: T?) {
38 | left.dict.setValue(right?.encodableRepresentation(), forKey: left.key)
39 | }
40 |
41 | // For Decodable
42 |
43 | // Primitive
44 | public func <==(left: inout T?, right: (instance: S, dict: NSDictionary?, key: String)) where S: Keymappable {
45 | let value: T? = right.instance.mapped(right.dict, key: right.key)
46 | left = value ?? left
47 | }
48 |
49 | public func <==(left: inout T, right: (instance: S, dict: NSDictionary?, key: String)) where S: Keymappable {
50 | let value: T? = right.instance.mapped(right.dict, key: right.key)
51 | left = value ?? left
52 | }
53 |
54 | // Serializable
55 | public func <==(left: inout T?, right: (instance: S, dict: NSDictionary?, key: String)) where T: Decodable, S: Keymappable {
56 | let value: T? = right.instance.mapped(right.dict, key: right.key)
57 | left = value ?? left
58 | }
59 |
60 | public func <==(left: inout T, right: (instance: S, dict: NSDictionary?, key: String)) where T: Decodable, S: Keymappable {
61 | let value: T? = right.instance.mapped(right.dict, key: right.key)
62 | left = value ?? left
63 | }
64 |
65 | // Enum
66 | public func <==(left: inout T?, right: (instance: S, dict: NSDictionary?, key: String)) where T: RawRepresentable, S: Keymappable {
67 | let value: T? = right.instance.mapped(right.dict, key: right.key)
68 | left = value ?? left
69 | }
70 |
71 | public func <==(left: inout T, right: (instance: S, dict: NSDictionary?, key: String)) where T: RawRepresentable, S: Keymappable {
72 | let value: T? = right.instance.mapped(right.dict, key: right.key)
73 | left = value ?? left
74 | }
75 |
76 | public func <==(left: inout [T]?, right: (instance: S, dict: NSDictionary?, key: String)) where T: RawRepresentable, S: Keymappable {
77 | let value: [T]? = right.instance.mapped(right.dict, key: right.key)
78 | left = value ?? left
79 | }
80 |
81 | public func <==(left: inout [T], right: (instance: S, dict: NSDictionary?, key: String)) where T: RawRepresentable, S: Keymappable {
82 | let value: [T]? = right.instance.mapped(right.dict, key: right.key)
83 | left = value ?? left
84 | }
85 |
86 | // [Serializable]
87 | public func <==(left: inout T?, right: (instance: S, dict: NSDictionary?, key: String)) where T:Sequence, T.Iterator.Element: Decodable, S: Keymappable {
88 | let value: T? = right.instance.mapped(right.dict, key: right.key)
89 | left = value ?? left
90 | }
91 |
92 | public func <==(left: inout T, right: (instance: S, dict: NSDictionary?, key: String)) where T:Sequence, T.Iterator.Element: Decodable, S: Keymappable {
93 | let value: T? = right.instance.mapped(right.dict, key: right.key)
94 | left = value ?? left
95 | }
96 |
97 | // StringInitializable
98 |
99 | public func <==(left: inout T?, right: (instance: S, dict: NSDictionary?, key: String)) where T: StringInitializable, S: Keymappable {
100 | let value: T? = right.instance.mapped(right.dict, key: right.key)
101 | left = value ?? left
102 | }
103 |
104 | public func <==(left: inout T, right: (instance: S, dict: NSDictionary?, key: String)) where T: StringInitializable, S: Keymappable {
105 | let value: T? = right.instance.mapped(right.dict, key: right.key)
106 | left = value ?? left
107 | }
108 |
109 | // HexInitializable
110 |
111 | public func <==(left: inout T?, right: (instance: S, dict: NSDictionary?, key: String)) where T: HexInitializable, S: Keymappable {
112 | let value: T? = right.instance.mapped(right.dict, key: right.key)
113 | left = value ?? left
114 | }
115 |
116 | public func <==(left: inout T, right: (instance: S, dict: NSDictionary?, key: String)) where T: HexInitializable, S: Keymappable {
117 | let value: T? = right.instance.mapped(right.dict, key: right.key)
118 | left = value ?? left
119 | }
120 |
--------------------------------------------------------------------------------
/Serpent/Serpent/Classes/Other/BridgingBox.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Bridging.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 16/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | @objc open class BridgingBox : NSObject, NSCoding {
12 |
13 | static var sharedBoxCache = [String : Any]()
14 |
15 | fileprivate var internalValue: Encodable?
16 | open var dictValue:NSDictionary?
17 |
18 | /**
19 | Get value of the `NSDictionary` `dictValue` that will be or was archived and that conforms with `Serializable`.
20 |
21 | - returns: Value of type `Serializable` or `nil`.
22 | */
23 | open func value() -> T? {
24 | if let dictValue = dictValue , internalValue == nil {
25 | return T(dictionary: dictValue)
26 | }
27 |
28 | return internalValue as? T
29 | }
30 |
31 | public init(_ value: Encodable) {
32 | self.internalValue = value
33 | }
34 |
35 | required public init?(coder aDecoder: NSCoder) {
36 | super.init()
37 | dictValue = aDecoder.decodeObject(forKey: "dictValue") as? NSDictionary
38 | }
39 |
40 | open func encode(with aCoder: NSCoder) {
41 | if let value = internalValue {
42 | if dictValue == nil {
43 | dictValue = value.encodableRepresentation() as? NSDictionary
44 | }
45 |
46 | aCoder.encode(dictValue, forKey:"dictValue")
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Serpent/Serpent/Classes/Serpent.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Serializable.swift
3 | // NOCore
4 | //
5 | // Created by Kasper Welner on 22/01/15.
6 | // Copyright (c) 2015 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // MARK: - Serializable -
12 |
13 | public protocol Serializable: Decodable, Encodable, Keymappable {}
14 |
15 | // MARK: - Encodable -
16 |
17 | public protocol Encodable {
18 | func encodableRepresentation() -> NSCoding
19 | }
20 |
21 | // MARK: - Decodable -
22 |
23 | public protocol Decodable {
24 | init(dictionary:NSDictionary?)
25 | }
26 |
27 | public extension Decodable {
28 | static func array(_ source: Any?) -> [Self] {
29 | guard let source = source as? [NSDictionary] else {
30 | return [Self]()
31 | }
32 | return source.map {
33 | Self(dictionary: ($0))
34 | }
35 | }
36 | }
37 |
38 | private struct DefaultKeyMappings {
39 | fileprivate static let mappings = [String : String]()
40 | }
41 |
42 | // MARK: - Keymappable -
43 |
44 | public protocol Keymappable {}
45 |
46 | public extension Keymappable {
47 |
48 | /**
49 | Maps the content of value for **key** in **dictionary** to generic type **T**, conforming to **Serializable** protocol.
50 |
51 | - parameter dictionary: An optional dictionary containing values which should be parsed.
52 | - parameter key: ValueForKey will be called on the dictionary to extract the value to be parsed
53 |
54 | - returns: A mapped object conforming to *Serializable*, or nil if parsing failed
55 | */
56 | func mapped(_ dictionary: NSDictionary?, key: String) -> T? where T:Decodable {
57 |
58 | // Ensure the dictionary is not nil
59 | guard let dict = dictionary else { return nil }
60 |
61 | // Get the value from the dictionary for our key
62 | let sourceOpt = dict[key]
63 |
64 | // Check if we have the correct type and return it if possible
65 | if sourceOpt != nil && sourceOpt is NSDictionary {
66 | return T(dictionary: (sourceOpt as! NSDictionary))
67 | }
68 |
69 | return nil
70 | }
71 |
72 | /**
73 | Maps the content of value for **key** in **dictionary** to an array containing where elements is of generic type **T**, conforming to **Serializable** protocol.
74 |
75 | - parameter dictionary: An optional dictionary containing values which should be parsed.
76 | - parameter key: ValueForKey will be called on the dictionary to extract the value to be parsed.
77 |
78 | - returns: An array of mapped objects conforming to *Serializable*, or an empty array if parsing failed.
79 | */
80 | func mapped(_ dictionary: NSDictionary?, key: String) -> T? where T:Sequence, T.Iterator.Element: Decodable {
81 | // Ensure the dictionary is not nil and get the value from the dictionary for our key
82 | guard let dict = dictionary, let sourceOpt = dict[key] else { return nil }
83 |
84 | if sourceOpt is [NSDictionary] {
85 | let source = (sourceOpt as! [NSDictionary])
86 | let finalArray = source.map { T.Iterator.Element.init(dictionary: $0) } as? T
87 | return finalArray
88 | }
89 |
90 | return nil
91 | }
92 |
93 | /**
94 | A generic mapping function that will try to parse primitive types from the provided dictionary.
95 | Currently supported types are `Int`, `Float`, `Double`, `Bool`, `Char` and `String`.
96 |
97 | The `key` parameter will be first used to check value in custom input key mappings and if
98 | no value is found, then `key` is used as the key to get the value stored in `dictionary`.
99 |
100 | - parameter dictionary: An optional dictionary containing values which should be parsed.
101 | - parameter key: The key which will be used to get the actual key from input key mappings
102 | or as the actual key for the value being parsed from the dictionary.
103 |
104 | - returns: The value of primitive type `T` or `nil` if parsing was unsuccessful.
105 | */
106 | func mapped(_ dictionary: NSDictionary?, key: String) -> T? {
107 |
108 | // Ensure the dictionary is not nil
109 | guard let dict = dictionary else { return nil }
110 |
111 | // Get the value from the dictionary for our key
112 | let sourceOpt = dict[key]
113 |
114 | // Figure out what type is the value we got and parse accordingly
115 | switch sourceOpt {
116 |
117 | case (is T):
118 | return (sourceOpt as! T)
119 |
120 | case (is String) where T.self is Int.Type:
121 | let source = (sourceOpt as! String)
122 | return Int(source) as? T
123 |
124 | case (is String) where T.self is Double.Type:
125 | let source = (sourceOpt as! String)
126 | return Double(source) as? T
127 |
128 | case (is NSString) where T.self is Bool.Type:
129 | let source = (sourceOpt as! NSString)
130 | return source.boolValue as? T
131 |
132 | case (is String) where T.self is Character.Type:
133 | let source = (sourceOpt as! String)
134 | return Character(source) as? T
135 |
136 | case (is NSNumber) where T.self is String.Type:
137 | let source = (sourceOpt as! NSNumber)
138 | return String(describing: source) as? T
139 |
140 | case (is Double) where T.self is Float.Type:
141 | let source = (sourceOpt as! Double)
142 | return Float(source) as? T
143 |
144 | default:
145 | return nil
146 | }
147 | }
148 |
149 | /**
150 | A generic mapping function that will try to parse an object of type `T` from the string
151 | value contained in the provided dictionary.
152 |
153 | The `key` parameter will be first used to check value in custom input key mappings and if
154 | no value is found, then `key` is used as the key to get the value stored in `dictionary`.
155 |
156 | - parameter dictionary: An optional dictionary containing the array which should be parsed.
157 | - parameter key: The key which will be used to get the actual key from input key mappings
158 | or as the actual key for the value being parsed from the dictionary.
159 |
160 | - returns: The value of type `T` or `nil` if parsing was unsuccessful.
161 | */
162 | func mapped(_ dictionary: NSDictionary?, key: String) -> T? {
163 | if let dict = dictionary, let source = dict[key] as? String , source.isEmpty == false {
164 | return T.fromString(source)
165 | }
166 |
167 | return nil
168 | }
169 |
170 | /**
171 | A generic mapping function that will try to parse enumerations with raw value from the
172 | provided dictionary.
173 |
174 | This function internally uses a variant of the generic `mapped()` function used to parse
175 | primitive types, which means that only enums with raw value of primitive type are supported.
176 |
177 | The `key` parameter will be first used to check value in custom input key mappings and if
178 | no value is found, then `key` is used as the key to get the value stored in `dictionary`.
179 |
180 | - parameter dictionary: An optional dictionary containing values which should be parsed.
181 | - parameter key: The key which will be used to get the actual key from input key mappings
182 | or as the actual key for the value being parsed from the dictionary.
183 |
184 | - returns: The enumeration of enum type `T` or `nil` if parsing was unsuccessful or
185 | enumeration does not exist.
186 | */
187 | func mapped(_ dictionary: NSDictionary?, key: String) -> T? {
188 | guard let source: T.RawValue = self.mapped(dictionary, key: key) else {
189 | return nil
190 | }
191 |
192 | return T(rawValue: source)
193 | }
194 |
195 | /**
196 | A generic mapping function that will try to parse an array of enumerations with raw value from the
197 | array contained in the provided dictionary.
198 |
199 | The `key` parameter will be first used to check value in custom input key mappings and if
200 | no value is found, then `key` is used as the key to get the value stored in `dictionary`.
201 |
202 | - parameter dictionary: An optional dictionary containing the array which should be parsed.
203 | - parameter key: The key which will be used to get the actual key from input key mappings
204 | or as the actual key for the value being parsed from the dictionary.
205 |
206 | - returns: An array of enum type `T` or an empty array if parsing was unsuccessful.
207 | */
208 | func mapped(_ dictionary: NSDictionary?, key: String) -> T? where T:Sequence, T.Iterator.Element: RawRepresentable {
209 | if let dict = dictionary, let source = dict[key] as? [T.Iterator.Element.RawValue] {
210 | let finalArray = source.map { T.Iterator.Element.init(rawValue: $0)! }
211 | return (finalArray as! T)
212 | }
213 |
214 | return nil
215 | }
216 |
217 |
218 |
219 | /**
220 | A generic mapping function that will try to parse an object of type `T` from the hex string
221 | value contained in the provided dictionary.
222 |
223 | The `key` parameter will be first used to check value in custom input key mappings and if
224 | no value is found, then `key` is used as the key to get the value stored in `dictionary`.
225 |
226 | - parameter dictionary: An optional dictionary containing the array which should be parsed.
227 | - parameter key: The key which will be used to get the actual key from input key mappings
228 | or as the actual key for the value being parsed from the dictionary.
229 |
230 | - returns: The value of type `T` or `nil` if parsing was unsuccessful.
231 | */
232 | func mapped(_ dictionary: NSDictionary?, key: String) -> T? {
233 | guard let dict = dictionary, let source = dict[key] else {
234 | return nil
235 | }
236 |
237 | if let hexString = source as? String , hexString.isEmpty == false {
238 | return T.fromHexString(hexString)
239 | }
240 |
241 | return source as? T
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/Serpent/Serpent/Supporting Files/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 |
--------------------------------------------------------------------------------
/Serpent/Serpent/Supporting Files/Serpent.h:
--------------------------------------------------------------------------------
1 | //
2 | // Serpent.h
3 | // Serpent
4 | //
5 | // Created by Chris Combs on 09/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 | @import Foundation;
9 |
10 | //! Project version number for Serializable.
11 | FOUNDATION_EXPORT double SerializableVersionNumber;
12 |
13 | //! Project version string for Serializable.
14 | FOUNDATION_EXPORT const unsigned char SerializableVersionString[];
15 |
16 | // In this header, you should import all the public headers of your framework using statements like #import
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/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 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/CustomOperatorsTest.json:
--------------------------------------------------------------------------------
1 | {
2 | "string": "success",
3 | "second_string": "haha",
4 | "other_serializable": {
5 | "integer": 1,
6 | "optional_integer": 2,
7 | "optional_integer_with_default_value": 3,
8 | "string": "success",
9 | "optional_string": "optional success",
10 | "optional_string_with_default_value": "optional default success"
11 | },
12 | "some_serializable": {
13 | "integer": 1,
14 | "optional_integer": 2,
15 | "optional_integer_with_default_value": 3,
16 | "string": "success",
17 | "optional_string": "optional success",
18 | "optional_string_with_default_value": "optional default success"
19 | },
20 | "some_url" : "http://www.google.com",
21 | "some_enum" : 1,
22 | "some_array" : [
23 | {
24 | "integer": 1,
25 | "optional_integer": 2,
26 | "optional_integer_with_default_value": 3,
27 | "string": "success",
28 | "optional_string": "optional success",
29 | "optional_string_with_default_value": "optional default success"
30 | },
31 | {
32 | "integer": 1,
33 | "optional_integer": 2,
34 | "optional_integer_with_default_value": 3,
35 | "string": "success",
36 | "optional_string": "optional success",
37 | "optional_string_with_default_value": "optional default success"
38 | },
39 | ],
40 | "some_dictionary" : {
41 | "test" : "value",
42 | "integer" : 1,
43 | "float" : 1.023
44 | }
45 | }
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/CustomOperatorsTestModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomOperatorsModel.swift
3 | // NOCore
4 | //
5 | // Created by Dominik Hádl on 04/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Serpent
11 |
12 | enum Type: Int {
13 | case first = 0
14 | case second = 1
15 | }
16 |
17 | struct CustomOperatorsTestModel {
18 | var string: String = ""
19 | var secondString = ""
20 | var nilString: String?
21 | var otherSerializable: CustomOperatorsTestNestedModel?
22 | var someSerializable = CustomOperatorsTestNestedModel()
23 | var someUrl = URL(string: "")
24 | var someEnum: Type = .first
25 | var someArray: [CustomOperatorsTestNestedModel] = []
26 | var someDictionary: [String : AnyObject]?
27 | }
28 |
29 | extension CustomOperatorsTestModel: Serializable {
30 | init(dictionary: NSDictionary?) {
31 | string <== (self, dictionary, "string")
32 | secondString <== (self, dictionary, "second_string")
33 | nilString <== (self, dictionary, "nil_string")
34 | otherSerializable <== (self, dictionary, "other_serializable")
35 | someSerializable <== (self, dictionary, "some_serializable")
36 | someUrl <== (self, dictionary, "some_url")
37 | someEnum <== (self, dictionary, "some_enum")
38 | someArray <== (self, dictionary, "some_array")
39 | someDictionary <== (self, dictionary, "some_dictionary")
40 | }
41 |
42 | func encodableRepresentation() -> NSCoding {
43 | let dict = NSMutableDictionary()
44 | (dict, "string") <== string
45 | (dict, "second_string") <== secondString
46 | (dict, "nil_string") <== nilString
47 | (dict, "other_serializable") <== otherSerializable
48 | (dict, "some_serializable") <== someSerializable
49 | (dict, "some_url") <== someUrl
50 | (dict, "some_enum") <== someEnum
51 | (dict, "some_array") <== someArray
52 | (dict, "some_dictionary") <== someDictionary
53 | return dict
54 | }
55 | }
56 |
57 | struct CustomOperatorsTestNestedModel {
58 | var integer: Int = 0
59 | var optionalInteger: Int?
60 | var optionalIntegerWithDefaultValue: Int? = 0
61 |
62 | var double: Double = 0.0
63 | var optionalDouble: Double?
64 | var optionalDoubleWithDefaultValue: Double? = 0.0
65 |
66 | var string: String = ""
67 | var optionalString: String?
68 | var optionalStringWithDefaultValue: String? = ""
69 | }
70 |
71 | extension CustomOperatorsTestNestedModel: Equatable {}
72 |
73 | func ==(lhs: CustomOperatorsTestNestedModel, rhs: CustomOperatorsTestNestedModel) -> Bool {
74 | return lhs.string == rhs.string
75 | }
76 |
77 | extension CustomOperatorsTestNestedModel:Serializable {
78 | init(dictionary: NSDictionary?) {
79 | integer <== (self, dictionary, "integer")
80 | optionalInteger <== (self, dictionary, "optional_integer")
81 | optionalIntegerWithDefaultValue <== (self, dictionary, "optional_integer_with_default_value")
82 | double <== (self, dictionary, "double")
83 | optionalDouble <== (self, dictionary, "optional_double")
84 | optionalDoubleWithDefaultValue <== (self, dictionary, "optional_doubleWithDefaultValue")
85 | string <== (self, dictionary, "string")
86 | optionalString <== (self, dictionary, "optional_string")
87 | optionalStringWithDefaultValue <== (self, dictionary, "optional_string_with_default_value")
88 | }
89 |
90 | func encodableRepresentation() -> NSCoding {
91 | let dict = NSMutableDictionary()
92 | (dict, "integer") <== integer
93 | (dict, "optional_integer") <== optionalInteger
94 | (dict, "optional_integer_with_default_value") <== optionalIntegerWithDefaultValue
95 | (dict, "double") <== double
96 | (dict, "optional_double") <== optionalDouble
97 | (dict, "optional_doubleWithDefaultValue") <== optionalDoubleWithDefaultValue
98 | (dict, "string") <== string
99 | (dict, "optional_string") <== optionalString
100 | (dict, "optional_string_with_default_value") <== optionalStringWithDefaultValue
101 | return dict
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/DecodableModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DecodableModel.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 16/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Serpent
11 |
12 | struct DecodableModel {
13 | var id = 0
14 | var name = ""
15 | var text = ""
16 | }
17 |
18 | extension DecodableModel:Serializable {
19 | init(dictionary: NSDictionary?) {
20 | id <== (self, dictionary, key: "id")
21 | name <== (self, dictionary, key: "name")
22 | text <== (self, dictionary, key: "text")
23 | }
24 |
25 | func encodableRepresentation() -> NSCoding {
26 | let dict = NSMutableDictionary()
27 | (dict, "id") <== id
28 | (dict, "name") <== name
29 | (dict, "text") <== text
30 | return dict
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/DecodableTest.json:
--------------------------------------------------------------------------------
1 | {
2 | "models" : [
3 | {
4 | "id" : 1,
5 | "name" : "Hello",
6 | "text" : "some text"
7 | },
8 | {
9 | "id" : 2,
10 | "name" : "Hello2",
11 | "text" : "some text2"
12 | },
13 | {
14 | "id" : 3,
15 | "name" : "Hello3",
16 | "text" : "some text3"
17 | },
18 | {
19 | "id" : 4,
20 | "name" : "Hello4",
21 | "text" : "some text4"
22 | }
23 | ],
24 | "model" :
25 | {
26 | "id" : 5,
27 | "name" : "Hello5",
28 | "text" : "some text5"
29 | }
30 | }
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/EnumsTest.json:
--------------------------------------------------------------------------------
1 | {
2 | "string_enum" : "value1",
3 | "optional_string_enum" : "DifferentValue",
4 | "optional_string_enum_with_default_value" : "value1",
5 | "wrong_type_string_enum" : 1,
6 |
7 | "double_enum" : 123.456,
8 | "optional_double_enum" : 0,
9 | "optional_double_enum_with_default_value" : -9453.5147684,
10 | "wrong_type_double_enum" : "someval",
11 |
12 | "string_enum_array" : [ "value1", "DifferentValue"],
13 | "optional_string_enum_array" : [ "value1", "DifferentValue"],
14 | "optional_string_enum_array_with_default_value" : [ "value1", "DifferentValue"],
15 | "wrong_type_string_enum_array" : [ 1, "LOL"],
16 |
17 | "double_enum_array" : [ 123.456, -9453.5147684],
18 | "optional_double_enum_array" : [ 123.456, -9453.5147684],
19 | "optional_double_enum_array_with_default_value" : [ 123.456, -9453.5147684],
20 | "wrong_type_double_enum_array" : [ 1.456, "LOL"],
21 | }
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/EnumsTestModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PrimitivesTestModel.swift
3 | // NOCore
4 | //
5 | // Created by Dominik Hádl on 17/01/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Serpent
11 |
12 | enum StringEnum: String {
13 | case Value1 = "value1"
14 | case DifferentValue = "DifferentValue"
15 | case NoneValue
16 | }
17 |
18 | enum DoubleEnum: Double {
19 | case noneValue
20 | case value1 = 123.456
21 | case differentValue = -9453.5147684
22 | }
23 |
24 | struct EnumsTestModel {
25 | var stringEnum: StringEnum = .NoneValue
26 | var optionalStringEnum: StringEnum?
27 | var optionalStringEnumWithDefaultValue: StringEnum? = .NoneValue
28 | var nonExistentStringEnum: StringEnum?
29 | var wrongTypeStringEnum: StringEnum?
30 |
31 | var stringEnumArray: [StringEnum] = [StringEnum]()
32 | var optionalStringEnumArray: [StringEnum]?
33 | var optionalStringEnumArrayWithDefaultValue: [StringEnum]? = [StringEnum]()
34 | var nonExistentStringEnumArray: [StringEnum]?
35 | var wrongTypeStringEnumArray: [StringEnum]?
36 |
37 | var doubleEnum: DoubleEnum = .noneValue
38 | var optionalDoubleEnum: DoubleEnum?
39 | var optionalDoubleEnumWithDefaultValue: DoubleEnum? = .noneValue
40 | var nonExistentDoubleEnum: DoubleEnum?
41 | var wrongTypeDoubleEnum: DoubleEnum?
42 |
43 | var doubleEnumArray: [DoubleEnum] = [DoubleEnum]()
44 | var optionalDoubleEnumArray: [DoubleEnum]?
45 | var optionalDoubleEnumArrayWithDefaultValue: [DoubleEnum]? = [DoubleEnum]()
46 | var nonExistentDoubleEnumArray: [DoubleEnum]?
47 | var wrongTypeDoubleEnumArray: [DoubleEnum]?
48 | }
49 |
50 | extension EnumsTestModel:Serializable {
51 | init(dictionary: NSDictionary?) {
52 | stringEnum <== (self, dictionary, key: "string_enum")
53 | optionalStringEnum <== (self, dictionary, key: "optional_string_enum")
54 | optionalStringEnumWithDefaultValue <== (self, dictionary, key: "optional_string_enum_with_default_value")
55 | nonExistentStringEnum <== (self, dictionary, key: "non_existent_string_enum")
56 | wrongTypeStringEnum <== (self, dictionary, key: "wrong_type_string_enum")
57 |
58 | stringEnumArray <== (self, dictionary, key: "string_enum_array")
59 | optionalStringEnumArray <== (self, dictionary, key: "optional_string_enum_array")
60 | optionalStringEnumArrayWithDefaultValue <== (self, dictionary, key: "optional_string_enum_array_with_default_value")
61 | nonExistentStringEnumArray <== (self, dictionary, key: "non_existent_string_enum_array")
62 | wrongTypeStringEnumArray <== (self, dictionary, key: "wrong_type_string_enum_array")
63 |
64 | doubleEnum <== (self, dictionary, key: "double_enum")
65 | optionalDoubleEnum <== (self, dictionary, key: "optional_double_enum")
66 | optionalDoubleEnumWithDefaultValue <== (self, dictionary, key: "optional_double_enum_with_default_value")
67 | nonExistentDoubleEnum <== (self, dictionary, key: "non_existent_double_enum")
68 | wrongTypeDoubleEnum <== (self, dictionary, key: "wrong_type_double_enum")
69 |
70 | doubleEnumArray <== (self, dictionary, key: "double_enum_array")
71 | optionalDoubleEnumArray <== (self, dictionary, key: "optional_double_enum_array")
72 | optionalDoubleEnumArrayWithDefaultValue <== (self, dictionary, key: "optional_double_enum_array_with_default_value")
73 | nonExistentDoubleEnumArray <== (self, dictionary, key: "non_existent_double_enum_array")
74 | wrongTypeDoubleEnumArray <== (self, dictionary, key: "wrong_type_double_enum_array")
75 | }
76 |
77 | func encodableRepresentation() -> NSCoding {
78 | let dict = NSMutableDictionary()
79 | (dict, "string_enum") <== stringEnum
80 | (dict, "optional_string_enum") <== optionalStringEnum
81 | (dict, "optional_string_enum_with_default_value") <== optionalStringEnumWithDefaultValue
82 | (dict, "non_existent_string_enum") <== nonExistentStringEnum
83 | (dict, "wrong_type_string_enum") <== wrongTypeStringEnum
84 | (dict, "string_enum_array") <== stringEnumArray
85 | (dict, "optional_string_enum_array") <== optionalStringEnumArray
86 | (dict, "optional_string_enum_array_with_default_value") <== optionalStringEnumArrayWithDefaultValue
87 | (dict, "non_existent_string_enum") <== nonExistentStringEnum
88 | (dict, "wrong_type_string_enum") <== wrongTypeStringEnum
89 | (dict, "double_enum") <== doubleEnum
90 | (dict, "optional_double_enum") <== optionalDoubleEnum
91 | (dict, "optional_double_enum_with_default_value") <== optionalDoubleEnumWithDefaultValue
92 | (dict, "non_existent_double_enum") <== nonExistentDoubleEnum
93 | (dict, "wrong_type_double_enum") <== wrongTypeDoubleEnum
94 | (dict, "double_enum_array") <== doubleEnumArray
95 | (dict, "optional_double_enum_array") <== optionalDoubleEnumArray
96 | (dict, "optional_double_enum_array_with_default_value") <== optionalDoubleEnumArrayWithDefaultValue
97 | (dict, "non_existent_double_enum") <== nonExistentDoubleEnum
98 | (dict, "wrong_type_double_enum") <== wrongTypeDoubleEnum
99 | return dict
100 | }
101 | }
102 |
103 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/HexInitializableTest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_color" : "55AACC",
3 | "full_color" : "#55AACC",
4 | "bad_color" : "0066",
5 | "not_color" : {
6 | "color" : "asdf"
7 | },
8 | "three_color" : "5AC",
9 | "invalid_hex_color" : "SSg12F",
10 | "alpha_rgb" : "CC55AACC"
11 | }
12 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/HexInitializableTestModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HexInitializableTest.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 10/03/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Serpent
11 | #if os(OSX)
12 | import Cocoa
13 | typealias Color = NSColor
14 | #else
15 | import UIKit
16 | typealias Color = UIColor
17 | #endif
18 |
19 | struct HexInitializableTestModel {
20 | var shortColor = Color.red
21 | var fullColor: Color?
22 | var badColor: Color?
23 | var notColor: Color?
24 | var invalidHexColor: Color?
25 | var threeColor: Color?
26 | var alphaRGB: Color?
27 | }
28 |
29 | extension HexInitializableTestModel: Serializable {
30 | init(dictionary: NSDictionary?) {
31 | shortColor <== (self, dictionary, "short_color")
32 | fullColor <== (self, dictionary, "full_color")
33 | badColor <== (self, dictionary, "bad_color")
34 | notColor <== (self, dictionary, "not_color")
35 | invalidHexColor <== (self, dictionary, "invalid_hex_color")
36 | threeColor <== (self, dictionary, "three_color")
37 | alphaRGB <== (self, dictionary, "alpha_rgb")
38 | }
39 |
40 | func encodableRepresentation() -> NSCoding {
41 | let dict = NSMutableDictionary()
42 | (dict, "short_color") <== shortColor
43 | (dict, "full_color") <== fullColor
44 | (dict, "bad_color") <== badColor
45 | (dict, "not_color") <== notColor
46 | (dict, "invalid_hex_color") <== invalidHexColor
47 | (dict, "three_color") <== threeColor
48 | (dict, "alpha_rgb") <== alphaRGB
49 | return dict
50 | }
51 | }
52 |
53 | struct HexInitializableTestNilModel {
54 | var someColor = Color.red
55 | }
56 |
57 | extension HexInitializableTestNilModel: Serializable {
58 | init(dictionary: NSDictionary?) {
59 | someColor <== (self, dictionary, "some_color")
60 | }
61 |
62 | func encodableRepresentation() -> NSCoding {
63 | let dict = NSMutableDictionary()
64 | (dict, "some_color") <== someColor
65 | return dict
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/NetworkTestModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkTestModel.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 18/03/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Serpent
10 |
11 | struct NetworkTestModel {
12 | var origin = ""
13 | var url: NSURL?
14 | }
15 |
16 | extension NetworkTestModel: Serializable {
17 | init(dictionary: NSDictionary?) {
18 | origin <== (self, dictionary, "origin")
19 | url <== (self, dictionary, "url")
20 | }
21 |
22 | func encodableRepresentation() -> NSCoding {
23 | let dict = NSMutableDictionary()
24 | (dict, "origin") <== origin
25 | (dict, "url") <== url
26 | return dict
27 | }
28 | }
29 |
30 | struct BadModel {
31 | var something = ""
32 | }
33 |
34 | extension BadModel: Serializable {
35 | init(dictionary: NSDictionary?) {
36 | something <== (self, dictionary, "something")
37 | }
38 |
39 | func encodableRepresentation() -> NSCoding {
40 | let dict = NSMutableDictionary()
41 | (dict, "something") <== something
42 | return dict
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/NilTestModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NilTestModel.swift
3 | // Serializable
4 | //
5 | // Created by Chris on 24/09/2016.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Serpent
11 |
12 | public struct NilModel {
13 |
14 | public enum NilModelType: Int {
15 | case first = 0
16 | case second = 1
17 | }
18 |
19 | var id: Int = 0
20 | var name = SimpleModel()
21 | var names = [SimpleModel]()
22 | var url = URL(string: "http://www.google.com")!
23 | var someEnum: NilModelType = .first
24 | var someEnumArray: [NilModelType] = []
25 | var somePrimitiveArray: [String] = []
26 |
27 | var optionalId: Int?
28 | var optionalName: SimpleModel?
29 | var optionalNames: [SimpleModel]?
30 | var optionalUrl: URL?
31 | var optionalEnum: NilModelType?
32 | var optionalEnumArray: [NilModelType]?
33 | var optionalPrimitiveArray: [String]?
34 | }
35 | extension NilModel: Serializable {
36 | public init(dictionary: NSDictionary?) {
37 | id <== (self, dictionary, "id")
38 | name <== (self, dictionary, "name")
39 | names <== (self, dictionary, "names")
40 | url <== (self, dictionary, "url")
41 | someEnum <== (self, dictionary, "some_enum")
42 | someEnumArray <== (self, dictionary, "some_enum_array")
43 | somePrimitiveArray <== (self, dictionary, "some_primitive_array")
44 | optionalId <== (self, dictionary, "optional_id")
45 | optionalName <== (self, dictionary, "optional_name")
46 | optionalNames <== (self, dictionary, "optional_names")
47 | optionalUrl <== (self, dictionary, "optional_url")
48 | optionalEnum <== (self, dictionary, "optional_enum")
49 | optionalEnumArray <== (self, dictionary, "optional_enum_array")
50 | optionalPrimitiveArray <== (self, dictionary, "optional_primitive_array")
51 | }
52 |
53 | public func encodableRepresentation() -> NSCoding {
54 | let dict = NSMutableDictionary()
55 | (dict, "id") <== id
56 | (dict, "name") <== name
57 | (dict, "names") <== names
58 | (dict, "url") <== url
59 | (dict, "some_enum") <== someEnum
60 | (dict, "some_enum_array") <== someEnumArray
61 | (dict, "some_primitive_array") <== somePrimitiveArray
62 | (dict, "optional_id") <== optionalId
63 | (dict, "optional_name") <== optionalName
64 | (dict, "optional_names") <== optionalNames
65 | (dict, "optional_url") <== optionalUrl
66 | (dict, "optional_enum") <== optionalEnum
67 | (dict, "optional_enum_array") <== optionalEnumArray
68 | (dict, "optional_primitive_array") <== optionalPrimitiveArray
69 | return dict
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/PerformanceTestModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PerformanceTestModel.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 25/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 |
10 | import Serpent
11 |
12 |
13 | struct PerformanceTestModel {
14 |
15 | enum EyeColor: String {
16 | case Blue = "blue"
17 | case Green = "green"
18 | case Brown = "brown"
19 | }
20 |
21 | enum Fruit: String {
22 | case Apple = "apple"
23 | case Banana = "banana"
24 | case Strawberry = "strawberry"
25 | }
26 |
27 | var id = ""
28 | var index = 0
29 | var guid = ""
30 | var isActive = true //<- isActive
31 | var balance = ""
32 | var picture: NSURL?
33 | var age = 0
34 | var eyeColor: EyeColor = .Brown //<- eyeColor
35 | var name = Name()
36 | var company = ""
37 | var email = ""
38 | var phone = ""
39 | var address = ""
40 | var about = ""
41 | var registered = ""
42 | var latitude = 0.0
43 | var longitude = 0.0
44 | var greeting = ""
45 | var favoriteFruit: Fruit? //<- favoriteFruit
46 | }
47 |
48 | extension PerformanceTestModel: Serializable {
49 | init(dictionary: NSDictionary?) {
50 | id <== (self, dictionary, "id")
51 | index <== (self, dictionary, "index")
52 | guid <== (self, dictionary, "guid")
53 | isActive <== (self, dictionary, "isActive")
54 | balance <== (self, dictionary, "balance")
55 | picture <== (self, dictionary, "picture")
56 | age <== (self, dictionary, "age")
57 | eyeColor <== (self, dictionary, "eyeColor")
58 | name <== (self, dictionary, "name")
59 | company <== (self, dictionary, "company")
60 | email <== (self, dictionary, "email")
61 | phone <== (self, dictionary, "phone")
62 | address <== (self, dictionary, "address")
63 | about <== (self, dictionary, "about")
64 | registered <== (self, dictionary, "registered")
65 | latitude <== (self, dictionary, "latitude")
66 | longitude <== (self, dictionary, "longitude")
67 | greeting <== (self, dictionary, "greeting")
68 | favoriteFruit <== (self, dictionary, "favoriteFruit")
69 | }
70 |
71 | func encodableRepresentation() -> NSCoding {
72 | let dict = NSMutableDictionary()
73 | (dict, "id") <== id
74 | (dict, "index") <== index
75 | (dict, "guid") <== guid
76 | (dict, "isActive") <== isActive
77 | (dict, "balance") <== balance
78 | (dict, "picture") <== picture
79 | (dict, "age") <== age
80 | (dict, "eyeColor") <== eyeColor
81 | (dict, "name") <== name
82 | (dict, "company") <== company
83 | (dict, "email") <== email
84 | (dict, "phone") <== phone
85 | (dict, "address") <== address
86 | (dict, "about") <== about
87 | (dict, "registered") <== registered
88 | (dict, "latitude") <== latitude
89 | (dict, "longitude") <== longitude
90 | (dict, "greeting") <== greeting
91 | (dict, "favoriteFruit") <== favoriteFruit
92 | return dict
93 | }
94 | }
95 |
96 | struct Name {
97 | var first = ""
98 | var last = ""
99 | }
100 |
101 | extension Name: Serializable {
102 | init(dictionary: NSDictionary?) {
103 | first <== (self, dictionary, "first")
104 | last <== (self, dictionary, "last")
105 | }
106 |
107 | func encodableRepresentation() -> NSCoding {
108 | let dict = NSMutableDictionary()
109 | (dict, "first") <== first
110 | (dict, "last") <== last
111 | return dict
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/PerformanceTestSmallModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PerformanceTestModel.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 25/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 |
10 | import Serpent
11 |
12 |
13 | struct PerformanceTestSmallModel {
14 | var id = ""
15 | var name = ""
16 | }
17 |
18 | extension PerformanceTestSmallModel: Serializable {
19 | init(dictionary: NSDictionary?) {
20 | id <== (self, dictionary, "id")
21 | name <== (self, dictionary, "name")
22 | }
23 |
24 | func encodableRepresentation() -> NSCoding {
25 | let dict = NSMutableDictionary()
26 | (dict, "id") <== id
27 | (dict, "name") <== name
28 | return dict
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/PrimitivesTest.json:
--------------------------------------------------------------------------------
1 | {
2 | "integer" : 1,
3 | "optional_integer" : 2,
4 | "optional_integer_with_default_value" : 3,
5 |
6 | "negative_integer" : -10,
7 | "optional_negative_integer" : -20,
8 | "optional_negative_integer_with_default_value" : -30,
9 |
10 | "double" : 123.1234567,
11 | "optional_double" : 234.2345678,
12 | "optional_double_with_default_value" : 345.3456789,
13 |
14 | "float" : 1.234,
15 | "optional_float" : 2.345,
16 | "optional_float_with_default_value" : 3.456,
17 |
18 | "bool" : "yes",
19 | "optional_bool" : "1",
20 | "optional_bool_with_default_value" : true,
21 |
22 | "char" : "S",
23 | "optional_char" : "O",
24 | "optional_char_with_default_value" : "D",
25 |
26 | "string" : "success",
27 | "optional_string" : "optional success",
28 | "optional_string_with_default_value" : "optional default success",
29 |
30 | "intString" : "1",
31 | "doubleString" : "1.5",
32 | "boolString" : "true",
33 | "boolIntString" : "1",
34 |
35 | "stringInt" : 1,
36 | "stringDouble" : 1.5,
37 | "stringBool" : true
38 | }
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/PrimitivesTestModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PrimitivesTestModel.swift
3 | // NOCore
4 | //
5 | // Created by Dominik Hádl on 17/01/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Serpent
11 |
12 | struct PrimitivesTestModel {
13 | var integer: Int = 0
14 | var optionalInteger: Int?
15 | var optionalIntegerWithDefaultValue: Int? = 0
16 |
17 | var negativeInteger: Int = -1
18 | var optionalNegativeInteger: Int?
19 | var optionalNegativeIntegerWithDefaultValue: Int? = -1
20 |
21 | var double: Double = 0.0
22 | var optionalDouble: Double?
23 | var optionalDoubleWithDefaultValue: Double? = 0.0
24 |
25 | var float: Float = 0.0
26 | var optionalFloat: Float?
27 | var optionalFloatWithDefaultValue: Float? = 0.0
28 |
29 | var bool: Bool = false
30 | var optionalBool: Bool?
31 | var optionalBoolWithDefaultValue: Bool? = false
32 |
33 | var char: Character = "A"
34 | var optionalChar: Character?
35 | var optionalCharWithDefaultValue: Character? = "A"
36 |
37 | var string: String = ""
38 | var optionalString: String?
39 | var optionalStringWithDefaultValue: String? = ""
40 |
41 | var intString: Int = 0
42 | var doubleString: Double = 0.0
43 | var boolString: Bool = false
44 | var boolIntString: Bool = false
45 |
46 | var stringDouble: String = ""
47 | var stringBool: String = ""
48 | }
49 |
50 | extension PrimitivesTestModel:Serializable {
51 | init(dictionary: NSDictionary?) {
52 | integer = self.mapped(dictionary, key: "integer") ?? integer
53 | optionalInteger = self.mapped(dictionary, key: "optional_integer")
54 | optionalIntegerWithDefaultValue = self.mapped(dictionary, key: "optional_integer_with_default_value")
55 | negativeInteger = self.mapped(dictionary, key: "negative_integer") ?? negativeInteger
56 | optionalNegativeInteger = self.mapped(dictionary, key: "optional_negative_integer")
57 | optionalNegativeIntegerWithDefaultValue = self.mapped(dictionary, key: "optional_negative_integer_with_default_value")
58 | double = self.mapped(dictionary, key: "double") ?? double
59 | optionalDouble = self.mapped(dictionary, key: "optional_double")
60 | optionalDoubleWithDefaultValue = self.mapped(dictionary, key: "optional_double_with_default_value")
61 | float = self.mapped(dictionary, key: "float") ?? float
62 | optionalFloat = self.mapped(dictionary, key: "optional_float")
63 | optionalFloatWithDefaultValue = self.mapped(dictionary, key: "optional_float_with_default_value")
64 | bool = self.mapped(dictionary, key: "bool") ?? bool
65 | optionalBool = self.mapped(dictionary, key: "optional_bool")
66 | optionalBoolWithDefaultValue = self.mapped(dictionary, key: "optional_bool_with_default_value")
67 | char = self.mapped(dictionary, key: "char") ?? char
68 | optionalChar = self.mapped(dictionary, key: "optional_char")
69 | optionalCharWithDefaultValue = self.mapped(dictionary, key: "optional_char_with_default_value")
70 | string = self.mapped(dictionary, key: "string") ?? string
71 | optionalString = self.mapped(dictionary, key: "optional_string")
72 | optionalStringWithDefaultValue = self.mapped(dictionary, key: "optional_string_with_default_value")
73 | intString = self.mapped(dictionary, key: "intString") ?? intString
74 | doubleString = self.mapped(dictionary, key: "doubleString") ?? doubleString
75 | boolString = self.mapped(dictionary, key: "boolString") ?? boolString
76 | boolIntString = self.mapped(dictionary, key: "boolIntString") ?? boolIntString
77 | stringDouble = self.mapped(dictionary, key: "stringDouble") ?? stringDouble
78 | stringBool = self.mapped(dictionary, key: "stringBool") ?? stringBool
79 | }
80 |
81 | func encodableRepresentation() -> NSCoding {
82 | let dict = NSMutableDictionary()
83 | dict["integer"] = integer
84 | dict["optional_integer"] = optionalInteger
85 | dict["optional_integer_with_default_value"] = optionalIntegerWithDefaultValue
86 | dict["negative_integer"] = negativeInteger
87 | dict["optional_negative_integer"] = optionalNegativeInteger
88 | dict["optional_negative_integer_with_default_value"] = optionalNegativeIntegerWithDefaultValue
89 | dict["double"] = double
90 | dict["optional_double"] = optionalDouble
91 | dict["optional_double_with_default_value"] = optionalDoubleWithDefaultValue
92 | dict["float"] = float
93 | dict["optional_float"] = optionalFloat
94 | dict["optional_float_with_default_value"] = optionalFloatWithDefaultValue
95 | dict["bool"] = bool
96 | dict["optional_bool"] = optionalBool
97 | dict["optional_bool_with_default_value"] = optionalBoolWithDefaultValue
98 | dict["char"] = "\(char)"
99 | dict["optional_char"] = optionalChar != nil ? "\(optionalChar!)" : "\(String(describing: optionalChar))"
100 | dict["optional_char_with_default_value"] = optionalCharWithDefaultValue != nil ? "\(optionalCharWithDefaultValue!)" : "\(String(describing: optionalCharWithDefaultValue))"
101 | dict["string"] = string
102 | dict["optional_string"] = optionalString
103 | dict["optional_string_with_default_value"] = optionalStringWithDefaultValue
104 | dict["intString"] = intString
105 | dict["doubleString"] = doubleString
106 | dict["boolString"] = boolString
107 | dict["boolIntString"] = boolIntString
108 | dict["stringBool"] = stringBool
109 | return dict
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/SerializableEntityTest.json:
--------------------------------------------------------------------------------
1 | {
2 | "id" : 1,
3 | "simple" :
4 | [{
5 | "id" : 0
6 | }],
7 | "simples" :
8 | [{
9 | "id" : 0
10 | },
11 | {
12 | "id" : 1
13 | }],
14 | "strings" :
15 | [
16 | "one",
17 | "two",
18 | "three"
19 | ],
20 | "ints" :
21 | [
22 | "one",
23 | "two",
24 | "three"
25 | ],
26 | "stringInts" :
27 | [
28 | "one",
29 | "two",
30 | "three"
31 | ],
32 | "stringDicts" :
33 | [
34 | {
35 | "one" : "asdf"
36 | },
37 | {
38 | "two" : false
39 | },
40 | ]
41 | }
42 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/SerializableEntityTestModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SerializableEntityTestModel.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 16/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Serpent
11 |
12 | struct SerializableEntityTestModel {
13 | var id = 0
14 | var simple: SimpleModel?
15 | var simples: [SimpleModel]?
16 | var strings: [String] = []
17 | var ints: [Int] = []
18 | var stringInts: [SimpleModel] = []
19 | var stringDicts: [SimpleModel] = []
20 | }
21 |
22 | extension SerializableEntityTestModel:Serializable {
23 | init(dictionary: NSDictionary?) {
24 | id <== (self, dictionary, "id")
25 | simple <== (self, dictionary, "simple")
26 | simples <== (self, dictionary, "simples")
27 | strings <== (self, dictionary, "strings")
28 | ints <== (self, dictionary, "ints")
29 | stringInts <== (self, dictionary, "stringInts")
30 | stringDicts <== (self, dictionary, "stringDicts")
31 | }
32 |
33 | func encodableRepresentation() -> NSCoding {
34 | let dict = NSMutableDictionary()
35 | (dict, "id") <== id
36 | (dict, "simple") <== simple
37 | (dict, "simples") <== simples
38 | (dict, "strings") <== strings
39 | (dict, "ints") <== ints
40 | (dict, "stringInts") <== stringInts
41 | (dict, "stringDicts") <== stringDicts
42 | return dict
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/SimpleModel.json:
--------------------------------------------------------------------------------
1 | {
2 | "id" : 1
3 | }
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/SimpleModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleModel.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 16/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Serpent
11 |
12 | struct SimpleModel {
13 | var id = 0
14 | }
15 |
16 | extension SimpleModel:Serializable {
17 | init(dictionary: NSDictionary?) {
18 | id <== (self, dictionary, key: "id")
19 | }
20 |
21 | func encodableRepresentation() -> NSCoding {
22 | let dict = NSMutableDictionary()
23 | (dict, "id") <== id
24 | return dict
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/StringInitializableTest.json:
--------------------------------------------------------------------------------
1 | {
2 | "some_url" : "http://www.google.com",
3 | "some_date" : "1974-08-15T15:17:14+00:00",
4 | "some_empty_url" : "",
5 | "some_empty_date" : "",
6 | "some_bad_date" :"WOW_SUCH_DATE_NOT_CORRECT",
7 | "some_custom_date" : "2016-11-15 17:19:04"
8 | }
9 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Models/StringInitializableTestModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringInitializableTestModel.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 17/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Serpent
11 |
12 | struct StringInitializableTestModel {
13 | var someUrl: URL?
14 | var someDate: Date?
15 | var someEmptyDate: Date?
16 | var someEmptyURL: URL? //<- some_empty_url
17 | var someBadDate: Date?
18 | var someCustomDate: Date?
19 | }
20 |
21 | extension StringInitializableTestModel: Serializable {
22 | init(dictionary: NSDictionary?) {
23 | someUrl <== (self, dictionary, "some_url")
24 | someDate <== (self, dictionary, "some_date")
25 | someEmptyDate <== (self, dictionary, "some_empty_date")
26 | someEmptyURL <== (self, dictionary, "some_empty_url")
27 | someBadDate <== (self, dictionary, "some_bad_date")
28 | someCustomDate <== (self, dictionary, "some_custom_date")
29 | }
30 |
31 | func encodableRepresentation() -> NSCoding {
32 | let dict = NSMutableDictionary()
33 | (dict, "some_url") <== someUrl
34 | (dict, "some_date") <== someDate
35 | (dict, "some_empty_date") <== someEmptyDate
36 | (dict, "some_empty_url") <== someEmptyURL
37 | (dict, "some_bad_date") <== someBadDate
38 | (dict, "some_custom_date") <== someCustomDate
39 | return dict
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/TestEndpoint/ArrayTest.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id": 1,
3 | "name": "Hello",
4 | "text": "some text"
5 | },
6 | {
7 | "id": 2,
8 | "name": "Hello2",
9 | "text": "some text2"
10 | },
11 | {
12 | "id": 3,
13 | "name": "Hello3",
14 | "text": "some text3"
15 | },
16 | {
17 | "id": 4,
18 | "name": "Hello4",
19 | "text": "some text4"
20 | }
21 | ]
22 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/TestEndpoint/ArrayTestOneObject.json:
--------------------------------------------------------------------------------
1 | [{
2 | "id": 1,
3 | "name": "Hello",
4 | "text": "some text"
5 | }
6 | ]
7 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/TestEndpoint/BadArrayTest.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/SerpentTests/TestEndpoint/BadArrayTest.json
--------------------------------------------------------------------------------
/Serpent/SerpentTests/TestEndpoint/Empty.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent/SerpentTests/TestEndpoint/Empty.json
--------------------------------------------------------------------------------
/Serpent/SerpentTests/TestEndpoint/NestedArrayTest.json:
--------------------------------------------------------------------------------
1 | {
2 | "data": [
3 | {
4 | "id": 1,
5 | "name": "Hello",
6 | "text": "some text"
7 | },
8 | {
9 | "id": 2,
10 | "name": "Hello2",
11 | "text": "some text2"
12 | },
13 | {
14 | "id": 3,
15 | "name": "Hello3",
16 | "text": "some text3"
17 | },
18 | {
19 | "id": 4,
20 | "name": "Hello4",
21 | "text": "some text4"
22 | }
23 | ]
24 | }
--------------------------------------------------------------------------------
/Serpent/SerpentTests/TestEndpoint/NetworkModel.json:
--------------------------------------------------------------------------------
1 | {
2 | "origin": "185.118.250.50",
3 | "url": "http://httpbin.org/get"
4 | }
5 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/TestEndpoint/NetworkModelBad.json:
--------------------------------------------------------------------------------
1 | {
2 | "origin": "185.118.250.50",
3 | "url": "http://httpbin.org/get"
4 | Hello!
5 | }
6 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/AlamofireExtensionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AlamofireExtensionTests.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 18/03/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import Serpent
11 | import Alamofire
12 |
13 | class AlamofireExtensionTests: XCTestCase {
14 |
15 | let timeoutDuration = 2.0
16 |
17 | let manager = SessionManager()
18 |
19 | override func setUp() {
20 | super.setUp()
21 | // Put setup code here. This method is called before the invocation of each test method in the class.
22 | }
23 |
24 | func urlForResource(resource: String) -> URL? {
25 | if let path = Bundle(for: type(of: self)).path(forResource: resource, ofType: "json") {
26 | return URL(fileURLWithPath: path)
27 | }
28 | return nil
29 | }
30 |
31 | func testAlamofireExtension() {
32 | let expectation = self.expectation(description: "Expected network request success")
33 | let handler:(Alamofire.DataResponse) -> Void = { result in
34 | switch result.result {
35 | case .success:
36 | expectation.fulfill()
37 | default:
38 | print(result)
39 | break // Fail
40 |
41 | }
42 | }
43 | if let url = urlForResource(resource: "NetworkModel") {
44 | manager.request(url, method: .get).responseSerializable(handler)
45 | }
46 | waitForExpectations(timeout: timeoutDuration, handler: nil)
47 | }
48 |
49 | func testAlamofireExtensionBadJSON() {
50 | let expectation = self.expectation(description: "Expected bad data from response")
51 | let handler:(Alamofire.DataResponse) -> Void = { result in
52 | switch result.result {
53 | case .failure:
54 | expectation.fulfill()
55 | default:
56 | break
57 | }
58 | }
59 | if let url = urlForResource(resource: "NetworkModelBad") {
60 | manager.request(url, method: .get).responseSerializable(handler)
61 | }
62 | waitForExpectations(timeout: timeoutDuration, handler: nil)
63 | }
64 |
65 |
66 | func testAlamofireExtensionBadJSONObject() {
67 | let expectation = self.expectation(description: "Expected bad object from response")
68 | let handler:(Alamofire.DataResponse<[NetworkTestModel]>) -> Void = { result in
69 | switch result.result {
70 | case .failure:
71 | expectation.fulfill()
72 | default:
73 | break
74 | }
75 | }
76 | if let url = urlForResource(resource: "NetworkModel") {
77 | manager.request(url, method: .get).responseSerializable(handler)
78 | }
79 | waitForExpectations(timeout: timeoutDuration, handler: nil)
80 | }
81 |
82 | func testAlamofireExtensionUnexpectedArrayJSON() {
83 | let expectation = self.expectation(description: "Expected array data to single object from response")
84 | let handler:(Alamofire.DataResponse) -> Void = { result in
85 | switch result.result {
86 | case .failure:
87 | expectation.fulfill()
88 | default:
89 | break
90 | }
91 | }
92 | if let url = urlForResource(resource: "ArrayTest") {
93 | manager.request(url, method: .get).responseSerializable(handler)
94 | }
95 | waitForExpectations(timeout: timeoutDuration, handler: nil)
96 | }
97 |
98 | func testAlamofireExtensionEmptyJSON() {
99 | let expectation = self.expectation(description: "Expected empty response")
100 | let handler:(Alamofire.DataResponse) -> Void = { result in
101 | switch result.result {
102 | case .failure:
103 | expectation.fulfill()
104 | default:
105 | break
106 | }
107 | }
108 | if let url = urlForResource(resource: "Empty") {
109 | manager.request(url, method: .get).responseSerializable(handler)
110 | }
111 | waitForExpectations(timeout: timeoutDuration, handler: nil)
112 | }
113 | func testAlamofireArrayUnwrapper() {
114 | let expectation = self.expectation(description: "Expected unwrapped array response")
115 | let handler:(Alamofire.DataResponse<[DecodableModel]>) -> Void = { result in
116 | switch result.result {
117 | case .success:
118 | expectation.fulfill()
119 | default:
120 | break
121 | }
122 | }
123 | let unwrapper: Parser.Unwrapper = { dict, _ in dict["data"] }
124 |
125 | if let url = urlForResource(resource: "NestedArrayTest") {
126 | manager.request(url, method: .get).responseSerializable(handler, unwrapper: unwrapper)
127 | }
128 |
129 | waitForExpectations(timeout: timeoutDuration, handler: nil)
130 | }
131 |
132 | func testAlamofireArrayNotUnwrapped() {
133 | let expectation = self.expectation(description: "Expected unwrapped array response")
134 | let handler:(Alamofire.DataResponse<[DecodableModel]>) -> Void = { result in
135 | switch result.result {
136 | case .failure:
137 | expectation.fulfill()
138 | default:
139 | break
140 | }
141 | }
142 |
143 | if let url = urlForResource(resource: "NestedArrayTest") {
144 | manager.request(url, method: .get).responseSerializable(handler)
145 | }
146 | waitForExpectations(timeout: timeoutDuration, handler: nil)
147 | }
148 |
149 | func testAlamofireWrongTypeUnwrapper() {
150 | let expectation = self.expectation(description: "Expected unwrapped array response")
151 | let handler:(Alamofire.DataResponse) -> Void = { result in
152 | switch result.result {
153 | case .success:
154 | expectation.fulfill()
155 | default:
156 | break
157 | }
158 | }
159 | let unwrapper: Parser.Unwrapper = { dict, _ in ["data"] }
160 |
161 | if let url = urlForResource(resource: "NestedArrayTest") {
162 | manager.request(url, method: .get).responseSerializable(handler, unwrapper: unwrapper)
163 | }
164 | waitForExpectations(timeout: timeoutDuration, handler: nil)
165 | }
166 |
167 | func testAlamofireExtensionNonSerializable() {
168 | let expectation = self.expectation(description: "Expected empty response")
169 | let handler:(Alamofire.DataResponse) -> Void = { result in
170 | switch result.result {
171 | case .failure:
172 | expectation.fulfill()
173 | default:
174 | break
175 | }
176 | }
177 | if let url = urlForResource(resource: "Empty") {
178 | manager.request(url, method: .get).responseSerializable(handler)
179 | }
180 | waitForExpectations(timeout: timeoutDuration, handler: nil)
181 | }
182 |
183 | func testAlamofireExtensionArrayRootObjectParsingToOne() {
184 | let expectation = self.expectation(description: "Expected valid object")
185 | let handler:(Alamofire.DataResponse) -> Void = { result in
186 | switch result.result {
187 | case .success(let value):
188 | XCTAssertEqual(value.id, 1)
189 | XCTAssertEqual(value.name, "Hello")
190 | expectation.fulfill()
191 | default:
192 | break
193 | }
194 | }
195 | if let url = urlForResource(resource: "ArrayTestOneObject") {
196 | manager.request(url, method: .get).responseSerializable(handler)
197 | }
198 | waitForExpectations(timeout: timeoutDuration, handler: nil)
199 | }
200 |
201 | func testAlamofireExtensionArrayRootObjectParsingToOneWithWrongUnwrapper() {
202 | let expectation = self.expectation(description: "Expected valid object")
203 | let handler:(Alamofire.DataResponse) -> Void = { result in
204 | switch result.result {
205 | case .success(let value):
206 | XCTAssertEqual(value.id, 1)
207 | XCTAssertEqual(value.name, "Hello")
208 | expectation.fulfill()
209 | default:
210 | break
211 | }
212 | }
213 |
214 | let unwrapper: Parser.Unwrapper = { dict, _ in ["nonexistent"] }
215 |
216 | if let url = urlForResource(resource: "ArrayTestOneObject") {
217 | manager.request(url, method: .get).responseSerializable(handler,
218 | unwrapper: unwrapper)
219 | }
220 | waitForExpectations(timeout: timeoutDuration, handler: nil)
221 | }
222 |
223 | func testAlamofireExtensionArrayRootObjectParsingToMultiple() {
224 | let expectation = self.expectation(description: "Expected valid array")
225 | let handler:(Alamofire.DataResponse<[DecodableModel]>) -> Void = { result in
226 | switch result.result {
227 | case .success(let values):
228 | XCTAssertEqual(values.count, 4)
229 | XCTAssertEqual(values[0].id, 1)
230 | XCTAssertEqual(values[0].name, "Hello")
231 | XCTAssertEqual(values[3].id, 4)
232 | XCTAssertEqual(values[3].name, "Hello4")
233 | expectation.fulfill()
234 | default:
235 | break
236 | }
237 | }
238 | if let url = urlForResource(resource: "ArrayTest") {
239 | manager.request(url, method: .get).responseSerializable(handler)
240 | }
241 | waitForExpectations(timeout: timeoutDuration, handler: nil)
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/BridgingBoxTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BridgingBoxTests.swift
3 | // Serializable
4 | //
5 | // Created by Chris on 14/05/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import Serpent
11 |
12 | class BridgingBoxTests: XCTestCase {
13 |
14 | var simpleModel: SimpleModel!
15 |
16 | override func setUp() {
17 | super.setUp()
18 | // Put setup code here. This method is called before the invocation of each test method in the class.
19 |
20 | do {
21 | if let path = Bundle(for: type(of: self)).path(forResource: "SimpleModel", ofType: "json"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
22 | let bridgedDictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary
23 | simpleModel = SimpleModel(dictionary: bridgedDictionary)
24 | }
25 | } catch {
26 | XCTAssert(false, "Failed to prepare bridged dictionary.")
27 | return
28 | }
29 | }
30 |
31 | func testBridgingBoxValue() {
32 | let box = BridgingBox(simpleModel)
33 | let value: SimpleModel? = box.value()
34 | XCTAssertNotNil(value, "Failed to load value from bridging box")
35 | XCTAssertEqual(value!.id, simpleModel.id, "Failed to load value from bridging box")
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/CashierExtensionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CashierExtensionTests.swift
3 | // Serializable
4 | //
5 | // Created by Chris on 14/05/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import Cashier
11 | import Serpent
12 |
13 | class CashierExtensionTests: XCTestCase {
14 |
15 | var simpleModel: SimpleModel!
16 |
17 | override func setUp() {
18 | super.setUp()
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 |
21 | do {
22 | if let path = Bundle(for: type(of: self)).path(forResource: "SimpleModel", ofType: "json"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
23 | let bridgedDictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary
24 | simpleModel = SimpleModel(dictionary: bridgedDictionary)
25 | }
26 | } catch {
27 | XCTAssert(false, "Failed to prepare bridged dictionary.")
28 | return
29 | }
30 | }
31 |
32 | func testSerializableCache() {
33 |
34 | Cashier.defaultCache().setSerializable(simpleModel, forKey: "SimpleModel")
35 | let loadedModel: SimpleModel? = Cashier.defaultCache().serializableForKey("SimpleModel")
36 |
37 | XCTAssertNotNil(loadedModel, "Failed to load model from cache")
38 | XCTAssertEqual(simpleModel.id, loadedModel!.id, "Failed to load correct model from cache")
39 | }
40 |
41 | func testSerializableNilCache() {
42 |
43 | let loadedModel: SimpleModel? = Cashier.defaultCache().serializableForKey("NilSimpleModel")
44 | XCTAssertNil(loadedModel, "Failed to handle nil from cache")
45 |
46 | }
47 |
48 | func testSerializableNilArrayCache() {
49 |
50 | let loadedModel: [SimpleModel]? = Cashier.defaultCache().serializableForKey("NilSimpleModel")
51 | XCTAssertNil(loadedModel, "Failed to handle nil from cache")
52 |
53 | }
54 |
55 | func testSerializableArrayCache() {
56 |
57 | Cashier.defaultCache().setSerializable([simpleModel], forKey: "SimpleModel")
58 | let loadedModel: [SimpleModel]? = Cashier.defaultCache().serializableForKey("SimpleModel")
59 |
60 | XCTAssertNotNil(loadedModel, "Failed to load array model from cache")
61 | XCTAssertEqual(simpleModel.id, loadedModel![0].id, "Failed to load correct array model from cache")
62 | }
63 |
64 | func testClearSerializableCache() {
65 | Cashier.defaultCache().setSerializable(simpleModel, forKey: "SimpleModel")
66 | Cashier.defaultCache().clearAllData(true)
67 |
68 | let loadedModel: SimpleModel? = Cashier.defaultCache().serializableForKey("SimpleModel")
69 | XCTAssertNil(loadedModel, "Failed to clear serializables from cache")
70 | let loadedArrayModel: [SimpleModel]? = Cashier.defaultCache().serializableForKey("SimpleModel")
71 | XCTAssertNil(loadedArrayModel, "Failed to clear serializables from cache")
72 | }
73 |
74 | func testDeleteSerializableCache() {
75 | Cashier.defaultCache().setSerializable(simpleModel, forKey: "SimpleModelDelete")
76 | Cashier.defaultCache().deleteSerializableForKey("SimpleModelDelete")
77 |
78 | let loadedModel: SimpleModel? = Cashier.defaultCache().serializableForKey("SimpleModelDelete")
79 | XCTAssertNil(loadedModel, "Failed to delete model from cache")
80 | }
81 |
82 | #if TEST_MEMORY_WARNING
83 | func testLoadIgnoringMemCaches() {
84 | let box = BridgingBox(simpleModel)
85 | Cashier.defaultCache().setObject(box, forKey: "ManualBox")
86 | NotificationCenter.default.post(name: UIApplication.didReceiveMemoryWarningNotification, object: nil) // clear the Cashier mem cache
87 | let loadedModel: SimpleModel? = Cashier.defaultCache().serializableForKey("ManualBox")
88 |
89 | XCTAssertNotNil(loadedModel, "Failed to load model direct from cache")
90 | XCTAssertEqual(simpleModel.id, loadedModel!.id, "Failed to load correct model direct from cache")
91 |
92 | }
93 | #endif
94 |
95 | func testArrayLoadIgnoringMemCaches() {
96 | let box = BridgingBox(simpleModel)
97 | Cashier.defaultCache().setObject([box], forKey: "ManualBox")
98 |
99 | let loadedModel: [SimpleModel]? = Cashier.defaultCache().serializableForKey("ManualBox")
100 |
101 | XCTAssertNotNil(loadedModel, "Failed to load array model direct from cache")
102 | XCTAssertEqual(simpleModel.id, loadedModel![0].id, "Failed to load correct array model direct from cache")
103 |
104 | }
105 |
106 | func testInvalidObjectInBridgingBox() {
107 | Cashier.defaultCache().setSerializable(simpleModel, forKey: "SimpleModel")
108 | Cashier.defaultCache().deleteObject(forKey: "SimpleModel")
109 | let loadedModel: SimpleModel? = Cashier.defaultCache().serializableForKey("SimpleModel")
110 |
111 | XCTAssertNil(loadedModel, "Failed to return nil model from cache")
112 | }
113 |
114 | func testInvalidArrayObjectInBridgingBox() {
115 | Cashier.defaultCache().setSerializable([simpleModel], forKey: "SimpleModel")
116 | Cashier.defaultCache().deleteObject(forKey: "SimpleModel")
117 | let loadedModel: [SimpleModel]? = Cashier.defaultCache().serializableForKey("SimpleModel")
118 |
119 | XCTAssertNil(loadedModel, "Failed to return nil model from cache")
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/CustomOperatorsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomOperatorsTests.swift
3 | // NOCore
4 | //
5 | // Created by Dominik Hádl on 04/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class CustomOperatorsTests: XCTestCase {
12 |
13 | var bridgedDictionary: NSDictionary!
14 | var testModel: CustomOperatorsTestModel!
15 |
16 | override func setUp() {
17 | super.setUp()
18 | // Put setup code here. This method is called before the invocation of each test method in the class.
19 |
20 | do {
21 | if let path = Bundle(for: type(of: self)).path(forResource: "CustomOperatorsTest", ofType: "json"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
22 | bridgedDictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary
23 | testModel = CustomOperatorsTestModel(dictionary: bridgedDictionary)
24 | }
25 | } catch {
26 | XCTAssert(false, "Failed to prepare bridged dictionary.")
27 | return
28 | }
29 | }
30 |
31 | func testCustomOperatorsDecode() {
32 | XCTAssertEqual(testModel.string, "success", "String parsing with custom operator failed.")
33 | XCTAssertEqual(testModel.secondString, "haha", "String parsing with custom operator failed.")
34 | XCTAssertNil(testModel.nilString, "Nil string parsing with custom operator failed.")
35 | XCTAssertNotNil(testModel.someDictionary, "Dictionary parsing with custom operator failed.")
36 | }
37 |
38 | func testCustomOperatorsEncode() {
39 | let rep = testModel.encodableRepresentation() as! NSDictionary
40 | let subArray = testModel.someArray
41 |
42 | XCTAssertEqual(rep.value(forKey: "string") as? String, "success", "String encode with custom operator failed.")
43 | XCTAssertEqual(rep.value(forKey: "second_string") as? String, "haha", "String encode with custom operator failed.")
44 | XCTAssertNil(rep.value(forKey: "nil_string"), "Nil string encode with custom operator failed.")
45 |
46 | if let type = rep.value(forKey: "some_enum") as? Int {
47 | XCTAssertEqual(Type(rawValue: type), .second, "Enum encode with custom operator failed.")
48 | }
49 | else {
50 | XCTFail("Enum encode with custom operator failed.")
51 | }
52 | XCTAssertEqual(CustomOperatorsTestNestedModel.array(rep.value(forKey: "some_array") as AnyObject?), subArray, "Array encode with custom operator failed.")
53 | }
54 |
55 | func testCustomOperatorNestedSerializable() {
56 |
57 | XCTAssertNotNil(testModel.otherSerializable, "Parsing a nested optional serializable failed.")
58 |
59 | XCTAssertEqual(testModel.otherSerializable?.optionalInteger, 2, "Optional integer parsing with custom operator in nested optional serializable failed.")
60 | XCTAssertEqual(testModel.otherSerializable?.optionalStringWithDefaultValue, "optional default success", "String (optional, w/ default val) parsing in nested optional serializable with custom operator failed.")
61 | XCTAssertNil(testModel.otherSerializable?.optionalDouble, "Optional double in nested optional serializable expected to be nil.")
62 |
63 | XCTAssertNotNil(testModel.someSerializable, "Parsing a nested serializable failed.")
64 |
65 | XCTAssertEqual(testModel.someSerializable.optionalInteger, 2, "Optional integer parsing with custom operator in nested serializable failed.")
66 | XCTAssertEqual(testModel.someSerializable.optionalStringWithDefaultValue, "optional default success", "String (optional, w/ default val) parsing in nested serializable with custom operator failed.")
67 | XCTAssertNil(testModel.someSerializable.optionalDouble, "Optional double in nested serializable expected to be nil.")
68 | }
69 |
70 | func testCustomOperatorStringInitializable() {
71 | XCTAssertEqual(testModel.someUrl, URL(string: "http://www.google.com"), "StringInitializable parsing failed in custom operator")
72 | }
73 |
74 | func testCustomOperatorDictionaryParsing() {
75 | XCTAssertEqual(testModel.someDictionary?["test"] as? String, "value", "Dictionary doesn't contain string value for key.")
76 | XCTAssertEqual(testModel.someDictionary?["integer"] as? Int, 1, "Dictionary doesn't contain integer value for key.")
77 | XCTAssertEqual(testModel.someDictionary?["float"] as? Double, 1.023, "Dictionary doesn't contain float value for key.")
78 | }
79 |
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/DecodableTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DecodableTests.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 16/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class DecodableTests: XCTestCase {
12 |
13 | var bridgedDictionary: NSDictionary!
14 | var testModels: [DecodableModel]!
15 | var testNonArrayModels: [DecodableModel]!
16 |
17 | override func setUp() {
18 | super.setUp()
19 | do {
20 | if let path = Bundle(for: type(of: self)).path(forResource: "DecodableTest", ofType: "json"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
21 | bridgedDictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary
22 | testModels = DecodableModel.array(bridgedDictionary.object(forKey:"models"))
23 | testNonArrayModels = DecodableModel.array(bridgedDictionary.object(forKey:"model"))
24 | }
25 | } catch {
26 | XCTFail("Failed to prepare bridged dictionary.")
27 | return
28 | }
29 | }
30 |
31 | override func tearDown() {
32 | // Put teardown code here. This method is called after the invocation of each test method in the class.
33 | super.tearDown()
34 | }
35 |
36 | func testArray() {
37 | if testModels.count == 0 {
38 | XCTFail("Failed to parse array model.")
39 | return
40 | }
41 | XCTAssertEqual(testModels.count, 4, "Did not create correct number of models in array")
42 | XCTAssertEqual(testModels[0].id, 1, "First entity model parsing failed")
43 | XCTAssertEqual(testModels[1].id, 2, "Second entity model parsing failed")
44 | }
45 |
46 | func testNonArray() {
47 | XCTAssertNotNil(testNonArrayModels, "Create array from non-array JSON failed")
48 | XCTAssertEqual(testNonArrayModels.count, 0, "Create *empty* array from non-array JSON failed")
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/HexInitalizableTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HexInitalizableTests.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 10/03/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import Serpent
11 | #if os(OSX)
12 | import Cocoa
13 | typealias HexColor = NSColor
14 | #else
15 | import UIKit
16 | typealias HexColor = UIColor
17 | #endif
18 |
19 | class HexInitalizableTests: XCTestCase {
20 |
21 | var testModel: HexInitializableTestModel!
22 |
23 | override func setUp() {
24 | super.setUp()
25 | do {
26 | if let path = Bundle(for: type(of: self)).path(forResource: "HexInitializableTest", ofType: "json"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
27 | let bridgedDictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary
28 | testModel = HexInitializableTestModel(dictionary: bridgedDictionary)
29 | }
30 | } catch {
31 | XCTFail("Failed to prepare bridged dictionary.")
32 | return
33 | }
34 | }
35 |
36 | func testHexInitializableUIColor() {
37 | let color = HexColor(red: CGFloat(85) / 255.0, green: CGFloat(170) / 255.0, blue: CGFloat(204) / 255.0, alpha: 1.0)
38 | let colorAlpha = HexColor(red: CGFloat(85) / 255.0, green: CGFloat(170) / 255.0, blue: CGFloat(204) / 255.0, alpha: 1.0).withAlphaComponent(0.8)
39 | XCTAssertEqual(testModel.shortColor, color, "Error parsing short form color in HexInitializable")
40 | XCTAssertEqual(testModel.fullColor, color, "Error parsing long form color in HexInitializable")
41 | XCTAssertEqual(testModel.threeColor, color, "Error parsing three hex form color in HexInitializable")
42 | XCTAssertEqual(testModel.alphaRGB, colorAlpha, "Error parsing three hex form color in HexInitializable")
43 | XCTAssertNil(testModel.badColor, "Error returning nil for malformed color hex")
44 | XCTAssertNil(testModel.notColor, "Error returning nil for non hex value")
45 | XCTAssertNil(testModel.invalidHexColor, "Error returning nil for invalid hex value")
46 | }
47 |
48 | func testHexInitializableNilDictionary() {
49 | let nilModel = HexInitializableTestNilModel(dictionary: nil)
50 | XCTAssertEqual(nilModel.someColor, HexColor.red, "Failed to parse nil dictionary into HexInitializable model")
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/SerializableEntityTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SerializableEntityTests.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 16/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import XCTest
11 |
12 | class SerializableEntityTests: XCTestCase {
13 |
14 | var bridgedDictionary: NSDictionary!
15 | var testModel: SerializableEntityTestModel!
16 |
17 | override func setUp() {
18 | super.setUp()
19 | do {
20 | if let path = Bundle(for: type(of: self)).path(forResource: "SerializableEntityTest", ofType: "json"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
21 | bridgedDictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary
22 | testModel = SerializableEntityTestModel(dictionary: bridgedDictionary)
23 | }
24 | } catch {
25 | XCTFail("Failed to prepare bridged dictionary.")
26 | return
27 | }
28 |
29 | }
30 |
31 | override func tearDown() {
32 | super.tearDown()
33 | }
34 |
35 | func testSerializableParsing() {
36 | XCTAssertNil(testModel.simple, "Failed to return nil when JSON contains array while struct expects Serializable")
37 | XCTAssertNotNil(testModel.simples, "Failed to parse array of Serializables")
38 | XCTAssertNotEqual(testModel.strings.count, 0, "Failed to parse array of primitives")
39 | XCTAssertEqual(testModel.ints.count, 0, "Failed to parse array of primitives")
40 | XCTAssertEqual(testModel.stringInts.count, 0, "Failed to create empty array from non dictionary serializable array type")
41 | XCTAssertEqual(testModel.stringDicts.count, 2, "Failed to create array of empty Serializables from wrong serializable array type")
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/SerializableEnumsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SerializablePrimitivesTests.swift
3 | // NOCore
4 | //
5 | // Created by Dominik Hádl on 17/01/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import XCTest
11 |
12 | // TODO: Add tests for explicit Int8, Int16, Int32, Int64 and all UInt variants, Float80 and UnicodeScalar
13 |
14 | class SerializableEnumsTests: XCTestCase {
15 |
16 | var bridgedDictionary: NSDictionary!
17 | var testModel: EnumsTestModel!
18 |
19 | override func setUp() {
20 | super.setUp()
21 | // Put setup code here. This method is called before the invocation of each test method in the class.
22 |
23 | do {
24 | if let path = Bundle(for: type(of: self)).path(forResource: "EnumsTest", ofType: "json"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
25 | bridgedDictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary
26 | testModel = EnumsTestModel(dictionary: bridgedDictionary)
27 | }
28 | } catch {
29 | XCTAssert(false, "Failed to prepare bridged dictionary.")
30 | return
31 | }
32 | }
33 |
34 | override func tearDown() {
35 | super.tearDown()
36 | }
37 |
38 | func testStringEnumParsing() {
39 | XCTAssertEqual(testModel.stringEnum, StringEnum.Value1, "String parsing test failed!")
40 | XCTAssertEqual(testModel.optionalStringEnum, StringEnum.DifferentValue, "Optional String parsing test failed!")
41 | XCTAssertEqual(testModel.optionalStringEnumWithDefaultValue, StringEnum.Value1, "Optional String with default value parsing test failed!")
42 | XCTAssertEqual(testModel.nonExistentStringEnum, nil, "Optional String with default value parsing test failed!")
43 | }
44 |
45 | func testStringEnumArrayParsing() {
46 | XCTAssertEqual(testModel.stringEnumArray, [StringEnum.Value1, StringEnum.DifferentValue], "String enum array test failed!")
47 | XCTAssertEqual(testModel.optionalStringEnumArray!, [StringEnum.Value1, StringEnum.DifferentValue], "Optional string enum array test failed!")
48 | XCTAssertEqual(testModel.optionalStringEnumArrayWithDefaultValue!, [StringEnum.Value1, StringEnum.DifferentValue], "Optional string enum array with default value test failed!")
49 | XCTAssertNil(testModel.nonExistentStringEnumArray, "Optional string enum array with non existent value test failed!")
50 | XCTAssertNil(testModel.wrongTypeStringEnumArray, "Wrong type string enum array test failed!")
51 | }
52 |
53 | func testDoubleEnumArrayParsing() {
54 | XCTAssertEqual(testModel.doubleEnumArray, [DoubleEnum.value1, DoubleEnum.differentValue], "Double enum array test failed!")
55 | XCTAssertEqual(testModel.optionalDoubleEnumArray!, [DoubleEnum.value1, DoubleEnum.differentValue], "Optional double enum array test failed!")
56 | XCTAssertEqual(testModel.optionalDoubleEnumArrayWithDefaultValue!, [DoubleEnum.value1, DoubleEnum.differentValue], "Optional double enum array with default value test failed!")
57 | XCTAssertNil(testModel.nonExistentDoubleEnumArray, "Optional double enum array with non existent value test failed!")
58 | XCTAssertNil(testModel.wrongTypeDoubleEnumArray,"Wrong type double enum array test failed!")
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/SerializableNilEntitiesTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SerializableNilEntitiesTests.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 16/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import XCTest
11 |
12 |
13 | class SerializableNilEntitiesTests: XCTestCase {
14 | var testModel: NilModel!
15 |
16 | override func setUp() {
17 | super.setUp()
18 | testModel = NilModel(dictionary: nil)
19 | }
20 |
21 | func testNilPrimitives() {
22 | XCTAssertEqual(testModel.id, 0, "Guard statement failed for primitive with nil Dictionary")
23 | XCTAssertNil(testModel.optionalId, "Guard statement failed for optional primitive with nil Dictionary")
24 | XCTAssertNotNil(testModel.somePrimitiveArray, "Guard statement failed for Primitive array with nil dictionary")
25 | XCTAssertEqual(testModel.somePrimitiveArray.count, 0, "Guard statement failed for Primitive array with nil dictionary")
26 | XCTAssertNil(testModel.optionalPrimitiveArray, "Guard statement failed for optional Primitive array with nil dictionary")
27 | }
28 |
29 | func testNilSerializables() {
30 | XCTAssertEqual(testModel.name.id, SimpleModel().id, "Guard statement failed for Serializable with nil Dictionary")
31 | XCTAssertNotNil(testModel.names, "Guard statement failed for Serializable array with nil Dictionary")
32 | XCTAssertEqual(testModel.names.count, 0, "Guard statement failed for Serializable array with nil Dictionary")
33 | XCTAssertNil(testModel.optionalName, "Guard statement failed for optional Serializable with nil Dictionary")
34 | XCTAssertNil(testModel.optionalNames, "Guard statement failed for optional Serializable array with nil Dictionary")
35 |
36 | }
37 |
38 | func testNilEnums() {
39 | XCTAssertEqual(testModel.someEnum.rawValue, 0, "Guard statement failed for Enum with nil dictionary")
40 | XCTAssertNotNil(testModel.someEnumArray, "Guard statement failed for Enum array with nil dictionary")
41 | XCTAssertEqual(testModel.someEnumArray.count, 0, "Guard statement failed for Enum array with nil dictionary")
42 | XCTAssertNil(testModel.optionalEnum, "Guard statement failed for optional Enum with nil dictionary")
43 | XCTAssertNil(testModel.optionalEnumArray, "Guard statement failed for optional Enum array with nil dictionary")
44 | }
45 |
46 | func testNilStringInitializables() {
47 | XCTAssertEqual(testModel.url, URL(string: "http://www.google.com"), "Guard statement failed for StringInitializable with nil dictionary")
48 | XCTAssertNil(testModel.optionalUrl, "Guard statement failed for optional StringInitializable with nil dictionary")
49 | }
50 |
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/SerializablePerformanceTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SerializablePerformanceTest.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 25/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class SerializablePerformanceTest: XCTestCase {
12 |
13 | var largeData: NSData!
14 | var smallData: NSData!
15 |
16 | var jsonDict: NSDictionary!
17 | var smallJsonDict: NSDictionary!
18 |
19 |
20 | override func setUp() {
21 | super.setUp()
22 | if let path = Bundle(for: type(of: self)).path(forResource: "PerformanceTest", ofType: "json"), let data = NSData(contentsOfFile: path) {
23 | largeData = data
24 | }
25 | if let path = Bundle(for: type(of: self)).path(forResource: "PerformanceSmallTest", ofType: "json"), let data = NSData(contentsOfFile: path) {
26 | smallData = data
27 | }
28 | }
29 |
30 |
31 | func testBigPerformance() {
32 | self.measure { () -> Void in
33 | do {
34 | self.jsonDict = try JSONSerialization.jsonObject(with: self.largeData as Data, options: .allowFragments) as? NSDictionary
35 | let _ = PerformanceTestModel.array(self.jsonDict.object(forKey: "data"))
36 | }
37 | catch {
38 | print(error)
39 | }
40 | }
41 | }
42 |
43 | func testSmallPerformance() {
44 | self.measure {
45 | do {
46 | self.smallJsonDict = try JSONSerialization.jsonObject(with: self.smallData as Data, options: .allowFragments) as? NSDictionary
47 | let _ = PerformanceTestSmallModel.array(self.smallJsonDict.object(forKey: "data"))
48 | }
49 | catch {
50 | print(error)
51 | }
52 | }
53 | }
54 |
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/SerializablePrimitivesTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SerializablePrimitivesTests.swift
3 | // NOCore
4 | //
5 | // Created by Dominik Hádl on 17/01/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import XCTest
11 |
12 | // TODO: Add tests for explicit Int8, Int16, Int32, Int64 and all UInt variants, Float80 and UnicodeScalar
13 |
14 | class SerializablePrimitivesTests: XCTestCase {
15 |
16 | var bridgedDictionary: NSDictionary!
17 | var testModel: PrimitivesTestModel!
18 |
19 | override func setUp() {
20 | super.setUp()
21 | // Put setup code here. This method is called before the invocation of each test method in the class.
22 |
23 | do {
24 | if let path = Bundle(for: type(of: self)).path(forResource: "PrimitivesTest", ofType: "json"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
25 | bridgedDictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary
26 | testModel = PrimitivesTestModel(dictionary: bridgedDictionary)
27 | }
28 | } catch {
29 | XCTAssert(false, "Failed to prepare bridged dictionary.")
30 | return
31 | }
32 | }
33 |
34 | override func tearDown() {
35 | // Put teardown code here. This method is called after the invocation of each test method in the class.
36 | super.tearDown()
37 | }
38 |
39 | func testIntegerParsing() {
40 | XCTAssertEqual(testModel.integer, 1, "Integer parsing test failed!")
41 | XCTAssertEqual(testModel.optionalInteger, 2, "Optional integer parsing test failed!")
42 | XCTAssertEqual(testModel.optionalIntegerWithDefaultValue, 3, "Optional integer with default value parsing test failed!")
43 | XCTAssertEqual(testModel.intString, 1, "Integer parsing from string failed")
44 | }
45 |
46 | func testNegativeIntegerParsing() {
47 | XCTAssertEqual(testModel.negativeInteger, -10, "Negative integer parsing test failed!")
48 | XCTAssertEqual(testModel.optionalNegativeInteger, -20, "Optional negative integer parsing test failed!")
49 | XCTAssertEqual(testModel.optionalNegativeIntegerWithDefaultValue, -30, "Optional negative integer with default value parsing test failed!")
50 | }
51 |
52 | func testDoubleParsing() {
53 | XCTAssertEqual(testModel.double, 123.1234567, "Double parsing test failed!")
54 | XCTAssertEqual(testModel.optionalDouble, 234.2345678, "Optional double parsing test failed!")
55 | XCTAssertEqual(testModel.optionalDoubleWithDefaultValue, 345.3456789, "Optional double with default value parsing test failed!")
56 | XCTAssertEqual(testModel.doubleString, 1.5, "Double parsing from string failed")
57 | }
58 |
59 | func testFloatParsing() {
60 | XCTAssertEqual(testModel.float, 1.234, "Float parsing test failed!")
61 | XCTAssertEqual(testModel.optionalFloat, 2.345, "Optional float parsing test failed!")
62 | XCTAssertEqual(testModel.optionalFloatWithDefaultValue, 3.456, "Optional float with default value parsing test failed!")
63 | }
64 |
65 | func testBoolParsing() {
66 | XCTAssertTrue(testModel.bool, "Bool parsing test failed!")
67 | XCTAssertTrue(testModel.optionalBool ?? false, "Optional bool parsing test failed!")
68 | XCTAssertTrue(testModel.optionalBoolWithDefaultValue ?? false, "Optional bool with default value parsing test failed!")
69 | XCTAssertEqual(testModel.boolString, true, "Bool parsing from string failed")
70 | XCTAssertEqual(testModel.boolIntString, true, "Bool parsing from integer string failed")
71 | }
72 |
73 | func testCharParsing() {
74 | XCTAssertEqual(testModel.char, "S", "Character parsing test failed!")
75 | XCTAssertEqual(testModel.optionalChar, "O", "Optional character parsing test failed!")
76 | XCTAssertEqual(testModel.optionalCharWithDefaultValue, "D", "Optional character with default value parsing test failed!")
77 | }
78 |
79 | func testStringParsing() {
80 | XCTAssertEqual(testModel.string, "success", "String parsing test failed!")
81 | XCTAssertEqual(testModel.optionalString, "optional success", "Optional string parsing test failed!")
82 | XCTAssertEqual(testModel.optionalStringWithDefaultValue, "optional default success", "Optional string with default value parsing test failed!")
83 | XCTAssertEqual(testModel.stringDouble, "1.5", "String parsing from double failed")
84 | XCTAssertEqual(testModel.stringBool, "1", "String parsing from bool failed")
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Serpent/SerpentTests/Tests/StringInitializableTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringInitializableTests.swift
3 | // Serializable
4 | //
5 | // Created by Chris Combs on 17/02/16.
6 | // Copyright © 2016 Nodes. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | import Serpent
11 |
12 | class StringInitializableTests: XCTestCase {
13 | var testModel: StringInitializableTestModel!
14 |
15 | override func setUp() {
16 | super.setUp()
17 | do {
18 | if let path = Bundle(for: type(of: self)).path(forResource: "StringInitializableTest", ofType: "json"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
19 | Date.customDateFormats = ["yyyy-MM-dd HH:mm:ss"]
20 | let bridgedDictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary
21 | testModel = StringInitializableTestModel(dictionary: bridgedDictionary)
22 | }
23 | } catch {
24 | XCTFail("Failed to prepare bridged dictionary.")
25 | return
26 | }
27 | }
28 |
29 | override func tearDown() {
30 | super.tearDown()
31 | Date.customDateFormats = []
32 | }
33 |
34 | func testNSURL() {
35 | XCTAssertNotNil(testModel.someUrl, "Failed to create NSURL from string")
36 | XCTAssertEqual(testModel.someUrl, URL(string: "http://www.google.com"), "Failed to parse URL")
37 | XCTAssertNil(testModel.someEmptyURL, "Failed to return nil when parsing empty string")
38 | XCTAssertEqual(testModel.someUrl?.stringRepresentation(), "http://www.google.com", "String representation of URL differs.")
39 | }
40 |
41 | func testNSDate() {
42 | XCTAssertEqual(testModel.someDate, Date(timeIntervalSince1970: 145811834), "Failed to parse NSDate")
43 | XCTAssertNil(testModel.someEmptyDate, "Failed to return nil when parsing empty date")
44 | XCTAssertNil(testModel.someBadDate, "Failed to return nil when parsing bad date")
45 | XCTAssertEqual(Date.fromString(testModel.someDate?.stringRepresentation() ?? ""), testModel.someDate, "String representation of URL differs.")
46 | XCTAssertEqual(Date.fromString(testModel.someCustomDate?.stringRepresentation() ?? ""), testModel.someCustomDate, "String representation of custom URL differs.")
47 | }
48 |
49 | func testNSURLEncoding() {
50 | guard let encodedModel = testModel.encodableRepresentation() as? NSDictionary else { XCTFail("encodableRepresentation() Failed"); return }
51 |
52 | if let someUrl = encodedModel["some_url"] as? String {
53 | XCTAssertEqual(someUrl, "http://www.google.com", "Failed to encode NSURL to String")
54 | } else {
55 | XCTFail("Failed to encode NSURL to String")
56 | }
57 | }
58 |
59 | func testNSDateEncoding() {
60 | guard let encodedModel = testModel.encodableRepresentation() as? NSDictionary else { XCTFail("encodableRepresentation() Failed"); return }
61 |
62 | if let someDateString = encodedModel["some_date"] as? String {
63 | let someDate = DateFormatter().date(from: someDateString)
64 | XCTAssertEqual(someDate, DateFormatter().date(from: "1974-08-15T15:17:14+00:00"), "Failed to encode NSDate to String")
65 | } else {
66 | XCTFail("Failed to encode NSDate to String")
67 | }
68 | }
69 |
70 | func testCustomNSDateEncoding() {
71 | guard let encodedModel = testModel.encodableRepresentation() as? NSDictionary else { XCTFail("encodableRepresentation() Failed"); return }
72 |
73 | if let someDateString = encodedModel["some_custom_date"] as? String {
74 | let someDate = DateFormatter().date(from: someDateString)
75 | XCTAssertEqual(someDate, DateFormatter().date(from: "2016-11-15 17:19:04"), "Failed to encode NSDate to String")
76 | } else {
77 | XCTFail("Failed to encode NSDate to String")
78 | }
79 | }
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/Serpent_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ml-archive/Serpent/212d6d237670cdba48489c27bc67a89da3cbb9b4/Serpent_icon.png
--------------------------------------------------------------------------------
/Sources/Serpent/BridgingBox.swift:
--------------------------------------------------------------------------------
1 | ../../Serpent/Serpent/Classes/Other/BridgingBox.swift
--------------------------------------------------------------------------------
/Sources/Serpent/Extensions.swift:
--------------------------------------------------------------------------------
1 | ../../Serpent/Serpent/Classes/Extensions/Extensions.swift
--------------------------------------------------------------------------------
/Sources/Serpent/Operator.swift:
--------------------------------------------------------------------------------
1 | ../../Serpent/Serpent/Classes/Operators/Operator.swift
--------------------------------------------------------------------------------
/Sources/Serpent/Serpent.swift:
--------------------------------------------------------------------------------
1 | ../../Serpent/Serpent/Classes/Serpent.swift
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | status:
3 | patch: false
4 | ignore:
5 | - "Serpent/SerpentTests/.*"
6 |
--------------------------------------------------------------------------------