Repository: freshOS/Arrow Branch: master Commit: 32f1ddfe4160 Files: 73 Total size: 388.8 KB Directory structure: gitextract__riymgu1/ ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── .swiftlint.yml ├── CODE_OF_CONDUCT.md ├── LICENSE ├── Package.swift ├── README.md ├── Sources/ │ ├── Arrow/ │ │ ├── Arrow.swift │ │ ├── Extensions.swift │ │ └── JSON.swift │ └── PrivacyInfo.xcprivacy ├── Tests/ │ └── ArrowTests/ │ ├── ArrayTests.swift │ ├── CustomModelTests.swift │ ├── CustomRawRepresentableTests.swift │ ├── DateTests.swift │ ├── EnumTests.swift │ ├── JSON/ │ │ └── MockJSON.swift │ ├── Mapping/ │ │ ├── CustomModelContainer+JSON.swift │ │ ├── CustomRawRepresentableContainer+JSON.swift │ │ ├── DateContainer+JSON.swift │ │ ├── EnumContainer+JSON.swift │ │ ├── PhoneNumber+JSON.swift │ │ ├── Profile+JSON.swift │ │ ├── Stats+JSON.swift │ │ ├── StringContainer+JSON.swift │ │ └── URLContainer+JSON.swift │ ├── Models/ │ │ ├── CustomModelContainer.swift │ │ ├── CustomRawRepresentableContainer.swift │ │ ├── DateContainer.swift │ │ ├── Difficulty.swift │ │ ├── EnumContainer.swift │ │ ├── PhoneNumber.swift │ │ ├── Profile.swift │ │ ├── Stats.swift │ │ ├── StringContainer.swift │ │ └── URLContainer.swift │ ├── NativeTypesTests.swift │ ├── StringCoercionTests.swift │ ├── StringTests.swift │ ├── TypeConversionTests.swift │ ├── URLTests.swift │ └── WeekDay.swift └── docs/ ├── Classes/ │ ├── Arrow.html │ └── JSON.html ├── Classes.html ├── Functions/ │ └── <--(_:_:).html ├── Functions.html ├── Guides.html ├── Protocols/ │ └── ArrowParsable.html ├── Protocols.html ├── css/ │ ├── highlight.css │ └── jazzy.css ├── docsets/ │ ├── Arrow.docset/ │ │ └── Contents/ │ │ ├── Info.plist │ │ └── Resources/ │ │ ├── Documents/ │ │ │ ├── Classes/ │ │ │ │ ├── Arrow.html │ │ │ │ └── JSON.html │ │ │ ├── Classes.html │ │ │ ├── Functions/ │ │ │ │ └── <--(_:_:).html │ │ │ ├── Functions.html │ │ │ ├── Guides.html │ │ │ ├── Protocols/ │ │ │ │ └── ArrowParsable.html │ │ │ ├── Protocols.html │ │ │ ├── css/ │ │ │ │ ├── highlight.css │ │ │ │ └── jazzy.css │ │ │ ├── index.html │ │ │ ├── js/ │ │ │ │ └── jazzy.js │ │ │ ├── readme.html │ │ │ └── undocumented.json │ │ └── docSet.dsidx │ └── Arrow.tgz ├── index.html ├── js/ │ └── jazzy.js ├── readme.html └── undocumented.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ open_collective: freshos github: s4cha ================================================ FILE: .gitignore ================================================ # Xcode # build/ *.pbxuser !default.pbxuser *.mode1v3 !default.mode1v3 *.mode2v3 !default.mode2v3 *.perspectivev3 !default.perspectivev3 xcuserdata *.xccheckout *.moved-aside DerivedData *.hmap *.ipa *.xcuserstate .DS_Store .swiftpm # CocoaPods # # We recommend against adding the Pods directory to your .gitignore. However # you should judge for yourself, the pros and cons are mentioned at: # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control # # Pods/ # Carthage # # Add this line if you want to avoid checking in source code from Carthage dependencies. # Carthage/Checkouts Carthage/Build # SPM /.build /Packages /*.xcodeproj ================================================ FILE: .swiftlint.yml ================================================ disabled_rules: - cyclomatic_complexity - trailing_whitespace - valid_docs - type_name - missing_docs - conditional_binding_cascade - force_unwrapping - identifier_name opt_in_rules: - empty_count - missing_docs - force_unwrapping ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at sachadso@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Package.swift ================================================ // swift-tools-version:6.0 import PackageDescription let package = Package( name: "Arrow", platforms: [ .iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v4)], products: [.library(name: "Arrow", targets: ["Arrow"])], targets: [ .target(name: "Arrow", path: "Sources", resources: [.copy("PrivacyInfo.xcprivacy")]), .testTarget(name: "ArrowTests", dependencies: ["Arrow"]) ] ) ================================================ FILE: README.md ================================================ ![Arrow](https://raw.githubusercontent.com/freshOS/Arrow/master/banner.png) # Arrow [![Language: Swift 6](https://img.shields.io/badge/language-swift6-f48041.svg?style=flat)](https://developer.apple.com/swift) ![Platform: iOS 8+](https://img.shields.io/badge/platform-iOS%208%2B-blue.svg?style=flat) [![SPM compatible](https://img.shields.io/badge/SPM-compatible-4BC51D.svg?style=flat)](https://swift.org/package-manager) [![Build Status](https://app.bitrise.io/app/57b6b1b8959ef398/status.svg?token=WBTcuBRgfLeDB6-A3j7gFA)](https://app.bitrise.io/app/57b6b1b8959ef398) [![codebeat badge](https://codebeat.co/badges/f037ac0c-b3d9-4132-9fca-64150a908113)](https://codebeat.co/projects/github-com-freshos-arrow) [![License: MIT](http://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](https://github.com/freshOS/Arrow/blob/master/LICENSE) ![Release version](https://img.shields.io/github/release/freshos/Arrow.svg) [Reason](#reason) - [Example](#example) - [Installation](#installation) ```swift identifier <-- json["id"] name <-- json["name"] stats <-- json["stats"] ``` Because parsing JSON in Swift is full of **unecessary if lets, obvious casts and nil-checks** *There must be a better way* ## Try it Arrow is part of [freshOS](https://freshos.github.io/) iOS toolset. Try it in an example App! Download Starter Project ## How By using a simple arrow operator that takes care of the boilerplate code for us. Json mapping code becomes **concise** and **maintainable** ❤️ ## Why use Arrow - [x] Infers types - [x] Leaves your models clean - [x] Handles custom & nested models - [x] Dot and array syntax - [x] Pure Swift, Simple & Lightweight ## Example ### Swift Model ```swift struct Profile { var identifier = 0 var name = "" var link:NSURL? var weekday:WeekDay = .Monday var stats = Stats() var phoneNumbers = [PhoneNumber]() } ``` ### JSON File ```json { "id": 15678, "name": "John Doe", "link": "https://apple.com/steve", "weekdayInt" : 3, "stats": { "numberOfFriends": 163, "numberOfFans": 10987 }, "phoneNumbers": [{ "label": "house", "number": "9809876545" }, { "label": "cell", "number": "0908070656" }, { "label": "work", "number": "0916570656" }] } ``` ### Before (Chaos) ```swift var profile = Profile() // Int if let id = json["id"] as? Int { profile.identifier = id } // String if let name = json["name"] as? String { profile.name = name } // NSURL if let link = json["link"] as? String, url = NSURL(string:link) { profile.link = link } // Enum if let weekdayInt = json["weekdayInt"] as? Int, weekday = WeekDay(rawValue:weekdayInt) { profile.weekday = weekday } // Custom nested object if let statsJson = json["stats"] as? AnyObject { if let numberOfFans = statsJson["numberOfFans"] as? Int { profile.stats.numberOfFans = numberOfFans } if let numberOfFriends = statsJson["numberOfFriends"] as? Int { profile.stats.numberOfFriends = numberOfFriends } } // Array of custom nested object if let pns = json["phoneNumbers"] as? [AnyObject] { for pn in pns { phoneNumbers.append(PhoneNumber(json: pn)) } } ``` ### After 🎉🎉🎉 ```swift extension Profile:ArrowParsable { mutating func deserialize(_ json: JSON) { identifier <-- json["id"] link <-- json["link"] name <-- json["name"] weekday <-- json["weekdayInt"] stats <- json["stats"] phoneNumbers <-- json["phoneNumbers"] } } ``` ### Usage ```swift let profile = Profile() profile.deserialize(json) ``` ## Installation The Swift Package Manager (SPM) is now the official way to install `Arrow`. The other package managers are now deprecated as of `5.1.2` and won't be supported in future versions. #### Swift Package Manager `Xcode` > `File` > `Swift Packages` > `Add Package Dependency...` > `Paste` `https://github.com/freshOS/Arrow` #### Carthage - Deprecated ``` github "freshOS/Arrow" ``` #### CocoaPods - Deprecated ``` target 'MyApp' pod 'Arrow' use_frameworks! ``` ## How Does That Work Notice earlier we typed : ```swift stats <-- json["stats"] ``` That's because we created and extension "Stats+Arrow.swift" enabling us to use the Arrow Operator ```swift // Stats+Arrow.swift import Foundation extension Stats:ArrowParsable { mutating func deserialize(json: JSON) { numberOfFriends <-- json["numberOfFriends"] numberOfFans <-- json["numberOfFans"] } } ``` ## Flexible you said - DO I have to use the <-- for my sub models - Nope, you could write it like so if you wanted : ```swift stats.numberOfFriends <-- json["stats.numberOfFriends"] stats.numberOfFans <-- json["stats.numberOfFans"] ``` ## Date Parsing ### Globally ```swift // Configure Global Date Parsing with one of those Arrow.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") Arrow.setUseTimeIntervalSinceReferenceDate(true) Arrow.setDateFormatter(aDateFormatter) // Then later dates can be parsed form custom date format or timestamps automatically 🎉 let json:JSON = JSON(["date": "2013-06-07T16:38:40+02:00", "timestamp": 392308720]) date1 <-- json["date"] date2 <-- json["timestamp"] ``` ### On a per-key basis ```swift createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") createdAt <-- json["created_at"]?.dateFormatter(aCustomDateFormatter) ``` Just provide it on a case per case basis ! 🎉 ## Accessing JSON values ### Nested values ```swift value <-- json["nested.nested.nested.nestedValue"] ``` ### Object at index ```swift value <-- json[12] ``` ### Combine both ```swift value <-- json[1]?["someKey"]?[2]?["something.other"] ``` ### Looping on Array ```swift if let collection = json.collection { for jsonEntry in collection { //Do something } } ``` ## Swift Version - Swift 2 -> version [**2.0.3**](https://github.com/freshOS/Arrow/releases/tag/2.0.3) - Swift 3 -> version [**3.0.5**](https://github.com/freshOS/Arrow/releases/tag/3.0.5) - Swift 4 -> version [**4.0.0**](https://github.com/freshOS/Arrow/releases/tag/4.0.0) - Swift 4.1 -> version [**4.1.0**](https://github.com/freshOS/Arrow/releases/tag/4.1.0) - Swift 4.2 -> version [**4.2.0**](https://github.com/freshOS/Arrow/releases/tag/4.2.0) - Swift 5.0 -> version [**5.0.0**](https://github.com/freshOS/Arrow/releases/tag/5.0.0) - Swift 5.1 -> version [**5.1.0**](https://github.com/freshOS/Arrow/releases/tag/5.1.0) - Swift 5.1.3 -> version [**5.1.1**](https://github.com/freshOS/Arrow/releases/tag/5.1.1) - Swift 5.3 -> version [**6.0.0**](https://github.com/freshOS/Arrow/releases/tag/6.0.0) ## Acknowledgements This wouldn't exist without [YannickDot](https://github.com/YannickDot), [Damien-nd](https://github.com/damien-nd) and [maxkonovalov](https://github.com/maxkonovalov) ### Backers Like the project? Offer coffee or support us with a monthly donation and help us continue our activities :) ### Sponsors Become a sponsor and get your logo on our README on Github with a link to your site :) ================================================ FILE: Sources/Arrow/Arrow.swift ================================================ // // Arrow.swift // Swift Structs Test // // Created by Sacha Durand Saint Omer on 6/7/15. // Copyright (c) 2015 Sacha Durand Saint Omer. All rights reserved. // import Foundation /** This is the protocol that makes your swift Models JSON parsable. A typical implementation would be the following, preferably in an extension called `MyModel+JSON` to keep things nice and clean : // MyModel+JSON.swift import Arrow extension MyModel: ArrowParsable { mutating func deserialize(json: JSON) { myVariable <-- json["jsonProperty"] //... } } */ public protocol ArrowParsable { /// Makes sure your models can be constructed with an empty constructor. init() /// The method you declare your JSON mapping in. mutating func deserialize(_ json: JSON) } public extension ArrowParsable { /// A shortcut to init custom models with JSON. init?(_ json: JSON?) { guard let json = json else { return nil } self.init() self.deserialize(json) } } /** This is used to configure NSDate parsing on a global scale. Arrow.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") // or Arrow.setUseTimeIntervalSinceReferenceDate(true) For more fine grained control, use `dateFormat` on a per field basis : createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") */ public actor Arrow { internal static var dateFormatter: DateFormatter? = DateFormatter() internal static var useReferenceDate = false /// Sets the defaut dateFormat for parsing NSDates. public static func setDateFormat(_ format: String) { dateFormatter?.dateFormat = format } /// Sets the defaut dateFormatter for parsing NSDates. public static func setDateFormatter(_ formatter: DateFormatter?) { dateFormatter = formatter } /** Sets `timeIntervalSinceReferenceDate` parsing as the default for NSDates parsing. Default is `false` For more information see `NSDate(timeIntervalSinceReferenceDate` documentation */ public static func setUseTimeIntervalSinceReferenceDate(_ ref: Bool) { useReferenceDate = ref } } // MARK: - Parse Default swift Types infix operator <-- : AssignmentPrecedence public func <-- (left: inout T, right: JSON?) { setLeftIfIsResultNonNil(left: &left, right: right, function: parseType) } /// Parses optional default swift types. public func <-- (left: inout T?, right: JSON?) { parseType(&left, right: right) } /// Parses enums. public func <-- (left: inout T, right: JSON?) { setLeftIfIsResultNonNil(left: &left, right: right, function: <--) } /// Parses optional enums. public func <-- (left: inout T?, right: JSON?) { var temp: T.RawValue? = nil parseType(&temp, right: right) if let t = temp, let e = T.init(rawValue: t) { left = e } } /// Parses Array of enums. public func <-- (left: inout [T], right: JSON?) { setLeftIfIsResultNonNil(left: &left, right: right, function: <--) } /// Parses Optional Array of enums. public func <-- (left: inout [T]?, right: JSON?) { if let array = right?.data as? [T.RawValue] { left = array.map { T.init(rawValue: $0) }.compactMap {$0} } } /// Parses user defined custom types. public func <-- (left: inout T, right: JSON?) { setLeftIfIsResultNonNil(left: &left, right: right, function: <--) } /// Parses user defined optional custom types. public func <-- (left: inout T?, right: JSON?) { if let json = JSON(right?.data) { var t = T.init() t.deserialize(json) left = t } } /// Parses arrays of user defined custom types. public func <-- (left: inout [T], right: JSON?) { setLeftIfIsResultNonNil(left: &left, right: right, function: <--) } /// Parses optional arrays of user defined custom types. public func <-- (left: inout [T]?, right: JSON?) { if let a = right?.data as? [Any] { left = a.map { var t = T.init() if let json = JSON($0) { t.deserialize(json) } return t } } } /// Parses user defined custom types conforming to `RawRepresentable` protocol. public func <-- (left: inout T, right: JSON?) { setLeftIfIsResultNonNil(left: &left, right: right, function: <--) } /// Parses user defined optional custom types conforming to `RawRepresentable` protocol. public func <-- (left: inout T?, right: JSON?) { if let json = JSON(right?.data) { var t = T.init() t.deserialize(json) left = t } } /// Parses array of user defined custom types conforming to `RawRepresentable` protocol. public func <-- (left: inout [T], right: JSON?) { setLeftIfIsResultNonNil(left: &left, right: right, function: <--) } /// Parses array of user defined optional custom types conforming to `RawRepresentable` protocol. public func <-- (left: inout [T]?, right: JSON?) { if let a = right?.data as? [Any] { left = a.map { var t = T.init() if let json = JSON($0) { t.deserialize(json) } return t } } } /// Parses NSDates. public func <-- (left: inout Date, right: JSON?) { setLeftIfIsResultNonNil(left: &left, right: right, function: <--) } /// Parses optional NSDates. public func <-- (left: inout Date?, right: JSON?) { // Use custom date format over high level setting when provided if let customFormatter = right?.jsonDateFormatter, let s = right?.data as? String { left = customFormatter.date(from: s) } else if let customFormat = right?.jsonDateFormat, let s = right?.data as? String { let df = DateFormatter() df.dateFormat = customFormat left = df.date(from: s) } else if let s = right?.data as? String { if let date = Arrow.dateFormatter?.date(from: s) { left = date } else if let t = TimeInterval(s) { left = timeIntervalToDate(t) } } else if let t = right?.data as? TimeInterval { left = timeIntervalToDate(t) } } /// Parses NSURLs. public func <-- (left: inout URL, right: JSON?) { setLeftIfIsResultNonNil(left: &left, right: right, function: <--) } /// Parses optional NSURLs. public func <-- (left: inout URL?, right: JSON?) { var str = "" str <-- right let set = CharacterSet.urlQueryAllowed if let escapedStr = str.addingPercentEncoding(withAllowedCharacters: set), let url = URL(string: escapedStr) { left = url } } /// Parses arrays of plain swift types. public func <-- (left: inout [T], right: JSON?) { setLeftIfIsResultNonNil(left: &left, right: right, function: <--) } /// Parses optional arrays of plain swift types. public func <-- (left: inout [T]?, right: JSON?) { if let a = right?.data as? [Any] { let tmp: [T] = a.compactMap { var t: T?; parseType(&t, right: JSON($0)); return t } if tmp.count == a.count { left = tmp } } } /// Parses dictionaries of plain swift types. public func <-- (left: inout [K: V], right: JSON?) { setLeftIfIsResultNonNil(left: &left, right: right, function: <--) } /// Parses optional dictionaries of plain swift types. public func <-- (left: inout [K: V]?, right: JSON?) { if let d = right?.data as? [AnyHashable: Any] { var tmp: [K: V] = [:] d.forEach { var k: K?; parseType(&k, right: JSON($0)) var v: V?; parseType(&v, right: JSON($1)) if let k = k, let v = v { tmp[k] = v } } if tmp.count == d.count { left = tmp } } } // MARK: - Private methods. func parseType(_ left: inout T?, right: JSON?) { if let v: T = right?.data as? T { left = v } else if let s = right?.data as? String { parseString(&left, string: s) } else if T.self == Float.self { // Sepcial case for Float that // no longer works out of the box in Swift 4.1 if let v = right?.data as? Double, let l = Float(v) as? T { left = l } } } func parseString(_ left: inout T?, string: String) { switch T.self { case is Int.Type: if let v = Int(string) { left = v as? T } case is UInt.Type: if let v = UInt(string) { left = v as? T } case is Double.Type: if let v = Double(string) { left = v as? T } case is Float.Type: if let v = Float(string) { left = v as? T } case is CGFloat.Type: if let v = CGFloat.NativeType(string) { left = CGFloat(v) as? T } case is Bool.Type: if let v = Int(string) { left = Bool(v != 0) as? T} default:() } } func timeIntervalToDate(_ timeInterval: TimeInterval) -> Date { return Arrow.useReferenceDate ? Date(timeIntervalSinceReferenceDate: timeInterval) : Date(timeIntervalSince1970: timeInterval) } func setLeftIfIsResultNonNil(left: inout T, right: JSON?, function: (inout T?, JSON?) -> Void) { var temp: T? = nil function(&temp, right) if let t = temp { left = t } } ================================================ FILE: Sources/Arrow/Extensions.swift ================================================ // // Extensions.swift // Arrow // // Created by Max Konovalov on 07/11/2016. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation public protocol ArrowInitializable { init?(_ json: JSON?) } // MARK: - Plain types extension String: ArrowInitializable { /// Construct a `String` from JSON public init?(_ json: JSON?) { var x: String? x <-- json guard let s = x else { return nil } self.init(s) } } extension Int: ArrowInitializable { /// Construct an `Int` from JSON public init?(_ json: JSON?) { var x: Int? x <-- json guard let i = x else { return nil } self.init(i) } } extension UInt: ArrowInitializable { /// Construct a `UInt` from JSON public init?(_ json: JSON?) { var x: UInt? x <-- json guard let u = x else { return nil } self.init(u) } } extension Double: ArrowInitializable { /// Construct a `Double` from JSON public init?(_ json: JSON?) { var x: Double? x <-- json guard let d = x else { return nil } self.init(d) } } extension Float: ArrowInitializable { /// Construct a `Float` from JSON public init?(_ json: JSON?) { var x: Float? x <-- json guard let f = x else { return nil } self.init(f) } } extension CGFloat: ArrowInitializable { /// Construct a `CGFloat` from JSON public init?(_ json: JSON?) { var x: CGFloat? x <-- json guard let f = x else { return nil } self.init(f) } } extension Bool: ArrowInitializable { /// Construct a `Bool` from JSON public init?(_ json: JSON?) { var x: Bool? x <-- json guard let b = x else { return nil } self.init(b) } } extension URL: ArrowInitializable { /// Construct a `URL` from JSON public init?(_ json: JSON?) { var x: String? x <-- json guard let s = x else { return nil } self.init(string: s) } } // MARK: - Raw representable extension RawRepresentable where RawValue: ArrowInitializable { /// Construct a `RawRepresentable` from JSON public init?(_ json: JSON?) { var x: Self.RawValue? x <-- json guard let r = x else { return nil } self.init(rawValue: r) } } // MARK: - Arrays extension Array where Element: ArrowInitializable { /// Construct an `Array` from JSON public init?(_ json: JSON?) { var x: [Element]? x <-- json guard let a = x else { return nil } self.init(a) } } extension Array where Element: RawRepresentable, Element.RawValue: ArrowInitializable { /// Construct an `Array` from JSON public init?(_ json: JSON?) { var x: [Element]? x <-- json guard let a = x else { return nil } self.init(a) } } // MARK: - Dictionaries extension Dictionary where Value: ArrowInitializable { /// Construct a `Dictionary` from JSON public init?(_ json: JSON?) { var x: [Key: Value]? x <-- json guard let d = x else { return nil } self.init() for (k, v) in d { self.updateValue(v, forKey: k) } } } ================================================ FILE: Sources/Arrow/JSON.swift ================================================ // // JSON.swift // ArrowExample // // Created by Sacha Durand Saint Omer on 14/04/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation /** This abstraction helps working with the JSON Format. It provives a way to access JSON values via subscripting, whether it's an array or a dictionary. */ open class JSON { /// This is the raw data of the JSON open var data: Any? /// This date formating strategy that will be used for that JSON section. /// This should not be set, use `dateFormat` instead. internal var jsonDateFormat: String? internal var jsonDateFormatter: DateFormatter? /// This build a JSON object with raw data. public init?(_ data: Any?) { guard let data = data else { return nil } if let jsonString = data as? String, let jsonData = jsonString.data(using: .utf8), let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) { self.data = jsonObject } else { self.data = data } } /** - Returns: The array of JSON values. In case of the JSON being a dictionary, this will return nil. */ open var collection: [JSON]? { guard let a = data as? [Any] else { return nil } return a.map { JSON($0) }.compactMap {$0} } /** This defines the date format to be used for NSDate parsing. createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") - Returns: Itself for chaining purposes. */ open func dateFormat(_ format: String) -> Self { jsonDateFormat = format return self } open func dateFormatter(_ formatter: DateFormatter) -> Self { jsonDateFormatter = formatter return self } func isKeyPath(_ key: String) -> Bool { return key.split {$0 == "."}.count > 1 } func parseKeyPath(_ keyPath: String) -> JSON? { if var intermediateValue = JSON(data) { for k in keysForKeyPath(keyPath) { if !tryParseJSONKeyPathKey(k, intermediateValue: &intermediateValue) { return nil } } return intermediateValue } return nil } func keysForKeyPath(_ keyPath: String) -> [String] { return keyPath.split {$0 == "."}.map(String.init) } func tryParseJSONKeyPathKey(_ key: String, intermediateValue: inout JSON) -> Bool { if let ik = Int(key), let value = intermediateValue[ik] { // Array index intermediateValue = value } else if let value = intermediateValue[key] { //key intermediateValue = value } else { return false } return true } func regularParsing(_ key: String) -> JSON? { guard let d = data as? [String: Any], let x = d[key], let subJSON = JSON(x) else { return nil } return subJSON } open subscript(key: String) -> JSON? { get { return isKeyPath(key) ? parseKeyPath(key) : regularParsing(key) } set(obj) { if var d = data as? [String: Any] { d[key] = obj } } } open subscript(index: Int) -> JSON? { guard let array = data as? [Any], array.count > index else { return nil } return JSON(array[index]) } } extension JSON: CustomDebugStringConvertible { /// This is just for supporting default console logs. public var debugDescription: String { return data.debugDescription } } ================================================ FILE: Sources/PrivacyInfo.xcprivacy ================================================ NSPrivacyTracking NSPrivacyTrackingDomains NSPrivacyCollectedDataTypes NSPrivacyAccessedAPITypes ================================================ FILE: Tests/ArrowTests/ArrayTests.swift ================================================ // // ArrayTests.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Testing import Arrow struct ArrayContainer { var phoneNumbers = [PhoneNumber]() var optionalPhoneNumbers: [PhoneNumber]? var strings = [String]() var ints = [Int]() var bools = [Bool]() var meaningOfLife: Int = 0 var nestedArrayParsing = "" var weekdays = [WeekDay]() var optionalWeekdays: [WeekDay]? } extension ArrayContainer: ArrowParsable { mutating func deserialize(_ json: JSON) { phoneNumbers <-- json["phoneNumbers"] optionalPhoneNumbers <-- json["phoneNumbers"] strings <-- json["strings"] ints <-- json["ints"] bools <-- json["bools"] meaningOfLife <-- json["nested.nested.nested.nestedValue"] nestedArrayParsing <-- json["nestedArray.2"] weekdays <-- json["weekdays"] optionalWeekdays <-- json["weekdays"] } } @Suite struct ArrayTests { var arrayContainer = ArrayContainer() init() { if let json: JSON = mockJSON() { arrayContainer <-- json } } @Test func parsingArrayOfCustomModels() { #expect(arrayContainer.phoneNumbers.count == 3) if arrayContainer.phoneNumbers.count >= 3 { #expect(arrayContainer.phoneNumbers[0].label == "house") #expect(arrayContainer.phoneNumbers[1].label == "cell") #expect(arrayContainer.phoneNumbers[2].label == "work") #expect(arrayContainer.phoneNumbers[0].number == "9809876545") #expect(arrayContainer.phoneNumbers[1].number == "0908070656") #expect(arrayContainer.phoneNumbers[2].number == "0916570656") } else { Issue.record("Parsing ArrayOf Custom Models Fails") } } @Test func parsingOptionalArrayOfCustomModels() { #expect(arrayContainer.optionalPhoneNumbers?.count == 3) #expect(arrayContainer.optionalPhoneNumbers?[0].label == "house") #expect(arrayContainer.optionalPhoneNumbers?[1].label == "cell") #expect(arrayContainer.optionalPhoneNumbers?[2].label == "work") #expect(arrayContainer.optionalPhoneNumbers?[0].number == "9809876545") #expect(arrayContainer.optionalPhoneNumbers?[1].number == "0908070656") #expect(arrayContainer.optionalPhoneNumbers?[2].number == "0916570656") } @Test func parsingArrayOfStrings() { #expect(arrayContainer.strings.count == 3) if arrayContainer.strings.count >= 3 { #expect(arrayContainer.strings[0] == "one") #expect(arrayContainer.strings[1] == "two") #expect(arrayContainer.strings[2] == "three") } else { Issue.record("Parsing an array of strings fails") } } @Test func parsingArrayOfInts() { #expect(arrayContainer.ints.count == 3) if arrayContainer.ints.count >= 3 { #expect(arrayContainer.ints[0] == 1) #expect(arrayContainer.ints[1] == 2) #expect(arrayContainer.ints[2] == 3) } else { Issue.record("Parsing an array of ints fails") } } @Test func parsingArrayOfBools() { #expect(arrayContainer.bools.count == 3) if arrayContainer.bools.count >= 3 { #expect(arrayContainer.bools[0] == true) #expect(arrayContainer.bools[1] == false) #expect(arrayContainer.bools[2] == true) } else { Issue.record("Parsing an array of bools fails") } } @Test func nestedParsing() { #expect(arrayContainer.meaningOfLife == 42) } @Test func nestedArrayParsing() { #expect(arrayContainer.nestedArrayParsing == "Cool") } @Test func parsingArrayOfEnums() { #expect(arrayContainer.weekdays.count == 3) if arrayContainer.weekdays.count >= 3 { #expect(arrayContainer.weekdays[0] == WeekDay.monday) #expect(arrayContainer.weekdays[1] == WeekDay.wednesday) #expect(arrayContainer.weekdays[2] == WeekDay.friday) } else { Issue.record("Parsing an array of enums fails") } } @Test func parsingOptionalArrayOfEnums() { #expect(arrayContainer.optionalWeekdays?.count == 3) if let w = arrayContainer.optionalWeekdays, w.count >= 3 { #expect(arrayContainer.optionalWeekdays?[0] == WeekDay.monday) #expect(arrayContainer.optionalWeekdays?[1] == WeekDay.wednesday) #expect(arrayContainer.optionalWeekdays?[2] == WeekDay.friday) } else { Issue.record("Parsing an array of optional enums fails") } } } ================================================ FILE: Tests/ArrowTests/CustomModelTests.swift ================================================ // // CustomModelTests.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Testing import Foundation import Arrow @Suite struct CustomModelTests { var customModelContainer = CustomModelContainer() init() { if let json: JSON = mockJSON() { customModelContainer <-- json } } @Test func testParsingCustomModel() { #expect(customModelContainer.stats.numberOfFriends == 163) #expect(customModelContainer.stats.numberOfFans == 10987) } @Test func testParsingOptionalCustomModel() { #expect(customModelContainer.optionalStats?.numberOfFriends == 163) #expect(customModelContainer.optionalStats?.numberOfFans == 10987) } @Test func testParsingIssue() { let myJson = "{ \"homer\": \"simpson\"}" guard let jsonData = myJson.data(using: .utf8), let jsonObject = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers), let json = JSON(jsonObject) else { return } var aSimpson = Doh() aSimpson.deserialize(json) #expect(aSimpson.homer == "simpson") } @Test func testParsingIssueWorksWithString() { let myJson = "{ \"homer\": \"simpson\"}" guard let json = JSON(myJson) else { return } var aSimpson = Doh() aSimpson.deserialize(json) #expect(aSimpson.homer == "simpson") } } struct Doh: Codable { var homer = "" } extension Doh: ArrowParsable { public mutating func deserialize(_ json: JSON) { homer <-- json["homer"] } } ================================================ FILE: Tests/ArrowTests/CustomRawRepresentableTests.swift ================================================ // // CustomRawRepresentableTests.swift // Arrow // // Created by Max Konovalov on 03/11/2016. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Testing import Arrow @Suite struct CustomRawRepresentableTests { var customRawRepresentableContainer = CustomRawRepresentableContainer() init() { if let json: JSON = mockJSON() { customRawRepresentableContainer <-- json } } @Test func testParsingCustomModel() { #expect(customRawRepresentableContainer.identifier == 15678) #expect(customRawRepresentableContainer.rawValue == "15678") } } ================================================ FILE: Tests/ArrowTests/DateTests.swift ================================================ // // DateTests.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Testing import Foundation import Arrow @Suite struct DateTests { var dateContainer = DateContainer() init() { Arrow.setUseTimeIntervalSinceReferenceDate(true) if let json: JSON = mockJSON() { dateContainer <-- json } } @Test func testParsingDate() { let df = DateFormatter() df.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" if let date = df.date(from: "2013-06-07T16:38:40+02:00") { #expect(date.timeIntervalSinceReferenceDate == dateContainer.createdAt.timeIntervalSinceReferenceDate) } else { Issue.record("Parsing a date fails") } } @Test func testParsingOptionalDate() { let timestamp: TimeInterval = 392308720 if let d = dateContainer.optionalDate?.timeIntervalSinceReferenceDate { #expect(timestamp == d) } else { Issue.record("Parsing an Optional Date fails") } } @Test func testCustomDateFormatterDate() { if let json: JSON = mockJSON() { var aDate = Date() let df = DateFormatter() df.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" df.timeZone = TimeZone(secondsFromGMT: 60*60*5) aDate <-- json["created_at"]?.dateFormatter(df) #expect(aDate == df.date(from: "2013-06-07T16:38:40+02:00")) } else { Issue.record("Using a custom date Parser fails") } } @Test func testGlobalDateFormatterDate() { let df = DateFormatter() df.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ" Arrow.setDateFormatter(df) if let json: JSON = mockJSON() { var aDate = Date() aDate <-- json["created_at"] #expect(aDate == df.date(from: "2013-06-07T16:38:40+02:00")) } else { Issue.record("Using a global date parser fails") } Arrow.setDateFormatter(nil) } } ================================================ FILE: Tests/ArrowTests/EnumTests.swift ================================================ // // EnumTests.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Testing import Arrow @Suite struct EnumTests { var enumContainer = EnumContainer() init() { if let json: JSON = mockJSON() { enumContainer <-- json } } @Test func parsingEnumInt() { #expect(enumContainer.weekday == WeekDay.wednesday) } @Test func parsingEnumString() { #expect(enumContainer.difficulty == Difficulty.high) } @Test func parsingOptionalEnumInt() { #expect(enumContainer.optionalWeekday == WeekDay.wednesday) } @Test func parsingOptionalEnumString() { #expect(enumContainer.optionalDifficulty == Difficulty.high) } } ================================================ FILE: Tests/ArrowTests/JSON/MockJSON.swift ================================================ // // File.swift // // // Created by Sacha DSO on 24/12/2019. // import Arrow import Foundation func mockJSON() -> JSON? { let jsonString = """ { "id": "15678", "weekdayInt" : 3, "difficulty": "high", "link": "https://apple.com/steve", "emoji_link": "http://🆒🔗.ws", "accent_link": "http://gégé.com", "created_at" : "2013-06-07T16:38:40+02:00", "created_at_timestamp" : "392308720", "name": "Francky", "stats": { "numberOfFriends": 163, "numberOfFans": 10987 }, "phoneNumbers": [{ "label": "house", "number": "9809876545" }, { "label": "cell", "number": "0908070656" }, { "label": "work", "number": "0916570656" }], "strings": ["one", "two", "three"], "ints": [1, 2, 3], "bools": [true, false, true], "bool": true, "float" : 0.12, "double" : 0.123456789, "floatString" : "0.12", "doubleString" : "0.123456789", "nested":{ "nested" : { "nested" : { "nestedValue" : "42" } } }, "nestedArray" : ["one", "two", "Cool", "four"], "weekdays": [1, 3, 5], "dict": { "one": "1", "two": "2" } } """ if let jsonData = jsonString.data(using: String.Encoding.utf8), let json = ((try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as? NSDictionary) as NSDictionary??), let dic = json as? [String: Any] { return JSON(dic) } return nil } ================================================ FILE: Tests/ArrowTests/Mapping/CustomModelContainer+JSON.swift ================================================ // // CustomModelContainer+JSON.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Arrow extension CustomModelContainer: ArrowParsable { mutating func deserialize(_ json: JSON) { stats <-- json["stats"] optionalStats = nil optionalStats <-- json["stats"] } } ================================================ FILE: Tests/ArrowTests/Mapping/CustomRawRepresentableContainer+JSON.swift ================================================ // // CustomRawRepresentableContainer+JSON.swift // Arrow // // Created by Max Konovalov on 03/11/2016. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Arrow extension CustomRawRepresentableContainer: ArrowParsable { mutating func deserialize(_ json: JSON) { identifier <-- json["id"] } } ================================================ FILE: Tests/ArrowTests/Mapping/DateContainer+JSON.swift ================================================ // // DateContainer+JSON.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Arrow extension DateContainer: ArrowParsable { mutating func deserialize(_ json: JSON) { createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") optionalDate = nil optionalDate <-- json["created_at_timestamp"] } } ================================================ FILE: Tests/ArrowTests/Mapping/EnumContainer+JSON.swift ================================================ // // EnumContainer+JSON.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Arrow extension EnumContainer: ArrowParsable { mutating func deserialize(_ json: JSON) { weekday <-- json["weekdayInt"] difficulty <-- json["difficulty"] optionalWeekday <-- json["weekdayInt"] optionalDifficulty <-- json["difficulty"] } } ================================================ FILE: Tests/ArrowTests/Mapping/PhoneNumber+JSON.swift ================================================ // // PhoneNumber+JSON.swift // ArrowExample // // Created by Sacha Durand Saint Omer on 13/04/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Arrow extension PhoneNumber: ArrowParsable { mutating func deserialize(_ json: JSON) { label <-- json["label"] number <-- json["number"] } } ================================================ FILE: Tests/ArrowTests/Mapping/Profile+JSON.swift ================================================ // // Profile+JSON.swift // ArrowExample // // Created by Sacha Durand Saint Omer on 13/04/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Arrow extension Profile: ArrowParsable { mutating func deserialize(_ json: JSON) { identifier <-- json["id"] cgfloat <-- json["float"] float <-- json["float"] double <-- json["double"] } } ================================================ FILE: Tests/ArrowTests/Mapping/Stats+JSON.swift ================================================ // // Stats+JSON.swift // ArrowExample // // Created by Sacha Durand Saint Omer on 13/04/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Arrow extension Stats: ArrowParsable { mutating func deserialize(_ json: JSON) { numberOfFriends <-- json["numberOfFriends"] numberOfFans <-- json["numberOfFans"] } } ================================================ FILE: Tests/ArrowTests/Mapping/StringContainer+JSON.swift ================================================ // // StringContainer+JSON.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Arrow extension StringContainer: ArrowParsable { mutating func deserialize(_ json: JSON) { name <-- json["name"] optionalName = nil optionalName <-- json["name"] } } ================================================ FILE: Tests/ArrowTests/Mapping/URLContainer+JSON.swift ================================================ // // URLContainer+JSON.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Arrow extension URLContainer: ArrowParsable { mutating func deserialize(_ json: JSON) { link <-- json["link"] emojiLink <-- json["emoji_link"] accentLink <-- json["accent_link"] optionalLink <-- json["link"] } } ================================================ FILE: Tests/ArrowTests/Models/CustomModelContainer.swift ================================================ // // CustomModelContainer.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation struct CustomModelContainer { var stats = Stats() var optionalStats: Stats? } ================================================ FILE: Tests/ArrowTests/Models/CustomRawRepresentableContainer.swift ================================================ // // CustomRawRepresentableContainer.swift // Arrow // // Created by Max Konovalov on 03/11/2016. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation struct CustomRawRepresentableContainer { var identifier: Int = 0 } extension CustomRawRepresentableContainer: RawRepresentable { init?(rawValue: String) { guard let id = Int(rawValue) else { return nil } self.identifier = id } var rawValue: String { return "\(identifier)" } } ================================================ FILE: Tests/ArrowTests/Models/DateContainer.swift ================================================ // // DateContainer.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation struct DateContainer { var createdAt = Date() var optionalDate: Date? } ================================================ FILE: Tests/ArrowTests/Models/Difficulty.swift ================================================ // // Difficulty.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation enum Difficulty: String { case low case medium case high } ================================================ FILE: Tests/ArrowTests/Models/EnumContainer.swift ================================================ // // EnumContainer.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation struct EnumContainer { var weekday: WeekDay = .monday var difficulty = Difficulty.low var optionalWeekday: WeekDay? var optionalDifficulty: Difficulty? } ================================================ FILE: Tests/ArrowTests/Models/PhoneNumber.swift ================================================ // // PhoneNumber.swift // ArrowExample // // Created by Sacha Durand Saint Omer on 30/03/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation struct PhoneNumber { var label: String = "" var number: String = "" } ================================================ FILE: Tests/ArrowTests/Models/Profile.swift ================================================ // // Profile.swift // ArrowExample // // Created by Sacha Durand Saint Omer on 29/03/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation struct Profile { var identifier = 0 var double: Double = 0.0 var float: Float = 0.0 var cgfloat: CGFloat = 0.0 } extension Profile: RawRepresentable { init?(rawValue: String?) { } var rawValue: String? { return "" } } ================================================ FILE: Tests/ArrowTests/Models/Stats.swift ================================================ // // Stats.swift // ArrowExample // // Created by Sacha Durand Saint Omer on 29/03/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation struct Stats { var numberOfFriends = 0 var numberOfFans = 0 } ================================================ FILE: Tests/ArrowTests/Models/StringContainer.swift ================================================ // // StringContainer.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation struct StringContainer { var name = "" var optionalName: String? } ================================================ FILE: Tests/ArrowTests/Models/URLContainer.swift ================================================ // // URLContainer.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation struct URLContainer { var link = URL(string: "http://")! var emojiLink = URL(string: "http://")! var accentLink = URL(string: "http://")! var optionalLink: URL? } ================================================ FILE: Tests/ArrowTests/NativeTypesTests.swift ================================================ // // ArrowTests.swift // ArrowTests // // Created by Sacha Durand Saint Omer on 6/7/15. // Copyright (c) 2015 Sacha Durand Saint Omer. All rights reserved. // import Testing import Arrow struct NativeTypesTests { var profile = Profile() init() { Arrow.setUseTimeIntervalSinceReferenceDate(true) if let json: JSON = mockJSON() { profile <-- json } } @Test func pParsingInt() { #expect(profile.identifier == 15678) } @Test func parsingFloat() { #expect(profile.float == 0.12) } @Test func parsingCGFloat() { #expect(profile.cgfloat == 0.12) } @Test func parsingDouble() { #expect(profile.double == 0.123456789) } } ================================================ FILE: Tests/ArrowTests/StringCoercionTests.swift ================================================ // // StringCoercionTests.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Testing import Foundation import Arrow struct CoercionContainer { var doubleString: Double = 0.0 var floatString: Float = 0.0 var cgfloatString: CGFloat = 0.0 } extension CoercionContainer: ArrowParsable { mutating func deserialize(_ json: JSON) { cgfloatString <-- json["floatString"] floatString <-- json["floatString"] doubleString <-- json["doubleString"] } } @Suite struct StringCoercionTests { var coercionContainer = CoercionContainer() init() { if let json: JSON = mockJSON() { coercionContainer <-- json } } @Test func testParsingFloatString() { #expect(coercionContainer.floatString == 0.12) } @Test func testParsingCGFloatString() { #expect(coercionContainer.cgfloatString == 0.12) } @Test func testParsingDoubleString() { #expect(coercionContainer.doubleString == 0.123456789) } } ================================================ FILE: Tests/ArrowTests/StringTests.swift ================================================ // // StringTests.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Testing import Arrow @Suite struct StringTests { var stringContainer = StringContainer() init() { if let json: JSON = mockJSON() { stringContainer <-- json } } @Test func parsingString() { #expect(stringContainer.name == "Francky") } @Test func parsingOptionalString() { #expect(stringContainer.optionalName == "Francky") } } ================================================ FILE: Tests/ArrowTests/TypeConversionTests.swift ================================================ // // TypeConversionTests.swift // Arrow // // Created by Max Konovalov on 07/11/2016. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Testing import Foundation import Arrow @Suite struct TypeConversionTests { let json: JSON? = mockJSON() @Test func testStringConversion() { #expect(String(json?["name"]) == "Francky") } @Test func testIntConversion() { #expect(Int(json?["id"]) == 15678) } @Test func testUIntConversion() { #expect(UInt(json?["id"]) == 15678) } @Test func testDoubleConversion() { #expect(Double(json?["double"]) == 0.123456789) } @Test func testFloatConversion() { #expect(Float(json?["float"]) == 0.12) } @Test func testCGFloatConversion() { #expect(CGFloat(json?["float"]) == 0.12) } @Test func testBoolConversion() { #expect(Bool(json?["bool"]) == true) } @Test func testEnumConversion() { #expect(WeekDay(json?["weekdayInt"]) == WeekDay.wednesday) } @Test func testArrayConversions() { #expect([String](json?["strings"]) ?? [] == ["one", "two", "three"]) } @Test func testArrayEnumConversions() { #expect([WeekDay](json?["weekdays"]) ?? [] == [.monday, .wednesday, .friday]) } @Test func testDictionaryConversions() { #expect([String: String](json?["dict"]) ?? [:] == ["one": "1", "two": "2"]) } } ================================================ FILE: Tests/ArrowTests/URLTests.swift ================================================ // // URLTests.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Testing import Arrow @Suite struct URLTests { var urlContainer = URLContainer() init() { if let json: JSON = mockJSON() { urlContainer <-- json } } @Test func parsingURL() { #expect(urlContainer.link.absoluteString.removingPercentEncoding == "https://apple.com/steve") } @Test func parsingEmojiURL() { #expect(urlContainer.emojiLink.absoluteString.removingPercentEncoding == "http://🆒🔗.ws") } @Test func parsingAccentURL() { #expect(urlContainer.accentLink.absoluteString.removingPercentEncoding == "http://gégé.com") } @Test func parsingOptionalURL() { #expect(urlContainer.optionalLink?.absoluteString.removingPercentEncoding == "https://apple.com/steve") } } ================================================ FILE: Tests/ArrowTests/WeekDay.swift ================================================ // // WeekDay.swift // Arrow // // Created by Sacha Durand Saint Omer on 10/07/16. // Copyright © 2016 Sacha Durand Saint Omer. All rights reserved. // import Foundation enum WeekDay: Int { case monday = 1 case tuesday case wednesday case thursday case friday case saturday case sunday } ================================================ FILE: docs/Classes/Arrow.html ================================================ Arrow Class Reference

Arrow Docs (72% documented)

View on GitHub

Arrow

public class Arrow

This is used to configure NSDate parsing on a global scale.

    Arrow.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
    // or
    Arrow.setUseTimeIntervalSinceReferenceDate(true)

For more fine grained control, use dateFormat on a per field basis :

    createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
  • Sets the defaut dateFormat for parsing NSDates.

    Declaration

    Swift

    public class func setDateFormat(format: String)
  • Sets timeIntervalSinceReferenceDate parsing as the default for NSDates parsing.

    Default is false

    For more information see NSDate(timeIntervalSinceReferenceDate documentation

    Declaration

    Swift

    public class func setUseTimeIntervalSinceReferenceDate(ref: Bool)
================================================ FILE: docs/Classes/JSON.html ================================================ JSON Class Reference

Arrow Docs (72% documented)

View on GitHub

JSON

public class JSON: AnyObject, CustomDebugStringConvertible

This abstraction helps working with the JSON Format.

It provives a way to access JSON values via subscripting, whether it’s an array or a dictionary.

  • This is the raw data of the JSON

    Declaration

    Swift

    public var data: AnyObject?
  • This date formating strategy that will be used for that JSON section. This should not be set, use dateFormat instead.

    Declaration

    Swift

    public var jsonDateFormat: String?
  • This build a JSON object with raw data.

    Declaration

    Swift

    public init?(_ dic: AnyObject?)
  • Returns

    The array of JSON values. In case of the JSON being a dictionary, this will return nil.

    Declaration

    Swift

    public var collection: [JSON]?

    Return Value

    The array of JSON values. In case of the JSON being a dictionary, this will return nil.

  • This defines the date format to be used for NSDate parsing.

    createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
    

    Returns

    Itself for chaining purposes.

    Declaration

    Swift

    public func dateFormat(format: String) -> Self

    Return Value

    Itself for chaining purposes.

  • This is just for supporting default console logs.

    Declaration

    Swift

    public var debugDescription: String
================================================ FILE: docs/Classes.html ================================================ Classes Reference

Arrow Docs (72% documented)

View on GitHub

Classes

The following classes are available globally.

  • This abstraction helps working with the JSON Format.

    It provives a way to access JSON values via subscripting, whether it’s an array or a dictionary.

    See more

    Declaration

    Swift

    public class JSON: AnyObject, CustomDebugStringConvertible
  • This is used to configure NSDate parsing on a global scale.

        Arrow.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
        // or
        Arrow.setUseTimeIntervalSinceReferenceDate(true)
    

    For more fine grained control, use dateFormat on a per field basis :

        createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
    
    See more

    Declaration

    Swift

    public class Arrow
================================================ FILE: docs/Functions/<--(_:_:).html ================================================ <--(_:_:) Function Reference

Arrow Docs (72% documented)

View on GitHub

<--(_:_:)

public func <-- <T>(inout left: [T]?, right: JSON?)

Parses optional arrays of plain swift types.

  • T

    Undocumented

    Declaration

    Swift

    public func <-- <T>(inout left: [T]?, right: JSON?)
================================================ FILE: docs/Functions.html ================================================ Functions Reference

Arrow Docs (72% documented)

View on GitHub

Functions

The following functions are available globally.

  • Parses default swift types.

    See more

    Declaration

    Swift

    public func <-- <T>(inout left: T, right: JSON?)
  • Parses optional default swift types.

    See more

    Declaration

    Swift

    public func <-- <T>(inout left: T?, right: JSON?)
  • Parses enums.

    See more

    Declaration

    Swift

    public func <-- <T: RawRepresentable>(inout left: T, right: JSON?)
  • Parses optional enums.

    See more

    Declaration

    Swift

    public func <-- <T: RawRepresentable>(inout left: T?, right: JSON?)
  • Parses user defined custom types.

    See more

    Declaration

    Swift

    public func <-- <T: ArrowParsable>(inout left: T, right: JSON?)
  • Parses user defined optional custom types.

    See more

    Declaration

    Swift

    public func <-- <T: ArrowParsable>(inout left: T?, right: JSON?)
  • Parses arrays of user defined custom types.

    See more

    Declaration

    Swift

    public func <-- <T: ArrowParsable>(inout left: [T], right: JSON?)
  • Parses optional arrays of user defined custom types.

    See more

    Declaration

    Swift

    public func <-- <T: ArrowParsable>(inout left: [T]?, right: JSON?)
  • Parses NSDates.

    Declaration

    Swift

    public func <-- (inout left: NSDate, right: JSON?)
  • Parses optional NSDates.

    Declaration

    Swift

    public func <-- (inout left: NSDate?, right: JSON?)
  • Parses NSURLs.

    Declaration

    Swift

    public func <-- (inout left: NSURL, right: JSON?)
  • Parses optional NSURLs.

    Declaration

    Swift

    public func <-- (inout left: NSURL?, right: JSON?)
  • Parses arrays of plain swift types.

    See more

    Declaration

    Swift

    public func <-- <T>(inout left: [T], right: JSON?)
  • Parses optional arrays of plain swift types.

    See more

    Declaration

    Swift

    public func <-- <T>(inout left: [T]?, right: JSON?)
================================================ FILE: docs/Guides.html ================================================ Guides Reference

Arrow Docs (72% documented)

View on GitHub

================================================ FILE: docs/Protocols/ArrowParsable.html ================================================ ArrowParsable Protocol Reference

Arrow Docs (72% documented)

View on GitHub

ArrowParsable

public protocol ArrowParsable

This is the protocol that makes your swift Models JSON parsable.

A typical implementation would be the following, preferably in an extension called MyModel+JSON to keep things nice and clean :

   //  MyModel+JSON.swift

   import Arrow

   extension MyModel: ArrowParsable {

       mutating func deserialize(json: JSON) {
           myVariable <-- json["jsonProperty"]
           //...
       }
   }
  • Makes sur your models can be constructed with an empty constructor.

    Declaration

    Swift

    init()
  • The method you declare your json mapping in.

    Declaration

    Swift

    mutating func deserialize(json: JSON)
================================================ FILE: docs/Protocols.html ================================================ Protocols Reference

Arrow Docs (72% documented)

View on GitHub

Protocols

The following protocols are available globally.

  • This is the protocol that makes your swift Models JSON parsable.

    A typical implementation would be the following, preferably in an extension called MyModel+JSON to keep things nice and clean :

       //  MyModel+JSON.swift
    
       import Arrow
    
       extension MyModel: ArrowParsable {
    
           mutating func deserialize(json: JSON) {
               myVariable <-- json["jsonProperty"]
               //...
           }
       }
    
    See more

    Declaration

    Swift

    public protocol ArrowParsable
================================================ FILE: docs/css/highlight.css ================================================ /* Credit to https://gist.github.com/wataru420/2048287 */ .highlight { /* Comment */ /* Error */ /* Keyword */ /* Operator */ /* Comment.Multiline */ /* Comment.Preproc */ /* Comment.Single */ /* Comment.Special */ /* Generic.Deleted */ /* Generic.Deleted.Specific */ /* Generic.Emph */ /* Generic.Error */ /* Generic.Heading */ /* Generic.Inserted */ /* Generic.Inserted.Specific */ /* Generic.Output */ /* Generic.Prompt */ /* Generic.Strong */ /* Generic.Subheading */ /* Generic.Traceback */ /* Keyword.Constant */ /* Keyword.Declaration */ /* Keyword.Pseudo */ /* Keyword.Reserved */ /* Keyword.Type */ /* Literal.Number */ /* Literal.String */ /* Name.Attribute */ /* Name.Builtin */ /* Name.Class */ /* Name.Constant */ /* Name.Entity */ /* Name.Exception */ /* Name.Function */ /* Name.Namespace */ /* Name.Tag */ /* Name.Variable */ /* Operator.Word */ /* Text.Whitespace */ /* Literal.Number.Float */ /* Literal.Number.Hex */ /* Literal.Number.Integer */ /* Literal.Number.Oct */ /* Literal.String.Backtick */ /* Literal.String.Char */ /* Literal.String.Doc */ /* Literal.String.Double */ /* Literal.String.Escape */ /* Literal.String.Heredoc */ /* Literal.String.Interpol */ /* Literal.String.Other */ /* Literal.String.Regex */ /* Literal.String.Single */ /* Literal.String.Symbol */ /* Name.Builtin.Pseudo */ /* Name.Variable.Class */ /* Name.Variable.Global */ /* Name.Variable.Instance */ /* Literal.Number.Integer.Long */ } .highlight .c { color: #999988; font-style: italic; } .highlight .err { color: #a61717; background-color: #e3d2d2; } .highlight .k { color: #000000; font-weight: bold; } .highlight .o { color: #000000; font-weight: bold; } .highlight .cm { color: #999988; font-style: italic; } .highlight .cp { color: #999999; font-weight: bold; } .highlight .c1 { color: #999988; font-style: italic; } .highlight .cs { color: #999999; font-weight: bold; font-style: italic; } .highlight .gd { color: #000000; background-color: #ffdddd; } .highlight .gd .x { color: #000000; background-color: #ffaaaa; } .highlight .ge { color: #000000; font-style: italic; } .highlight .gr { color: #aa0000; } .highlight .gh { color: #999999; } .highlight .gi { color: #000000; background-color: #ddffdd; } .highlight .gi .x { color: #000000; background-color: #aaffaa; } .highlight .go { color: #888888; } .highlight .gp { color: #555555; } .highlight .gs { font-weight: bold; } .highlight .gu { color: #aaaaaa; } .highlight .gt { color: #aa0000; } .highlight .kc { color: #000000; font-weight: bold; } .highlight .kd { color: #000000; font-weight: bold; } .highlight .kp { color: #000000; font-weight: bold; } .highlight .kr { color: #000000; font-weight: bold; } .highlight .kt { color: #445588; } .highlight .m { color: #009999; } .highlight .s { color: #d14; } .highlight .na { color: #008080; } .highlight .nb { color: #0086B3; } .highlight .nc { color: #445588; font-weight: bold; } .highlight .no { color: #008080; } .highlight .ni { color: #800080; } .highlight .ne { color: #990000; font-weight: bold; } .highlight .nf { color: #990000; } .highlight .nn { color: #555555; } .highlight .nt { color: #000080; } .highlight .nv { color: #008080; } .highlight .ow { color: #000000; font-weight: bold; } .highlight .w { color: #bbbbbb; } .highlight .mf { color: #009999; } .highlight .mh { color: #009999; } .highlight .mi { color: #009999; } .highlight .mo { color: #009999; } .highlight .sb { color: #d14; } .highlight .sc { color: #d14; } .highlight .sd { color: #d14; } .highlight .s2 { color: #d14; } .highlight .se { color: #d14; } .highlight .sh { color: #d14; } .highlight .si { color: #d14; } .highlight .sx { color: #d14; } .highlight .sr { color: #009926; } .highlight .s1 { color: #d14; } .highlight .ss { color: #990073; } .highlight .bp { color: #999999; } .highlight .vc { color: #008080; } .highlight .vg { color: #008080; } .highlight .vi { color: #008080; } .highlight .il { color: #009999; } ================================================ FILE: docs/css/jazzy.css ================================================ *, *:before, *:after { box-sizing: inherit; } body { margin: 0; background: #fff; color: #333; font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: .2px; -webkit-font-smoothing: antialiased; box-sizing: border-box; } h1 { font-size: 2rem; font-weight: 700; margin: 1.275em 0 0.6em; } h2 { font-size: 1.75rem; font-weight: 700; margin: 1.275em 0 0.3em; } h3 { font-size: 1.5rem; font-weight: 700; margin: 1em 0 0.3em; } h4 { font-size: 1.25rem; font-weight: 700; margin: 1.275em 0 0.85em; } h5 { font-size: 1rem; font-weight: 700; margin: 1.275em 0 0.85em; } h6 { font-size: 1rem; font-weight: 700; margin: 1.275em 0 0.85em; color: #777; } p { margin: 0 0 1em; } ul, ol { padding: 0 0 0 2em; margin: 0 0 0.85em; } blockquote { margin: 0 0 0.85em; padding: 0 15px; color: #858585; border-left: 4px solid #e5e5e5; } img { max-width: 100%; } a { color: #4183c4; text-decoration: none; } a:hover, a:focus { outline: 0; text-decoration: underline; } table { background: #fff; width: 100%; border-collapse: collapse; border-spacing: 0; overflow: auto; margin: 0 0 0.85em; } tr:nth-child(2n) { background-color: #fbfbfb; } th, td { padding: 6px 13px; border: 1px solid #ddd; } pre { margin: 0 0 1.275em; padding: .85em 1em; overflow: auto; background: #f7f7f7; font-size: .85em; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } code { font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } p > code, li > code { background: #f7f7f7; padding: .2em; } p > code:before, p > code:after, li > code:before, li > code:after { letter-spacing: -.2em; content: "\00a0"; } pre code { padding: 0; white-space: pre; } .content-wrapper { display: flex; flex-direction: column; } @media (min-width: 768px) { .content-wrapper { flex-direction: row; } } .header { display: flex; padding: 8px; font-size: 0.875em; background: #444; color: #999; } .header-col { margin: 0; padding: 0 8px; } .header-col--primary { flex: 1; } .header-link { color: #fff; } .header-icon { padding-right: 6px; vertical-align: -4px; height: 16px; } .breadcrumbs { font-size: 0.875em; padding: 8px 16px; margin: 0; background: #fbfbfb; border-bottom: 1px solid #ddd; } .carat { height: 10px; margin: 0 5px; } .navigation { order: 2; } @media (min-width: 768px) { .navigation { order: 1; width: 25%; max-width: 300px; padding-bottom: 64px; overflow: hidden; word-wrap: normal; background: #fbfbfb; border-right: 1px solid #ddd; } } .nav-groups { list-style-type: none; padding-left: 0; } .nav-group-name { border-bottom: 1px solid #ddd; padding: 8px 0 8px 16px; } .nav-group-name-link { color: #333; } .nav-group-tasks { margin: 8px 0; padding: 0 0 0 8px; } .nav-group-task { font-size: 1em; list-style-type: none; white-space: nowrap; } .nav-group-task-link { color: #808080; } .main-content { order: 1; } @media (min-width: 768px) { .main-content { order: 2; flex: 1; padding-bottom: 60px; } } .section { padding: 0 32px; border-bottom: 1px solid #ddd; } .section-content { max-width: 834px; margin: 0 auto; padding: 16px 0; } .section-name { color: #666; display: block; } .declaration .highlight { overflow-x: initial; padding: 8px 0; margin: 0; background-color: transparent; border: none; } .task-group-section { border-top: 1px solid #ddd; } .task-group { padding-top: 0px; } .task-name-container a[name]:before { content: ""; display: block; } .item-container { padding: 0; } .item { padding-top: 8px; width: 100%; list-style-type: none; } .item a[name]:before { content: ""; display: block; } .item .token { padding-left: 3px; margin-left: 0px; font-size: 1rem; } .item .declaration-note { font-size: .85em; color: #808080; font-style: italic; } .pointer-container { border-bottom: 1px solid #ddd; left: -23px; padding-bottom: 13px; position: relative; width: 110%; } .pointer { left: 21px; top: 7px; display: block; position: absolute; width: 12px; height: 12px; border-left: 1px solid #ddd; border-top: 1px solid #ddd; background: #fff; transform: rotate(45deg); } .height-container { display: none; position: relative; width: 100%; overflow: hidden; } .height-container .section { background: #fff; border: 1px solid #ddd; border-top-width: 0; padding-top: 10px; padding-bottom: 5px; padding: 8px 16px; } .aside, .language { padding: 6px 12px; margin: 12px 0; border-left: 5px solid #dddddd; overflow-y: hidden; } .aside .aside-title, .language .aside-title { font-size: 9px; letter-spacing: 2px; text-transform: uppercase; padding-bottom: 0; margin: 0; color: #aaa; -webkit-user-select: none; } .aside p:last-child, .language p:last-child { margin-bottom: 0; } .language { border-left: 5px solid #cde9f4; } .language .aside-title { color: #4183c4; } .aside-warning { border-left: 5px solid #ff6666; } .aside-warning .aside-title { color: #ff0000; } .graybox { border-collapse: collapse; width: 100%; } .graybox p { margin: 0; word-break: break-word; min-width: 50px; } .graybox td { border: 1px solid #ddd; padding: 5px 25px 5px 10px; vertical-align: middle; } .graybox tr td:first-of-type { text-align: right; padding: 7px; vertical-align: top; word-break: normal; width: 40px; } .slightly-smaller { font-size: 0.9em; } .footer { padding: 8px 16px; background: #444; color: #ddd; font-size: 0.8em; } .footer p { margin: 8px 0; } .footer a { color: #fff; } html.dash .header, html.dash .breadcrumbs, html.dash .navigation { display: none; } html.dash .height-container { display: block; } ================================================ FILE: docs/docsets/Arrow.docset/Contents/Info.plist ================================================ CFBundleIdentifier com.jazzy.arrow CFBundleName Arrow DocSetPlatformFamily arrow isDashDocset dashIndexFilePath index.html isJavaScriptEnabled DashDocSetFamily dashtoc ================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/Classes/Arrow.html ================================================ Arrow Class Reference

Arrow Docs (72% documented)

View on GitHub

Arrow

public class Arrow

This is used to configure NSDate parsing on a global scale.

    Arrow.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
    // or
    Arrow.setUseTimeIntervalSinceReferenceDate(true)

For more fine grained control, use dateFormat on a per field basis :

    createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
  • Sets the defaut dateFormat for parsing NSDates.

    Declaration

    Swift

    public class func setDateFormat(format: String)
  • Sets timeIntervalSinceReferenceDate parsing as the default for NSDates parsing.

    Default is false

    For more information see NSDate(timeIntervalSinceReferenceDate documentation

    Declaration

    Swift

    public class func setUseTimeIntervalSinceReferenceDate(ref: Bool)
================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/Classes/JSON.html ================================================ JSON Class Reference

Arrow Docs (72% documented)

View on GitHub

JSON

public class JSON: AnyObject, CustomDebugStringConvertible

This abstraction helps working with the JSON Format.

It provives a way to access JSON values via subscripting, whether it’s an array or a dictionary.

  • This is the raw data of the JSON

    Declaration

    Swift

    public var data: AnyObject?
  • This date formating strategy that will be used for that JSON section. This should not be set, use dateFormat instead.

    Declaration

    Swift

    public var jsonDateFormat: String?
  • This build a JSON object with raw data.

    Declaration

    Swift

    public init?(_ dic: AnyObject?)
  • Returns

    The array of JSON values. In case of the JSON being a dictionary, this will return nil.

    Declaration

    Swift

    public var collection: [JSON]?

    Return Value

    The array of JSON values. In case of the JSON being a dictionary, this will return nil.

  • This defines the date format to be used for NSDate parsing.

    createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
    

    Returns

    Itself for chaining purposes.

    Declaration

    Swift

    public func dateFormat(format: String) -> Self

    Return Value

    Itself for chaining purposes.

  • This is just for supporting default console logs.

    Declaration

    Swift

    public var debugDescription: String
================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/Classes.html ================================================ Classes Reference

Arrow Docs (72% documented)

View on GitHub

Classes

The following classes are available globally.

  • This abstraction helps working with the JSON Format.

    It provives a way to access JSON values via subscripting, whether it’s an array or a dictionary.

    See more

    Declaration

    Swift

    public class JSON: AnyObject, CustomDebugStringConvertible
  • This is used to configure NSDate parsing on a global scale.

        Arrow.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
        // or
        Arrow.setUseTimeIntervalSinceReferenceDate(true)
    

    For more fine grained control, use dateFormat on a per field basis :

        createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
    
    See more

    Declaration

    Swift

    public class Arrow
================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/Functions/<--(_:_:).html ================================================ <--(_:_:) Function Reference

Arrow Docs (72% documented)

View on GitHub

<--(_:_:)

public func <-- <T>(inout left: [T]?, right: JSON?)

Parses optional arrays of plain swift types.

  • T

    Undocumented

    Declaration

    Swift

    public func <-- <T>(inout left: [T]?, right: JSON?)
================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/Functions.html ================================================ Functions Reference

Arrow Docs (72% documented)

View on GitHub

Functions

The following functions are available globally.

  • Parses default swift types.

    See more

    Declaration

    Swift

    public func <-- <T>(inout left: T, right: JSON?)
  • Parses optional default swift types.

    See more

    Declaration

    Swift

    public func <-- <T>(inout left: T?, right: JSON?)
  • Parses enums.

    See more

    Declaration

    Swift

    public func <-- <T: RawRepresentable>(inout left: T, right: JSON?)
  • Parses optional enums.

    See more

    Declaration

    Swift

    public func <-- <T: RawRepresentable>(inout left: T?, right: JSON?)
  • Parses user defined custom types.

    See more

    Declaration

    Swift

    public func <-- <T: ArrowParsable>(inout left: T, right: JSON?)
  • Parses user defined optional custom types.

    See more

    Declaration

    Swift

    public func <-- <T: ArrowParsable>(inout left: T?, right: JSON?)
  • Parses arrays of user defined custom types.

    See more

    Declaration

    Swift

    public func <-- <T: ArrowParsable>(inout left: [T], right: JSON?)
  • Parses optional arrays of user defined custom types.

    See more

    Declaration

    Swift

    public func <-- <T: ArrowParsable>(inout left: [T]?, right: JSON?)
  • Parses NSDates.

    Declaration

    Swift

    public func <-- (inout left: NSDate, right: JSON?)
  • Parses optional NSDates.

    Declaration

    Swift

    public func <-- (inout left: NSDate?, right: JSON?)
  • Parses NSURLs.

    Declaration

    Swift

    public func <-- (inout left: NSURL, right: JSON?)
  • Parses optional NSURLs.

    Declaration

    Swift

    public func <-- (inout left: NSURL?, right: JSON?)
  • Parses arrays of plain swift types.

    See more

    Declaration

    Swift

    public func <-- <T>(inout left: [T], right: JSON?)
  • Parses optional arrays of plain swift types.

    See more

    Declaration

    Swift

    public func <-- <T>(inout left: [T]?, right: JSON?)
================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/Guides.html ================================================ Guides Reference

Arrow Docs (72% documented)

View on GitHub

================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/Protocols/ArrowParsable.html ================================================ ArrowParsable Protocol Reference

Arrow Docs (72% documented)

View on GitHub

ArrowParsable

public protocol ArrowParsable

This is the protocol that makes your swift Models JSON parsable.

A typical implementation would be the following, preferably in an extension called MyModel+JSON to keep things nice and clean :

   //  MyModel+JSON.swift

   import Arrow

   extension MyModel: ArrowParsable {

       mutating func deserialize(json: JSON) {
           myVariable <-- json["jsonProperty"]
           //...
       }
   }
  • Makes sur your models can be constructed with an empty constructor.

    Declaration

    Swift

    init()
  • The method you declare your json mapping in.

    Declaration

    Swift

    mutating func deserialize(json: JSON)
================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/Protocols.html ================================================ Protocols Reference

Arrow Docs (72% documented)

View on GitHub

Protocols

The following protocols are available globally.

  • This is the protocol that makes your swift Models JSON parsable.

    A typical implementation would be the following, preferably in an extension called MyModel+JSON to keep things nice and clean :

       //  MyModel+JSON.swift
    
       import Arrow
    
       extension MyModel: ArrowParsable {
    
           mutating func deserialize(json: JSON) {
               myVariable <-- json["jsonProperty"]
               //...
           }
       }
    
    See more

    Declaration

    Swift

    public protocol ArrowParsable
================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/css/highlight.css ================================================ /* Credit to https://gist.github.com/wataru420/2048287 */ .highlight { /* Comment */ /* Error */ /* Keyword */ /* Operator */ /* Comment.Multiline */ /* Comment.Preproc */ /* Comment.Single */ /* Comment.Special */ /* Generic.Deleted */ /* Generic.Deleted.Specific */ /* Generic.Emph */ /* Generic.Error */ /* Generic.Heading */ /* Generic.Inserted */ /* Generic.Inserted.Specific */ /* Generic.Output */ /* Generic.Prompt */ /* Generic.Strong */ /* Generic.Subheading */ /* Generic.Traceback */ /* Keyword.Constant */ /* Keyword.Declaration */ /* Keyword.Pseudo */ /* Keyword.Reserved */ /* Keyword.Type */ /* Literal.Number */ /* Literal.String */ /* Name.Attribute */ /* Name.Builtin */ /* Name.Class */ /* Name.Constant */ /* Name.Entity */ /* Name.Exception */ /* Name.Function */ /* Name.Namespace */ /* Name.Tag */ /* Name.Variable */ /* Operator.Word */ /* Text.Whitespace */ /* Literal.Number.Float */ /* Literal.Number.Hex */ /* Literal.Number.Integer */ /* Literal.Number.Oct */ /* Literal.String.Backtick */ /* Literal.String.Char */ /* Literal.String.Doc */ /* Literal.String.Double */ /* Literal.String.Escape */ /* Literal.String.Heredoc */ /* Literal.String.Interpol */ /* Literal.String.Other */ /* Literal.String.Regex */ /* Literal.String.Single */ /* Literal.String.Symbol */ /* Name.Builtin.Pseudo */ /* Name.Variable.Class */ /* Name.Variable.Global */ /* Name.Variable.Instance */ /* Literal.Number.Integer.Long */ } .highlight .c { color: #999988; font-style: italic; } .highlight .err { color: #a61717; background-color: #e3d2d2; } .highlight .k { color: #000000; font-weight: bold; } .highlight .o { color: #000000; font-weight: bold; } .highlight .cm { color: #999988; font-style: italic; } .highlight .cp { color: #999999; font-weight: bold; } .highlight .c1 { color: #999988; font-style: italic; } .highlight .cs { color: #999999; font-weight: bold; font-style: italic; } .highlight .gd { color: #000000; background-color: #ffdddd; } .highlight .gd .x { color: #000000; background-color: #ffaaaa; } .highlight .ge { color: #000000; font-style: italic; } .highlight .gr { color: #aa0000; } .highlight .gh { color: #999999; } .highlight .gi { color: #000000; background-color: #ddffdd; } .highlight .gi .x { color: #000000; background-color: #aaffaa; } .highlight .go { color: #888888; } .highlight .gp { color: #555555; } .highlight .gs { font-weight: bold; } .highlight .gu { color: #aaaaaa; } .highlight .gt { color: #aa0000; } .highlight .kc { color: #000000; font-weight: bold; } .highlight .kd { color: #000000; font-weight: bold; } .highlight .kp { color: #000000; font-weight: bold; } .highlight .kr { color: #000000; font-weight: bold; } .highlight .kt { color: #445588; } .highlight .m { color: #009999; } .highlight .s { color: #d14; } .highlight .na { color: #008080; } .highlight .nb { color: #0086B3; } .highlight .nc { color: #445588; font-weight: bold; } .highlight .no { color: #008080; } .highlight .ni { color: #800080; } .highlight .ne { color: #990000; font-weight: bold; } .highlight .nf { color: #990000; } .highlight .nn { color: #555555; } .highlight .nt { color: #000080; } .highlight .nv { color: #008080; } .highlight .ow { color: #000000; font-weight: bold; } .highlight .w { color: #bbbbbb; } .highlight .mf { color: #009999; } .highlight .mh { color: #009999; } .highlight .mi { color: #009999; } .highlight .mo { color: #009999; } .highlight .sb { color: #d14; } .highlight .sc { color: #d14; } .highlight .sd { color: #d14; } .highlight .s2 { color: #d14; } .highlight .se { color: #d14; } .highlight .sh { color: #d14; } .highlight .si { color: #d14; } .highlight .sx { color: #d14; } .highlight .sr { color: #009926; } .highlight .s1 { color: #d14; } .highlight .ss { color: #990073; } .highlight .bp { color: #999999; } .highlight .vc { color: #008080; } .highlight .vg { color: #008080; } .highlight .vi { color: #008080; } .highlight .il { color: #009999; } ================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/css/jazzy.css ================================================ *, *:before, *:after { box-sizing: inherit; } body { margin: 0; background: #fff; color: #333; font: 16px/1.7 "Helvetica Neue", Helvetica, Arial, sans-serif; letter-spacing: .2px; -webkit-font-smoothing: antialiased; box-sizing: border-box; } h1 { font-size: 2rem; font-weight: 700; margin: 1.275em 0 0.6em; } h2 { font-size: 1.75rem; font-weight: 700; margin: 1.275em 0 0.3em; } h3 { font-size: 1.5rem; font-weight: 700; margin: 1em 0 0.3em; } h4 { font-size: 1.25rem; font-weight: 700; margin: 1.275em 0 0.85em; } h5 { font-size: 1rem; font-weight: 700; margin: 1.275em 0 0.85em; } h6 { font-size: 1rem; font-weight: 700; margin: 1.275em 0 0.85em; color: #777; } p { margin: 0 0 1em; } ul, ol { padding: 0 0 0 2em; margin: 0 0 0.85em; } blockquote { margin: 0 0 0.85em; padding: 0 15px; color: #858585; border-left: 4px solid #e5e5e5; } img { max-width: 100%; } a { color: #4183c4; text-decoration: none; } a:hover, a:focus { outline: 0; text-decoration: underline; } table { background: #fff; width: 100%; border-collapse: collapse; border-spacing: 0; overflow: auto; margin: 0 0 0.85em; } tr:nth-child(2n) { background-color: #fbfbfb; } th, td { padding: 6px 13px; border: 1px solid #ddd; } pre { margin: 0 0 1.275em; padding: .85em 1em; overflow: auto; background: #f7f7f7; font-size: .85em; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } code { font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; } p > code, li > code { background: #f7f7f7; padding: .2em; } p > code:before, p > code:after, li > code:before, li > code:after { letter-spacing: -.2em; content: "\00a0"; } pre code { padding: 0; white-space: pre; } .content-wrapper { display: flex; flex-direction: column; } @media (min-width: 768px) { .content-wrapper { flex-direction: row; } } .header { display: flex; padding: 8px; font-size: 0.875em; background: #444; color: #999; } .header-col { margin: 0; padding: 0 8px; } .header-col--primary { flex: 1; } .header-link { color: #fff; } .header-icon { padding-right: 6px; vertical-align: -4px; height: 16px; } .breadcrumbs { font-size: 0.875em; padding: 8px 16px; margin: 0; background: #fbfbfb; border-bottom: 1px solid #ddd; } .carat { height: 10px; margin: 0 5px; } .navigation { order: 2; } @media (min-width: 768px) { .navigation { order: 1; width: 25%; max-width: 300px; padding-bottom: 64px; overflow: hidden; word-wrap: normal; background: #fbfbfb; border-right: 1px solid #ddd; } } .nav-groups { list-style-type: none; padding-left: 0; } .nav-group-name { border-bottom: 1px solid #ddd; padding: 8px 0 8px 16px; } .nav-group-name-link { color: #333; } .nav-group-tasks { margin: 8px 0; padding: 0 0 0 8px; } .nav-group-task { font-size: 1em; list-style-type: none; white-space: nowrap; } .nav-group-task-link { color: #808080; } .main-content { order: 1; } @media (min-width: 768px) { .main-content { order: 2; flex: 1; padding-bottom: 60px; } } .section { padding: 0 32px; border-bottom: 1px solid #ddd; } .section-content { max-width: 834px; margin: 0 auto; padding: 16px 0; } .section-name { color: #666; display: block; } .declaration .highlight { overflow-x: initial; padding: 8px 0; margin: 0; background-color: transparent; border: none; } .task-group-section { border-top: 1px solid #ddd; } .task-group { padding-top: 0px; } .task-name-container a[name]:before { content: ""; display: block; } .item-container { padding: 0; } .item { padding-top: 8px; width: 100%; list-style-type: none; } .item a[name]:before { content: ""; display: block; } .item .token { padding-left: 3px; margin-left: 0px; font-size: 1rem; } .item .declaration-note { font-size: .85em; color: #808080; font-style: italic; } .pointer-container { border-bottom: 1px solid #ddd; left: -23px; padding-bottom: 13px; position: relative; width: 110%; } .pointer { left: 21px; top: 7px; display: block; position: absolute; width: 12px; height: 12px; border-left: 1px solid #ddd; border-top: 1px solid #ddd; background: #fff; transform: rotate(45deg); } .height-container { display: none; position: relative; width: 100%; overflow: hidden; } .height-container .section { background: #fff; border: 1px solid #ddd; border-top-width: 0; padding-top: 10px; padding-bottom: 5px; padding: 8px 16px; } .aside, .language { padding: 6px 12px; margin: 12px 0; border-left: 5px solid #dddddd; overflow-y: hidden; } .aside .aside-title, .language .aside-title { font-size: 9px; letter-spacing: 2px; text-transform: uppercase; padding-bottom: 0; margin: 0; color: #aaa; -webkit-user-select: none; } .aside p:last-child, .language p:last-child { margin-bottom: 0; } .language { border-left: 5px solid #cde9f4; } .language .aside-title { color: #4183c4; } .aside-warning { border-left: 5px solid #ff6666; } .aside-warning .aside-title { color: #ff0000; } .graybox { border-collapse: collapse; width: 100%; } .graybox p { margin: 0; word-break: break-word; min-width: 50px; } .graybox td { border: 1px solid #ddd; padding: 5px 25px 5px 10px; vertical-align: middle; } .graybox tr td:first-of-type { text-align: right; padding: 7px; vertical-align: top; word-break: normal; width: 40px; } .slightly-smaller { font-size: 0.9em; } .footer { padding: 8px 16px; background: #444; color: #ddd; font-size: 0.8em; } .footer p { margin: 8px 0; } .footer a { color: #fff; } html.dash .header, html.dash .breadcrumbs, html.dash .navigation { display: none; } html.dash .height-container { display: block; } ================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/index.html ================================================ Arrow Reference

Arrow Docs (72% documented)

View on GitHub

Arrow 🏹 - Elegant JSON Parsing in Swift

Language: Swift 2 Platform: iOS 8+ Carthage compatible Cocoapods compatible Build Status codebeat badge License: MIT Release version

Reason - Example - Installation

identifier <-- json["id"]
name <-- json["name"]
stats <-- json["stats"]

Reason

Why

Because parsing JSON in Swift is full of unecessary if lets, obvious casts and nil-checks
There must be a better way

How

By using a simple arrow operator that takes care of the boilerplate code for us.
Json mapping code becomes concise and maintainable ❤️

What

  • [x] Simple & Lightweight (~200lines)
  • [x] Pure Swift
  • [x] Leaves your models clean
  • [x] Implicitly casts JSON values to the right types in your model
  • [x] Automatic NSDate, NSURL, Enum, Custom model Parsing
  • [x] Converts string values to numeric types in your model
  • [x] Does not crash if JSON key is not there, nor returns nil, it simply doesn’t do anything
  • [x] No overly complex obscure functional chaining operator overloading voodoo magic ?==:>>><> 😅

Example

Swift Model

struct Profile {
    var identifier = 0
    var name = ""
    var link:NSURL?
    var weekday:WeekDay = .Monday
    var stats = Stats()
    var phoneNumbers = [PhoneNumber]()
}

JSON File

{
    "id": 15678,
    "name": "John Doe",
    "link": "https://apple.com/steve",
    "weekdayInt" : 3,
    "stats": {
        "numberOfFriends": 163,
        "numberOfFans": 10987
    },
    "phoneNumbers": [{
                     "label": "house",
                     "number": "9809876545"
                     }, {
                     "label": "cell",
                     "number": "0908070656"
                     }, {
                     "label": "work",
                     "number": "0916570656"
    }]
}

Before (Chaos)

var profile = Profile()

// Int
if let id = json["id"] as? Int {
    profile.identifier = id
}  
// String
if let name = json["name"] as? String {
    profile.name = name
}
// NSURL
if let link = json["link"] as? String, url = NSURL(string:link)  {
    profile.link = link
}
// Enum
if let weekdayInt = json["weekdayInt"] as? Int, weekday = WeekDay(rawValue:weekdayInt) {
    profile.weekday = weekday
}
// Custom nested object
if let statsJson = json["stats"] as? AnyObject {
    if let numberOfFans = statsJson["numberOfFans"] as? Int {
        profile.stats.numberOfFans = numberOfFans
    }
    if let numberOfFriends = statsJson["numberOfFriends"] as? Int {
        profile.stats.numberOfFriends = numberOfFriends
    }
}
// Array of custom nested object
if let pns = json["phoneNumbers"] as? [AnyObject] {
    for pn in pns {
        phoneNumbers.append(PhoneNumber(json: pn))
    }
}

After 🎉🎉🎉

extension Profile:ArrowParsable {
    mutating func deserialize(json: JSON) {
        identifier <-- json["id"]
        link <-- json["link"]
        name <-- json["name"]
        weekday <-- json["weekdayInt"]
        stats <- json["stats"]
        phoneNumbers <-- json["phoneNumbers"]
    }
}

Usage

let profile = Profile()
profile.deserialize(json)

Installation

Carthage

github "s4cha/Arrow"

CocoaPods

pod 'Arrow'
use_frameworks!

Manually

Simply Copy and Paste Arrow.swift in your Xcode Project :) https://github.com/s4cha/Arrow/blob/master/Arrow.swift

As A Framework

Grab this repository and build the Framework target on the example project. Then Link against this framework.

How Does That Work

Notice earlier we typed :

stats <-- json["stats"]

That’s because we created and extension Stats+Arrow.swift enabling us to use the Arrow Operator

//  Stats+Arrow.swift

import Foundation

extension Stats:ArrowParsable {
    mutating func deserialize(json: JSON) {
        numberOfFriends <-- json["numberOfFriends"]
        numberOfFans <-- json["numberOfFans"]
    }
}

Flexible you said

  • DO I have to use the <– for my sub models
  • Nope, you could write it like so if you wanted :
stats.numberOfFriends <-- json["stats.numberOfFriends"]
stats.numberOfFans <-- json["stats.numberOfFans"]
  • Hey I don’t want to parse NSDates in every files, do you have something for me?

Sure, just set your date format once and you’re done.

// Configure NSDate Parsing
Arrow.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
Arrow.setUseTimeIntervalSinceReferenceDate(true)

// Dates can be parsed form custom date format or timestamp
let json:JSON = JSON(["date": "2013-06-07T16:38:40+02:00", "timestamp": 392308720])
date1 <-- json["date"]
date2 <-- json["timestamp"]

What if I want a Custom NSDate format for a specific key ? swift createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") Just provide it on a case per case basis ! 🎉

Accessing JSON values

Nested values

value <-- json["nested.nested.nested.nestedValue"]

Object at index

value <-- json[12]

Combine both

value <-- json[1]?["someKey"]?[2]?["something.other"]

Looping on Array

if let collection = json.collection {
    for jsonEntry in collection {
        //Do something
    }
}

Acknoledgments

This wouldn’t exist without YannickDot, Damien-nd and maxkonovalov

Other repos ❤️

Arrow is part of a series of lightweight libraries aiming to make developing iOS Apps a breeze : - Layout : Stevia - Async code : then - JSON WebServices : ws

================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/js/jazzy.js ================================================ window.jazzy = {'docset': false} if (typeof window.dash != 'undefined') { document.documentElement.className += ' dash' window.jazzy.docset = true } if (navigator.userAgent.match(/xcode/i)) { document.documentElement.className += ' xcode' window.jazzy.docset = true } // On doc load, toggle the URL hash discussion if present $(document).ready(function() { if (!window.jazzy.docset) { var linkToHash = $('a[href="' + window.location.hash +'"]'); linkToHash.trigger("click"); } }); // On token click, toggle its discussion and animate token.marginLeft $(".token").click(function(event) { if (window.jazzy.docset) { return; } var link = $(this); var animationDuration = 300; $content = link.parent().parent().next(); $content.slideToggle(animationDuration); // Keeps the document from jumping to the hash. var href = $(this).attr('href'); if (history.pushState) { history.pushState({}, '', href); } else { location.hash = href; } event.preventDefault(); }); ================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/readme.html ================================================ README Reference

Arrow Docs (72% documented)

View on GitHub

Arrow 🏹 - Elegant JSON Parsing in Swift

Language: Swift 2 Platform: iOS 8+ Carthage compatible Cocoapods compatible Build Status codebeat badge License: MIT Release version

Reason - Example - Installation

identifier <-- json["id"]
name <-- json["name"]
stats <-- json["stats"]

Reason

Why

Because parsing JSON in Swift is full of unecessary if lets, obvious casts and nil-checks
There must be a better way

How

By using a simple arrow operator that takes care of the boilerplate code for us.
Json mapping code becomes concise and maintainable ❤️

What

  • [x] Simple & Lightweight (~200lines)
  • [x] Pure Swift
  • [x] Leaves your models clean
  • [x] Implicitly casts JSON values to the right types in your model
  • [x] Automatic NSDate, NSURL, Enum, Custom model Parsing
  • [x] Converts string values to numeric types in your model
  • [x] Does not crash if JSON key is not there, nor returns nil, it simply doesn’t do anything
  • [x] No overly complex obscure functional chaining operator overloading voodoo magic ?==:>>><> 😅

Example

Swift Model

struct Profile {
    var identifier = 0
    var name = ""
    var link:NSURL?
    var weekday:WeekDay = .Monday
    var stats = Stats()
    var phoneNumbers = [PhoneNumber]()
}

JSON File

{
    "id": 15678,
    "name": "John Doe",
    "link": "https://apple.com/steve",
    "weekdayInt" : 3,
    "stats": {
        "numberOfFriends": 163,
        "numberOfFans": 10987
    },
    "phoneNumbers": [{
                     "label": "house",
                     "number": "9809876545"
                     }, {
                     "label": "cell",
                     "number": "0908070656"
                     }, {
                     "label": "work",
                     "number": "0916570656"
    }]
}

Before (Chaos)

var profile = Profile()

// Int
if let id = json["id"] as? Int {
    profile.identifier = id
}  
// String
if let name = json["name"] as? String {
    profile.name = name
}
// NSURL
if let link = json["link"] as? String, url = NSURL(string:link)  {
    profile.link = link
}
// Enum
if let weekdayInt = json["weekdayInt"] as? Int, weekday = WeekDay(rawValue:weekdayInt) {
    profile.weekday = weekday
}
// Custom nested object
if let statsJson = json["stats"] as? AnyObject {
    if let numberOfFans = statsJson["numberOfFans"] as? Int {
        profile.stats.numberOfFans = numberOfFans
    }
    if let numberOfFriends = statsJson["numberOfFriends"] as? Int {
        profile.stats.numberOfFriends = numberOfFriends
    }
}
// Array of custom nested object
if let pns = json["phoneNumbers"] as? [AnyObject] {
    for pn in pns {
        phoneNumbers.append(PhoneNumber(json: pn))
    }
}

After 🎉🎉🎉

extension Profile:ArrowParsable {
    mutating func deserialize(json: JSON) {
        identifier <-- json["id"]
        link <-- json["link"]
        name <-- json["name"]
        weekday <-- json["weekdayInt"]
        stats <- json["stats"]
        phoneNumbers <-- json["phoneNumbers"]
    }
}

Usage

let profile = Profile()
profile.deserialize(json)

Installation

Carthage

github "s4cha/Arrow"

CocoaPods

pod 'Arrow'
use_frameworks!

Manually

Simply Copy and Paste Arrow.swift in your Xcode Project :) https://github.com/s4cha/Arrow/blob/master/Arrow.swift

As A Framework

Grab this repository and build the Framework target on the example project. Then Link against this framework.

How Does That Work

Notice earlier we typed :

stats <-- json["stats"]

That’s because we created and extension Stats+Arrow.swift enabling us to use the Arrow Operator

//  Stats+Arrow.swift

import Foundation

extension Stats:ArrowParsable {
    mutating func deserialize(json: JSON) {
        numberOfFriends <-- json["numberOfFriends"]
        numberOfFans <-- json["numberOfFans"]
    }
}

Flexible you said

  • DO I have to use the <– for my sub models
  • Nope, you could write it like so if you wanted :
stats.numberOfFriends <-- json["stats.numberOfFriends"]
stats.numberOfFans <-- json["stats.numberOfFans"]
  • Hey I don’t want to parse NSDates in every files, do you have something for me?

Sure, just set your date format once and you’re done.

// Configure NSDate Parsing
Arrow.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
Arrow.setUseTimeIntervalSinceReferenceDate(true)

// Dates can be parsed form custom date format or timestamp
let json:JSON = JSON(["date": "2013-06-07T16:38:40+02:00", "timestamp": 392308720])
date1 <-- json["date"]
date2 <-- json["timestamp"]

What if I want a Custom NSDate format for a specific key ? swift createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") Just provide it on a case per case basis ! 🎉

Accessing JSON values

Nested values

value <-- json["nested.nested.nested.nestedValue"]

Object at index

value <-- json[12]

Combine both

value <-- json[1]?["someKey"]?[2]?["something.other"]

Looping on Array

if let collection = json.collection {
    for jsonEntry in collection {
        //Do something
    }
}

Acknoledgments

This wouldn’t exist without YannickDot, Damien-nd and maxkonovalov

Other repos ❤️

Arrow is part of a series of lightweight libraries aiming to make developing iOS Apps a breeze : - Layout : Stevia - Async code : then - JSON WebServices : ws

================================================ FILE: docs/docsets/Arrow.docset/Contents/Resources/Documents/undocumented.json ================================================ {"warnings":[{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":77,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":86,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":91,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":100,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":109,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":118,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":127,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":140,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":181,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":190,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"}],"source_directory":"/Users/sacha/Projects/Arrow"} ================================================ FILE: docs/index.html ================================================ Arrow Reference

Arrow Docs (72% documented)

View on GitHub

Arrow 🏹 - Elegant JSON Parsing in Swift

Language: Swift 2 Platform: iOS 8+ Carthage compatible Cocoapods compatible Build Status codebeat badge License: MIT Release version

Reason - Example - Installation

identifier <-- json["id"]
name <-- json["name"]
stats <-- json["stats"]

Reason

Why

Because parsing JSON in Swift is full of unecessary if lets, obvious casts and nil-checks
There must be a better way

How

By using a simple arrow operator that takes care of the boilerplate code for us.
Json mapping code becomes concise and maintainable ❤️

What

  • [x] Simple & Lightweight (~200lines)
  • [x] Pure Swift
  • [x] Leaves your models clean
  • [x] Implicitly casts JSON values to the right types in your model
  • [x] Automatic NSDate, NSURL, Enum, Custom model Parsing
  • [x] Converts string values to numeric types in your model
  • [x] Does not crash if JSON key is not there, nor returns nil, it simply doesn’t do anything
  • [x] No overly complex obscure functional chaining operator overloading voodoo magic ?==:>>><> 😅

Example

Swift Model

struct Profile {
    var identifier = 0
    var name = ""
    var link:NSURL?
    var weekday:WeekDay = .Monday
    var stats = Stats()
    var phoneNumbers = [PhoneNumber]()
}

JSON File

{
    "id": 15678,
    "name": "John Doe",
    "link": "https://apple.com/steve",
    "weekdayInt" : 3,
    "stats": {
        "numberOfFriends": 163,
        "numberOfFans": 10987
    },
    "phoneNumbers": [{
                     "label": "house",
                     "number": "9809876545"
                     }, {
                     "label": "cell",
                     "number": "0908070656"
                     }, {
                     "label": "work",
                     "number": "0916570656"
    }]
}

Before (Chaos)

var profile = Profile()

// Int
if let id = json["id"] as? Int {
    profile.identifier = id
}  
// String
if let name = json["name"] as? String {
    profile.name = name
}
// NSURL
if let link = json["link"] as? String, url = NSURL(string:link)  {
    profile.link = link
}
// Enum
if let weekdayInt = json["weekdayInt"] as? Int, weekday = WeekDay(rawValue:weekdayInt) {
    profile.weekday = weekday
}
// Custom nested object
if let statsJson = json["stats"] as? AnyObject {
    if let numberOfFans = statsJson["numberOfFans"] as? Int {
        profile.stats.numberOfFans = numberOfFans
    }
    if let numberOfFriends = statsJson["numberOfFriends"] as? Int {
        profile.stats.numberOfFriends = numberOfFriends
    }
}
// Array of custom nested object
if let pns = json["phoneNumbers"] as? [AnyObject] {
    for pn in pns {
        phoneNumbers.append(PhoneNumber(json: pn))
    }
}

After 🎉🎉🎉

extension Profile:ArrowParsable {
    mutating func deserialize(json: JSON) {
        identifier <-- json["id"]
        link <-- json["link"]
        name <-- json["name"]
        weekday <-- json["weekdayInt"]
        stats <- json["stats"]
        phoneNumbers <-- json["phoneNumbers"]
    }
}

Usage

let profile = Profile()
profile.deserialize(json)

Installation

Carthage

github "s4cha/Arrow"

CocoaPods

pod 'Arrow'
use_frameworks!

Manually

Simply Copy and Paste Arrow.swift in your Xcode Project :) https://github.com/s4cha/Arrow/blob/master/Arrow.swift

As A Framework

Grab this repository and build the Framework target on the example project. Then Link against this framework.

How Does That Work

Notice earlier we typed :

stats <-- json["stats"]

That’s because we created and extension Stats+Arrow.swift enabling us to use the Arrow Operator

//  Stats+Arrow.swift

import Foundation

extension Stats:ArrowParsable {
    mutating func deserialize(json: JSON) {
        numberOfFriends <-- json["numberOfFriends"]
        numberOfFans <-- json["numberOfFans"]
    }
}

Flexible you said

  • DO I have to use the <– for my sub models
  • Nope, you could write it like so if you wanted :
stats.numberOfFriends <-- json["stats.numberOfFriends"]
stats.numberOfFans <-- json["stats.numberOfFans"]
  • Hey I don’t want to parse NSDates in every files, do you have something for me?

Sure, just set your date format once and you’re done.

// Configure NSDate Parsing
Arrow.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
Arrow.setUseTimeIntervalSinceReferenceDate(true)

// Dates can be parsed form custom date format or timestamp
let json:JSON = JSON(["date": "2013-06-07T16:38:40+02:00", "timestamp": 392308720])
date1 <-- json["date"]
date2 <-- json["timestamp"]

What if I want a Custom NSDate format for a specific key ? swift createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") Just provide it on a case per case basis ! 🎉

Accessing JSON values

Nested values

value <-- json["nested.nested.nested.nestedValue"]

Object at index

value <-- json[12]

Combine both

value <-- json[1]?["someKey"]?[2]?["something.other"]

Looping on Array

if let collection = json.collection {
    for jsonEntry in collection {
        //Do something
    }
}

Acknoledgments

This wouldn’t exist without YannickDot, Damien-nd and maxkonovalov

Other repos ❤️

Arrow is part of a series of lightweight libraries aiming to make developing iOS Apps a breeze : - Layout : Stevia - Async code : then - JSON WebServices : ws

================================================ FILE: docs/js/jazzy.js ================================================ window.jazzy = {'docset': false} if (typeof window.dash != 'undefined') { document.documentElement.className += ' dash' window.jazzy.docset = true } if (navigator.userAgent.match(/xcode/i)) { document.documentElement.className += ' xcode' window.jazzy.docset = true } // On doc load, toggle the URL hash discussion if present $(document).ready(function() { if (!window.jazzy.docset) { var linkToHash = $('a[href="' + window.location.hash +'"]'); linkToHash.trigger("click"); } }); // On token click, toggle its discussion and animate token.marginLeft $(".token").click(function(event) { if (window.jazzy.docset) { return; } var link = $(this); var animationDuration = 300; $content = link.parent().parent().next(); $content.slideToggle(animationDuration); // Keeps the document from jumping to the hash. var href = $(this).attr('href'); if (history.pushState) { history.pushState({}, '', href); } else { location.hash = href; } event.preventDefault(); }); ================================================ FILE: docs/readme.html ================================================ README Reference

Arrow Docs (72% documented)

View on GitHub

Arrow 🏹 - Elegant JSON Parsing in Swift

Language: Swift 2 Platform: iOS 8+ Carthage compatible Cocoapods compatible Build Status codebeat badge License: MIT Release version

Reason - Example - Installation

identifier <-- json["id"]
name <-- json["name"]
stats <-- json["stats"]

Reason

Why

Because parsing JSON in Swift is full of unecessary if lets, obvious casts and nil-checks
There must be a better way

How

By using a simple arrow operator that takes care of the boilerplate code for us.
Json mapping code becomes concise and maintainable ❤️

What

  • [x] Simple & Lightweight (~200lines)
  • [x] Pure Swift
  • [x] Leaves your models clean
  • [x] Implicitly casts JSON values to the right types in your model
  • [x] Automatic NSDate, NSURL, Enum, Custom model Parsing
  • [x] Converts string values to numeric types in your model
  • [x] Does not crash if JSON key is not there, nor returns nil, it simply doesn’t do anything
  • [x] No overly complex obscure functional chaining operator overloading voodoo magic ?==:>>><> 😅

Example

Swift Model

struct Profile {
    var identifier = 0
    var name = ""
    var link:NSURL?
    var weekday:WeekDay = .Monday
    var stats = Stats()
    var phoneNumbers = [PhoneNumber]()
}

JSON File

{
    "id": 15678,
    "name": "John Doe",
    "link": "https://apple.com/steve",
    "weekdayInt" : 3,
    "stats": {
        "numberOfFriends": 163,
        "numberOfFans": 10987
    },
    "phoneNumbers": [{
                     "label": "house",
                     "number": "9809876545"
                     }, {
                     "label": "cell",
                     "number": "0908070656"
                     }, {
                     "label": "work",
                     "number": "0916570656"
    }]
}

Before (Chaos)

var profile = Profile()

// Int
if let id = json["id"] as? Int {
    profile.identifier = id
}  
// String
if let name = json["name"] as? String {
    profile.name = name
}
// NSURL
if let link = json["link"] as? String, url = NSURL(string:link)  {
    profile.link = link
}
// Enum
if let weekdayInt = json["weekdayInt"] as? Int, weekday = WeekDay(rawValue:weekdayInt) {
    profile.weekday = weekday
}
// Custom nested object
if let statsJson = json["stats"] as? AnyObject {
    if let numberOfFans = statsJson["numberOfFans"] as? Int {
        profile.stats.numberOfFans = numberOfFans
    }
    if let numberOfFriends = statsJson["numberOfFriends"] as? Int {
        profile.stats.numberOfFriends = numberOfFriends
    }
}
// Array of custom nested object
if let pns = json["phoneNumbers"] as? [AnyObject] {
    for pn in pns {
        phoneNumbers.append(PhoneNumber(json: pn))
    }
}

After 🎉🎉🎉

extension Profile:ArrowParsable {
    mutating func deserialize(json: JSON) {
        identifier <-- json["id"]
        link <-- json["link"]
        name <-- json["name"]
        weekday <-- json["weekdayInt"]
        stats <- json["stats"]
        phoneNumbers <-- json["phoneNumbers"]
    }
}

Usage

let profile = Profile()
profile.deserialize(json)

Installation

Carthage

github "s4cha/Arrow"

CocoaPods

pod 'Arrow'
use_frameworks!

Manually

Simply Copy and Paste Arrow.swift in your Xcode Project :) https://github.com/s4cha/Arrow/blob/master/Arrow.swift

As A Framework

Grab this repository and build the Framework target on the example project. Then Link against this framework.

How Does That Work

Notice earlier we typed :

stats <-- json["stats"]

That’s because we created and extension Stats+Arrow.swift enabling us to use the Arrow Operator

//  Stats+Arrow.swift

import Foundation

extension Stats:ArrowParsable {
    mutating func deserialize(json: JSON) {
        numberOfFriends <-- json["numberOfFriends"]
        numberOfFans <-- json["numberOfFans"]
    }
}

Flexible you said

  • DO I have to use the <– for my sub models
  • Nope, you could write it like so if you wanted :
stats.numberOfFriends <-- json["stats.numberOfFriends"]
stats.numberOfFans <-- json["stats.numberOfFans"]
  • Hey I don’t want to parse NSDates in every files, do you have something for me?

Sure, just set your date format once and you’re done.

// Configure NSDate Parsing
Arrow.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ")
Arrow.setUseTimeIntervalSinceReferenceDate(true)

// Dates can be parsed form custom date format or timestamp
let json:JSON = JSON(["date": "2013-06-07T16:38:40+02:00", "timestamp": 392308720])
date1 <-- json["date"]
date2 <-- json["timestamp"]

What if I want a Custom NSDate format for a specific key ? swift createdAt <-- json["created_at"]?.dateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ") Just provide it on a case per case basis ! 🎉

Accessing JSON values

Nested values

value <-- json["nested.nested.nested.nestedValue"]

Object at index

value <-- json[12]

Combine both

value <-- json[1]?["someKey"]?[2]?["something.other"]

Looping on Array

if let collection = json.collection {
    for jsonEntry in collection {
        //Do something
    }
}

Acknoledgments

This wouldn’t exist without YannickDot, Damien-nd and maxkonovalov

Other repos ❤️

Arrow is part of a series of lightweight libraries aiming to make developing iOS Apps a breeze : - Layout : Stevia - Async code : then - JSON WebServices : ws

================================================ FILE: docs/undocumented.json ================================================ {"warnings":[{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":77,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":86,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":91,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":100,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":109,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":118,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":127,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":140,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":181,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"},{"file":"/Users/sacha/Projects/Arrow/Source/Arrow.swift","line":190,"symbol":"T","symbol_kind":"source.lang.swift.decl.generic_type_param","warning":"undocumented"}],"source_directory":"/Users/sacha/Projects/Arrow"}