...
 
Commits (2)
github "ReactiveX/RxSwift" "3.0.0"
github "thoughtbot/Argo" "master"
github "thoughtbot/Curry" "master"
github "RxSugar/RxSugar" "v0.1.1"
github "thoughtbot/Curry" "863ec7b235448ca53e682cf6958df4c30880c78e"
github "thoughtbot/Runes" "v4.0.1"
github "ReactiveX/RxSwift" "3.0.0"
github "thoughtbot/Argo" "0c5242ba09c19c3c2b6bfc41ebd8616b8218722c"
github "RxSugar/RxSugar" "v0.1.1"
github "thoughtbot/Argo" "ceb3775cfb22a3f0bf7f5aac1d71e3a89627ba36"
github "thoughtbot/Curry" "b6bf27ec9d711f607a8c7da9ca69ee9eaa201a22"
github "thoughtbot/Runes" "v4.1.1"
# OS X Finder
.DS_Store
# Xcode per-user config
*.mode1
*.mode1v3
*.mode2v3
*.perspective
*.perspectivev3
*.pbxuser
xcuserdata
*.xccheckout
# Build products
build/
*.o
*.LinkFileList
*.hmap
# Automatic backup files
*~.nib/
*.swp
*~
*.dat
*.dep
# Cocoapods
Pods
# Carthage
Carthage/
# AppCode specific files
.idea/
*.iml
# Swift Package Manager
.build/
Packages/
Argo.framework.zip
*.xcscmblueprint
[submodule "Carthage/Checkouts/Runes"]
path = Carthage/Checkouts/Runes
url = https://github.com/thoughtbot/Runes.git
[submodule "Carthage/Checkouts/Curry"]
path = Carthage/Checkouts/Curry
url = https://github.com/thoughtbot/Curry.git
swift:
enabled: true
config_file: .swiftlint.yml
disabled_rules:
- variable_name
- line_length
set tabstop=2
set shiftwidth=2
let g:xcode_default_scheme = 'Argo-Mac'
/*:
**Note:** For **Argo** to be imported into the Playground, ensure that the **Argo-Mac** *scheme* is selected from the list of schemes.
* * *
*/
import Foundation
import Argo
import Curry
/*:
**Helper function** – load JSON from a file
*/
func JSONFromFile(file: String) -> AnyObject? {
return NSBundle.mainBundle().pathForResource(file, ofType: "json")
.flatMap { NSData(contentsOfFile: $0) }
.flatMap(JSONObjectWithData)
}
func JSONObjectWithData(data: NSData) -> AnyObject? {
do { return try NSJSONSerialization.JSONObjectWithData(data, options: []) }
catch { return .None }
}
/*:
## Decoding JSON into a simple **User** struct
The **User** struct has three properties, one of which is an Optional value.
(The example JSON file can be found in the **Resources** folder.)
*/
struct User {
let id: Int
let name: String
let email: String?
}
extension User: CustomStringConvertible {
var description: String {
return "name: \(name), id: \(id), email: \(email)"
}
}
extension User: Decodable {
static func decode(j: JSON) -> Decoded<User> {
return curry(self.init)
<^> j <| "id"
<*> j <| "name"
<*> j <|? "email"
}
}
/*:
* * *
*/
let user: User? = JSONFromFile("user_with_email").flatMap(decode)
print(user!)
<?xml version="1.0" encoding="UTF-8"?>
<Timeline
version = "3.0">
<TimelineItems>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=9&amp;EndingLineNumber=67&amp;StartingColumnNumber=1&amp;StartingLineNumber=67&amp;Timestamp=455496627.584604"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=11&amp;EndingLineNumber=71&amp;StartingColumnNumber=1&amp;StartingLineNumber=69&amp;Timestamp=455496627.584762"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=12&amp;EndingLineNumber=73&amp;StartingColumnNumber=1&amp;StartingLineNumber=73&amp;Timestamp=455496627.584881"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=9&amp;EndingLineNumber=67&amp;StartingColumnNumber=1&amp;StartingLineNumber=67&amp;Timestamp=455496627.584995"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=11&amp;EndingLineNumber=67&amp;StartingColumnNumber=1&amp;StartingLineNumber=67&amp;Timestamp=455496627.585106"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=12&amp;EndingLineNumber=67&amp;StartingColumnNumber=1&amp;StartingLineNumber=67&amp;Timestamp=455496627.585219"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=10&amp;EndingLineNumber=75&amp;StartingColumnNumber=1&amp;StartingLineNumber=75&amp;Timestamp=455496627.58533"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=23&amp;EndingLineNumber=75&amp;StartingColumnNumber=1&amp;StartingLineNumber=75&amp;Timestamp=455496627.585446"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=20&amp;EndingLineNumber=75&amp;StartingColumnNumber=1&amp;StartingLineNumber=75&amp;Timestamp=455496627.585559"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=1681&amp;EndingColumnNumber=17&amp;EndingLineNumber=75&amp;StartingColumnNumber=1&amp;StartingLineNumber=75&amp;Timestamp=455498186.272651"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=11&amp;EndingLineNumber=67&amp;StartingColumnNumber=1&amp;StartingLineNumber=67&amp;Timestamp=455496627.585782"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=10&amp;EndingLineNumber=75&amp;StartingColumnNumber=1&amp;StartingLineNumber=75&amp;Timestamp=455496627.585894"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=23&amp;EndingLineNumber=75&amp;StartingColumnNumber=1&amp;StartingLineNumber=75&amp;Timestamp=455496627.586006"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=17&amp;EndingLineNumber=75&amp;StartingColumnNumber=1&amp;StartingLineNumber=75&amp;Timestamp=455496627.586117"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=20&amp;EndingLineNumber=75&amp;StartingColumnNumber=1&amp;StartingLineNumber=75&amp;Timestamp=455496627.58623"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=5&amp;EndingLineNumber=66&amp;StartingColumnNumber=1&amp;StartingLineNumber=66&amp;Timestamp=455496627.586343"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=14&amp;EndingLineNumber=66&amp;StartingColumnNumber=1&amp;StartingLineNumber=66&amp;Timestamp=455496627.586456"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=14&amp;EndingLineNumber=66&amp;StartingColumnNumber=1&amp;StartingLineNumber=66&amp;Timestamp=455496627.586569"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=14&amp;EndingLineNumber=56&amp;StartingColumnNumber=1&amp;StartingLineNumber=56&amp;Timestamp=455496627.586678"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=14&amp;EndingLineNumber=58&amp;StartingColumnNumber=1&amp;StartingLineNumber=58&amp;Timestamp=455496627.586787"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=14&amp;EndingLineNumber=58&amp;StartingColumnNumber=1&amp;StartingLineNumber=58&amp;Timestamp=455496627.586894"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=0&amp;CharacterRangeLoc=0&amp;EndingColumnNumber=14&amp;EndingLineNumber=0&amp;StartingColumnNumber=1&amp;StartingLineNumber=0&amp;Timestamp=455491959.867682"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=9&amp;CharacterRangeLoc=1260&amp;EndingColumnNumber=15&amp;EndingLineNumber=56&amp;StartingColumnNumber=1&amp;StartingLineNumber=56&amp;Timestamp=455498186.273935"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
<LoggerValueHistoryTimelineItem
documentLocation = "#CharacterRangeLen=8&amp;CharacterRangeLoc=1260&amp;EndingColumnNumber=15&amp;EndingLineNumber=55&amp;StartingColumnNumber=1&amp;StartingLineNumber=55&amp;Timestamp=455498186.274038"
selectedRepresentationIndex = "0"
shouldTrackSuperviewWidth = "NO">
</LoggerValueHistoryTimelineItem>
</TimelineItems>
</Timeline>
/*:
**Note:** For **Argo** to be imported into the Playground, ensure that the **Argo-Mac** *scheme* is selected from the list of schemes.
* * *
*/
import Foundation
import Argo
import Curry
/*:
**Helper function** – load JSON from a file
*/
func JSONFromFile(file: String) -> AnyObject? {
return NSBundle.mainBundle().pathForResource(file, ofType: "json")
.flatMap { NSData(contentsOfFile: $0) }
.flatMap(JSONObjectWithData)
}
func JSONObjectWithData(data: NSData) -> AnyObject? {
do { return try NSJSONSerialization.JSONObjectWithData(data, options: []) }
catch { return .None }
}
/*:
During JSON decoding, a **String** representation of a date needs to be converted to a **NSDate**.
To achieve this, a **NSDateFormatter** and a helper function will be used.
*/
let jsonDateFormatter: NSDateFormatter = {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssz"
return dateFormatter
}()
let toNSDate: String -> Decoded<NSDate> = {
.fromOptional(jsonDateFormatter.dateFromString($0))
}
/*:
## Decoding selected entries from the iTunes store JSON format
An example JSON file (**tropos.json**) can be found in the **resources** folder.
*/
struct App {
let name: String
let formattedPrice: String
let averageUserRating: Float?
let releaseDate: NSDate
}
extension App: CustomStringConvertible {
var description: String {
return "name: \(name)\nprice: \(formattedPrice), rating: \(averageUserRating), released: \(releaseDate)"
}
}
extension App: Decodable {
static func decode(j: JSON) -> Decoded<App> {
return curry(self.init)
<^> j <| "trackName"
<*> j <| "formattedPrice"
<*> j <|? "averageUserRating"
<*> (j <| "releaseDate" >>- toNSDate)
}
}
/*:
* * *
*/
let app: App? = (JSONFromFile("tropos")?["results"].flatMap(decode))?.first
print(app!)
{
"resultCount":1,
"results":[
{
"isGameCenterEnabled":false,
"screenshotUrls":[
"http://a5.mzstatic.com/us/r30/Purple5/v4/8b/3e/bd/8b3ebd2c-9dfe-1ce5-cdf5-8c89d854e375/screen322x572.jpeg",
"http://a1.mzstatic.com/us/r30/Purple5/v4/e6/4f/36/e64f369d-d453-f007-dd15-361d21641116/screen322x572.jpeg",
"http://a1.mzstatic.com/us/r30/Purple1/v4/74/77/cd/7477cd91-a094-3c22-fff1-cf75e7474dad/screen322x572.jpeg"
],
"ipadScreenshotUrls":[
],
"artworkUrl60":"http://is2.mzstatic.com/image/pf/us/r30/Purple3/v4/1b/b7/31/1bb731e3-a35a-9eaa-b13e-c536e639851c/AppIcon60x60_U00402x.png",
"artworkUrl512":"http://is4.mzstatic.com/image/pf/us/r30/Purple3/v4/e2/c5/42/e2c542e5-664e-36df-2285-490b7c16941e/mzl.ivsinquu.png",
"artistViewUrl":"https://itunes.apple.com/us/artist/thoughtbot-inc./id337354066?uo=4",
"kind":"software",
"features":[
],
"supportedDevices":[
"iPhone4S",
"iPadThirdGen4G",
"iPodTouchFifthGen",
"iPhone6",
"iPadMini4G",
"iPhone6Plus",
"iPadThirdGen",
"iPadFourthGen",
"iPad23G",
"iPad2Wifi",
"iPadFourthGen4G",
"iPadMini",
"iPhone5c",
"iPhone5",
"iPhone5s"
],
"advisories":[
],
"averageUserRatingForCurrentVersion":5.0,
"artworkUrl100":"http://is4.mzstatic.com/image/pf/us/r30/Purple3/v4/e2/c5/42/e2c542e5-664e-36df-2285-490b7c16941e/mzl.ivsinquu.png",
"trackCensoredName":"Tropos – Weather and forecasts for humans",
"languageCodesISO2A":[
"EN"
],
"fileSizeBytes":"2945254",
"sellerUrl":"http://troposweather.com",
"contentAdvisoryRating":"4+",
"userRatingCountForCurrentVersion":1,
"trackViewUrl":"https://itunes.apple.com/us/app/tropos-weather-forecasts-for/id955209376?mt=8&uo=4",
"trackContentRating":"4+",
"currency":"USD",
"wrapperType":"software",
"version":"1.0.1",
"artistId":337354066,
"artistName":"thoughtbot, inc.",
"genres":[
"Weather"
],
"price":0.99,
"description":"Weather and forecasts for humans. Information you can act on.\n\nMost weather apps throw a lot of information at you but that doesn't answer the question of \"What does it feel like outside?\". Tropos answers this by relating the current conditions to conditions the same time yesterday.\n\nFeatures:\n• Current conditions presented in plain language. For example, “It is warmer tonight than last night.”\n• Intelligent use of color to convey how it feels in your area compared to yesterday.\n• Simple, 3-day forecast that allows you to see how the next few days are trending.\n\nthoughtbot is dedicated to building the best possible application. Your feedback is invaluable to us. Get in touch at help@troposweather.com.",
"bundleId":"com.thoughtbot.carlweathers",
"genreIds":[
"6001"
],
"releaseDate":"2015-03-25T18:34:40Z",
"sellerName":"thoughtbot, inc.",
"trackId":955209376,
"trackName":"Tropos – Weather and forecasts for humans",
"primaryGenreName":"Weather",
"primaryGenreId":6001,
"releaseNotes":"- Fixed an issue where the app could crash if you're going buck-wild messing around with the pull to refresh control.\n- Improved the relative weather description in cases where it's crazy hot or ridiculously cold outside. Tropos will no longer tell you that it's hotter today when it's 10°F outside. (Sorry, Boston)\n- Fixed an issue where the high and low temperatures could be lower or higher than the current temperature, respectively. That's just silly.\n\nFinally, we want to thank everyone for supporting Tropos!",
"minimumOsVersion":"8.0",
"formattedPrice":"$0.99",
"averageUserRating":4.5,
"userRatingCount":10
}
]
}
<?xml version="1.0" encoding="UTF-8"?>
<Timeline
version = "3.0">
<TimelineItems>
</TimelineItems>
</Timeline>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='6.0' target-platform='osx' requires-full-environment='true' display-mode='rendered'/>
\ No newline at end of file
Pod::Spec.new do |spec|
spec.name = 'Argo'
spec.version = '4.1.0'
spec.summary = 'Functional JSON parsing library for Swift.'
spec.homepage = 'https://github.com/thoughtbot/Argo'
spec.license = { :type => 'MIT', :file => 'LICENSE' }
spec.author = {
'Gordon Fontenot' => 'gordon@thoughtbot.com',
'Tony DiPasquale' => 'tony@thoughtbot.com',
'thoughtbot' => nil,
}
spec.social_media_url = 'http://twitter.com/thoughtbot'
spec.source = { :git => 'https://github.com/thoughtbot/Argo.git', :tag => "v#{spec.version}" }
spec.source_files = 'Sources/**/*.{h,swift}'
spec.dependency 'Runes', '>= 4.0.0'
spec.requires_arc = true
spec.compiler_flags = '-whole-module-optimization'
spec.ios.deployment_target = '8.0'
spec.osx.deployment_target = '10.9'
spec.watchos.deployment_target = '2.0'
spec.tvos.deployment_target = '9.0'
end
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Argo.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Argo.xcodeproj">
</FileRef>
<FileRef
location = "group:Carthage/Checkouts/Runes/Runes.xcodeproj">
</FileRef>
<FileRef
location = "group:Carthage/Checkouts/Curry/Curry.xcodeproj">
</FileRef>
</Workspace>
import Runes
// pure merge for Dictionaries
func + <T, U>(lhs: [T: U], rhs: [T: U]) -> [T: U] {
var merged = lhs
for (key, val) in rhs {
merged[key] = val
}
return merged
}
extension Dictionary {
func map<T>(_ f: (Value) -> T) -> [Key: T] {
var accum = Dictionary<Key, T>(minimumCapacity: self.count)
for (key, value) in self {
accum[key] = f(value)
}
return accum
}
}
func <^> <T, U, V>(f: (T) -> U, x: [V: T]) -> [V: U] {
return x.map(f)
}
import Foundation
extension NSNumber {
var isBool: Bool {
return CFBooleanGetTypeID() == CFGetTypeID(self)
}
}
/**
Default implementation of `Decodable` for `RawRepresentable` types using
`String` as the raw value.
*/
public extension Decodable where Self.DecodedType == Self, Self: RawRepresentable, Self.RawValue == String {
static func decode(_ json: JSON) -> Decoded<Self> {
switch json {
case let .string(s): return self.init(rawValue: s).map(pure) ?? .typeMismatch(expected: "rawValue for \(self)", actual: json)
default: return .typeMismatch(expected: "String", actual: json)
}
}
}
/**
Default implementation of `Decodable` for `RawRepresentable` types using
`Int` as the raw value.
*/
public extension Decodable where Self.DecodedType == Self, Self: RawRepresentable, Self.RawValue == Int {
static func decode(_ json: JSON) -> Decoded<Self> {
switch json {
case let .number(n): return self.init(rawValue: n as Int).map(pure) ?? .typeMismatch(expected: "rawValue for \(self)", actual: json)
default: return .typeMismatch(expected: "Int", actual: json)
}
}
}
/**
Create a new array of unwrapped `.Success` values, filtering out `.Failure`s.
This will iterate through the array of `Decoded<T>` elements and safely
unwrap the values.
If the element is `.Success(T)`, it will unwrap the value and add it into the
array.
If the element is `.Failure`, it will not be added to the new array.
- parameter xs: An array of `Decoded<T>` values
- returns: An array of unwrapped values of type `T`
*/
public func catDecoded<T>(_ xs: [Decoded<T>]) -> [T] {
var accum: [T] = []
accum.reserveCapacity(xs.count)
for x in xs {
switch x {
case let .success(value): accum.append(value)
case .failure: continue
}
}
return accum
}
/**
Create a new dictionary of unwrapped `.Success` values, filtering out
`.Failure`s.
This will iterate through the dictionary of `Decoded<T>` elements and safely
unwrap the values.
If the element is `.Success(T)`, it will unwrap the value and assign it to
the existing key in the new dictionary.
If the element is `.Failure`, it will not be added to the new dictionary.
- parameter xs: A dictionary of `Decoded<T>` values assigned to `String` keys
- returns: A dictionary of unwrapped values of type `T` assigned to `String` keys
*/
public func catDecoded<T>(_ xs: [String: Decoded<T>]) -> [String: T] {
var accum = Dictionary<String, T>(minimumCapacity: xs.count)
for (key, x) in xs {
switch x {
case let .success(value): accum[key] = value
case .failure: continue
}
}
return accum
}
func curry<T, U, V>(f : (T, U) -> V) -> T -> U -> V {
return { x in { y in f(x, y) } }
}
/**
Attempt to transform `Any` into a `Decodable` value.
This function takes `Any` (usually the output from
`NSJSONSerialization`) and attempts to transform it into a `Decodable` value.
This works based on the type you ask for.
For example, the following code attempts to decode to `Decoded<String>`,
because that's what we have explicitly stated is the return type:
```
do {
let object = try NSJSONSerialization.JSONObjectWithData(data, options: nil)
let str: Decoded<String> = decode(object)
} catch {
// handle error
}
```
- parameter object: The `Any` instance to attempt to decode
- returns: A `Decoded<T>` value where `T` is `Decodable`
*/
public func decode<T: Decodable>(_ object: Any) -> Decoded<T> where T == T.DecodedType {
return T.decode(JSON(object))
}
/**
Attempt to transform `Any` into an `Array` of `Decodable` values.
This function takes `Any` (usually the output from
`NSJSONSerialization`) and attempts to transform it into an `Array` of
`Decodable` values. This works based on the type you ask for.
For example, the following code attempts to decode to
`Decoded<[String]>`, because that's what we have explicitly stated is
the return type:
```
do {
let object = try NSJSONSerialization.JSONObjectWithData(data, options: nil)
let str: Decoded<[String]> = decode(object)
} catch {
// handle error
}
```
- parameter object: The `Any` instance to attempt to decode
- returns: A `Decoded<[T]>` value where `T` is `Decodable`
*/
public func decode<T: Decodable>(_ object: Any) -> Decoded<[T]> where T == T.DecodedType {
return Array<T>.decode(JSON(object))
}
/**
Attempt to transform `Any` into a `Decodable` value and return an `Optional`.
This function takes `Any` (usually the output from
`NSJSONSerialization`) and attempts to transform it into a `Decodable` value,
returning an `Optional`. This works based on the type you ask for.
For example, the following code attempts to decode to `Optional<String>`,
because that's what we have explicitly stated is the return type:
```
do {
let object = try NSJSONSerialization.JSONObjectWithData(data, options: nil)
let str: String? = decode(object)
} catch {
// handle error
}
```
- parameter object: The `Any` instance to attempt to decode
- returns: An `Optional<T>` value where `T` is `Decodable`
*/
public func decode<T: Decodable>(_ object: Any) -> T? where T == T.DecodedType {
return decode(object).value
}
/**
Attempt to transform `Any` into an `Array` of `Decodable` values and
return an `Optional`.
This function takes `Any` (usually the output from
`NSJSONSerialization`) and attempts to transform it into an `Array` of
`Decodable` values, returning an `Optional`. This works based on the type you
ask for.
For example, the following code attempts to decode to
`Optional<[String]>`, because that's what we have explicitly stated is
the return type:
```
do {
let object = try NSJSONSerialization.JSONObjectWithData(data, options: nil)
let str: [String]? = decode(object)
} catch {
// handle error
}
```
- parameter object: The `Any` instance to attempt to decode
- returns: An `Optional<[T]>` value where `T` is `Decodable`
*/
public func decode<T: Decodable>(_ object: Any) -> [T]? where T == T.DecodedType {
return decode(object).value
}
/**
Attempt to transform `Any` into a `Decodable` value using a specified
root key.
This function attempts to extract the embedded `JSON` object from the
dictionary at the specified key and transform it into a `Decodable` value.
This works based on the type you ask for.
For example, the following code attempts to decode to `Decoded<String>`,
because that's what we have explicitly stated is the return type:
```
do {
let dict = try NSJSONSerialization.JSONObjectWithData(data, options: nil) as? [String: Any] ?? [:]
let str: Decoded<String> = decode(dict, rootKey: "value")
} catch {
// handle error
}
```
- parameter dict: The dictionary containing the `Any` instance to
attempt to decode
- parameter rootKey: The root key that contains the object to decode
- returns: A `Decoded<T>` value where `T` is `Decodable`
*/
public func decode<T: Decodable>(_ dict: [String: Any], rootKey: String) -> Decoded<T> where T == T.DecodedType {
return JSON(dict as Any) <| rootKey
}
/**
Attempt to transform `Any` into an `Array` of `Decodable` value using a
specified root key.
This function attempts to extract the embedded `JSON` object from the
dictionary at the specified key and transform it into an `Array` of
`Decodable` values. This works based on the type you ask for.
For example, the following code attempts to decode to `Decoded<[String]>`,
because that's what we have explicitly stated is the return type:
```
do {
let dict = try NSJSONSerialization.JSONObjectWithData(data, options: nil) as? [String: Any] ?? [:]
let str: Decoded<[String]> = decode(dict, rootKey: "value")
} catch {
// handle error
}
```
- parameter dict: The dictionary containing the `Any` instance to
attempt to decode
- parameter rootKey: The root key that contains the object to decode
- returns: A `Decoded<[T]>` value where `T` is `Decodable`
*/
public func decode<T: Decodable>(_ dict: [String: Any], rootKey: String) -> Decoded<[T]> where T == T.DecodedType {
return JSON(dict as Any) <|| rootKey
}
/**
Attempt to transform `Any` into a `Decodable` value using a specified
root key and return an `Optional`.
This function attempts to extract the embedded `JSON` object from the
dictionary at the specified key and transform it into a `Decodable` value,
returning an `Optional`. This works based on the type you ask for.
For example, the following code attempts to decode to `Optional<String>`,
because that's what we have explicitly stated is the return type:
```
do {
let dict = try NSJSONSerialization.JSONObjectWithData(data, options: nil) as? [String: Any] ?? [:]
let str: String? = decode(dict, rootKey: "value")
} catch {
// handle error
}
```
- parameter dict: The dictionary containing the `Any` instance to
attempt to decode
- parameter rootKey: The root key that contains the object to decode
- returns: A `Decoded<T>` value where `T` is `Decodable`
*/
public func decode<T: Decodable>(_ dict: [String: Any], rootKey: String) -> T? where T == T.DecodedType {
return decode(dict, rootKey: rootKey).value
}
/**
Attempt to transform `Any` into an `Array` of `Decodable` value using a
specified root key and return an `Optional`
This function attempts to extract the embedded `JSON` object from the
dictionary at the specified key and transform it into an `Array` of
`Decodable` values, returning an `Optional`. This works based on the type you
ask for.
For example, the following code attempts to decode to `Optional<[String]>`,
because that's what we have explicitly stated is the return type:
```
do {
let dict = try NSJSONSerialization.JSONObjectWithData(data, options: nil) as? [String: Any] ?? [:]
let str: [String]? = decode(dict, rootKey: "value")
} catch {
// handle error
}
```
- parameter dict: The dictionary containing the `Any` instance to
attempt to decode
- parameter rootKey: The root key that contains the object to decode
- returns: A `Decoded<[T]>` value where `T` is `Decodable`
*/
public func decode<T: Decodable>(_ dict: [String: Any], rootKey: String) -> [T]? where T == T.DecodedType {
return decode(dict, rootKey: rootKey).value
}
import Runes
/**
Reduce a sequence with a combinator that returns a `Decoded` type, flattening
the result.
This function is a helper function to make it easier to deal with combinators
that return `Decoded` types without ending up with multiple levels of nested
`Decoded` values.
For example, it can be used to traverse a JSON structure with an array of
keys. See the implementations of `<|` and `<||` that take an array of keys for
a real-world example of this use case.
- parameter sequence: Any `SequenceType` of values
- parameter initial: The initial value for the accumulator
- parameter combine: The combinator, which returns a `Decoded` type
- returns: The result of iterating the combinator over every element of the
sequence and flattening the result
*/
public func flatReduce<S: Sequence, U>(_ sequence: S, initial: U, combine: (U, S.Iterator.Element) -> Decoded<U>) -> Decoded<U> {
return sequence.reduce(pure(initial)) { accum, x in
accum >>- { combine($0, x) }
}
}
/**
Convert an `Array` of `Decoded<T>` values to a `Decoded` `Array` of unwrapped
`T` values.
This performs an all-or-nothing transformation on the array. If every element
is `.Success`, then this function will return `.Success` along with the array
of unwrapped `T` values.
However, if _any_ of the elements are `.Failure`, this function will also
return `.Failure`, and no array will be returned.
- parameter xs: An `Array` of `Decoded<T>` values
- returns: A `Decoded` `Array` of unwrapped `T` values
*/
public func sequence<T>(_ xs: [Decoded<T>]) -> Decoded<[T]> {
var accum: [T] = []
accum.reserveCapacity(xs.count)
for x in xs {
switch x {
case let .success(value): accum.append(value)
case let .failure(error): return .failure(error)
}
}
return pure(accum)
}
/**
Convert a `Dictionary` with `Decoded<T>` values to a `Decoded` `Dictionary`
with unwrapped `T` values.
This performs an all-or-nothing transformation on the dictionary. If every
key is associated with a `.Success` value, then this function will return
`.Success` along with the dictionary of unwrapped `T` values associated with
their original keys.
However, if _any_ of the keys are associated with a `.Failure` value, this
function will also return `.Failure`, and no dictionary will be returned.
- parameter xs: A `Dictionary` of arbitrary keys and `Decoded<T>` values
- returns: A `Decoded` `Dictionary` of unwrapped `T` values assigned to their
original keys
*/
public func sequence<Key, Value>(_ xs: [Key: Decoded<Value>]) -> Decoded<[Key: Value]> {
var accum = Dictionary<Key, Value>(minimumCapacity: xs.count)
for (key, x) in xs {
switch x {
case let .success(value): accum[key] = value
case let .failure(error): return .failure(error)
}
}
return pure(accum)
}
import Runes
precedencegroup ArgoDecodePrecedence {
associativity: left
higherThan: RunesApplicativeSequencePrecedence
lowerThan: NilCoalescingPrecedence
}
infix operator <| : ArgoDecodePrecedence
infix operator <|? : ArgoDecodePrecedence
infix operator <|| : ArgoDecodePrecedence
infix operator <||? : ArgoDecodePrecedence
import Runes
/**
Attempt to decode a value at the specified key into the requested type.
This operator is used to decode a mandatory value from the `JSON`. If the
decoding fails for any reason, this will result in a `.Failure` being
returned.
- parameter json: The `JSON` object containing the key
- parameter key: The key for the object to decode
- returns: A `Decoded` value representing the success or failure of the
decode operation
*/
public func <| <A: Decodable>(json: JSON, key: String) -> Decoded<A> where A == A.DecodedType {
return json <| [key]
}
/**
Attempt to decode an optional value at the specified key into the requested
type.
This operator is used to decode an optional value from the `JSON`. If the key
isn't present in the `JSON`, this will still return `.Success`. However, if
the key exists but the object assigned to that key is unable to be decoded
into the requested type, this will return `.Failure`.
- parameter json: The `JSON` object containing the key
- parameter key: The key for the object to decode
- returns: A `Decoded` optional value representing the success or failure of
the decode operation
*/
public func <|? <A: Decodable>(json: JSON, key: String) -> Decoded<A?> where A == A.DecodedType {
return .optional(json <| [key])
}
/**
Attempt to decode a value at the specified key path into the requested type.
This operator is used to decode a mandatory value from the `JSON`. If the
decoding fails for any reason, this will result in a `.Failure` being
returned.
- parameter json: The `JSON` object containing the key
- parameter keys: The key path for the object to decode, represented by an
array of strings
- returns: A `Decoded` value representing the success or failure of the
decode operation
*/
public func <| <A: Decodable>(json: JSON, keys: [String]) -> Decoded<A> where A == A.DecodedType {
return flatReduce(keys, initial: json, combine: decodedJSON) >>- A.decode
}
/**
Attempt to decode an optional value at the specified key path into the
requested type.
This operator is used to decode an optional value from the `JSON`. If any of
the keys in the key path aren't present in the `JSON`, this will still return
`.Success`. However, if the key path exists but the object assigned to the
final key is unable to be decoded into the requested type, this will return
`.Failure`.
- parameter json: The `JSON` object containing the key
- parameter keys: The key path for the object to decode, represented by an
array of strings
- returns: A `Decoded` optional value representing the success or failure of
the decode operation
*/
public func <|? <A: Decodable>(json: JSON, keys: [String]) -> Decoded<A?> where A == A.DecodedType {
return .optional(json <| keys)
}
/**
Attempt to decode an array of values at the specified key into the requested
type.
This operator is used to decode a mandatory array of values from the `JSON`.
If the decoding of any of the objects fail for any reason, this will result
in a `.Failure` being returned.
- parameter json: The `JSON` object containing the key
- parameter key: The key for the array of objects to decode
- returns: A `Decoded` array of values representing the success or failure of
the decode operation
*/
public func <|| <A: Decodable>(json: JSON, key: String) -> Decoded<[A]> where A == A.DecodedType {
return json <|| [key]
}
/**
Attempt to decode an optional array of values at the specified key into the
requested type.
This operator is used to decode an optional array of values from the `JSON`.
If the key isn't present in the `JSON`, this will still return `.Success`.
However, if the key exists but the objects assigned to that key are unable to
be decoded into the requested type, this will return `.Failure`.
- parameter json: The `JSON` object containing the key
- parameter key: The key for the object to decode
- returns: A `Decoded` optional array of values representing the success or
failure of the decode operation
*/
public func <||? <A: Decodable>(json: JSON, key: String) -> Decoded<[A]?> where A == A.DecodedType {
return .optional(json <|| [key])
}
/**
Attempt to decode an array of values at the specified key path into the
requested type.
This operator is used to decode a mandatory array of values from the `JSON`.
If the decoding fails for any reason, this will result in a `.Failure` being
returned.
- parameter json: The `JSON` object containing the key
- parameter keys: The key path for the object to decode, represented by an
array of strings
- returns: A `Decoded` array of values representing the success or failure of
the decode operation
*/
public func <|| <A: Decodable>(json: JSON, keys: [String]) -> Decoded<[A]> where A == A.DecodedType {
return flatReduce(keys, initial: json, combine: decodedJSON) >>- Array<A>.decode
}
/**
Attempt to decode an optional array of values at the specified key path into
the requested type.
This operator is used to decode an optional array of values from the `JSON`.
If any of the keys in the key path aren't present in the `JSON`, this will
still return `.Success`. However, if the key path exists but the objects
assigned to the final key are unable to be decoded into the requested type,
this will return `.Failure`.
- parameter json: The `JSON` object containing the key
- parameter keys: The key path for the object to decode, represented by an
array of strings
- returns: A `Decoded` optional array of values representing the success or
failure of the decode operation
*/
public func <||? <A: Decodable>(json: JSON, keys: [String]) -> Decoded<[A]?> where A == A.DecodedType {
return .optional(json <|| keys)
}
// MARK: Values
// Pull value from JSON
public func <| <A where A: Decodable, A == A.DecodedType>(json: JSON, key: String) -> Decoded<A> {
return json <| [key]
}
// Pull optional value from JSON
public func <|? <A where A: Decodable, A == A.DecodedType>(json: JSON, key: String) -> Decoded<A?> {
return .optional(json <| [key])
}
// Pull embedded value from JSON
public func <| <A where A: Decodable, A == A.DecodedType>(json: JSON, keys: [String]) -> Decoded<A> {
return flatReduce(keys, initial: json, combine: decodedJSON) >>- A.decode
}
// Pull embedded optional value from JSON
public func <|? <A where A: Decodable, A == A.DecodedType>(json: JSON, keys: [String]) -> Decoded<A?> {
return .optional(json <| keys)
}
// MARK: Arrays
// Pull array from JSON
public func <|| <A where A: Decodable, A == A.DecodedType>(json: JSON, key: String) -> Decoded<[A]> {
return json <|| [key]
}
// Pull optional array from JSON
public func <||? <A where A: Decodable, A == A.DecodedType>(json: JSON, key: String) -> Decoded<[A]?> {
return .optional(json <|| [key])
}
// Pull embedded array from JSON
public func <|| <A where A: Decodable, A == A.DecodedType>(json: JSON, keys: [String]) -> Decoded<[A]> {
return flatReduce(keys, initial: json, combine: decodedJSON) >>- Array<A>.decode
}
// Pull embedded optional array from JSON
public func <||? <A where A: Decodable, A == A.DecodedType>(json: JSON, keys: [String]) -> Decoded<[A]?> {
return .optional(json <|| keys)
}
// MARK: Values
// Pull value from JSON
public func <| <T where T: Decodable, T == T.DecodedType>(json: JSON, key: String) -> T? {
return json <| [key]
}
// Pull optional value from JSON
public func <|? <T where T: Decodable, T == T.DecodedType>(json: JSON, key: String) -> T?? {
return json <| [key]
}
// Pull embedded value from JSON
public func <| <T where T: Decodable, T == T.DecodedType>(json: JSON, keys: [String]) -> T? {
return (json <| keys).value
}
// Pull embedded optional value from JSON
public func <|? <T where T: Decodable, T == T.DecodedType>(json: JSON, keys: [String]) -> T?? {
return (json <|? keys).value
}
// MARK: Arrays
// Pull array from JSON
public func <|| <T where T: Decodable, T == T.DecodedType>(json: JSON, key: String) -> [T]? {
return json <|| [key]
}
// Pull optional array from JSON
public func <||? <T where T: Decodable, T == T.DecodedType>(json: JSON, key: String) -> [T]?? {
return json <||? [key]
}
// Pull embedded array from JSON
public func <|| <T where T: Decodable, T == T.DecodedType>(json: JSON, keys: [String]) -> [T]? {
return (json <|| keys).value
}
// Pull embedded optional array from JSON
public func <||? <T where T: Decodable, T == T.DecodedType>(json: JSON, keys: [String]) -> [T]?? {
return (json <||? keys).value
}
#import <Foundation/Foundation.h>
//! Project version number for Argo.
FOUNDATION_EXPORT double ArgoVersionNumber;
//! Project version string for Argo.
FOUNDATION_EXPORT const unsigned char ArgoVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <Argo/PublicHeader.h>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.0.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
public protocol Decodable {
/**
The type of object that will be decoded.
In order to work with the rest of Argo, this needs to be the same as `Self`.
You will only need to worry about this if the object conforming to
`Decodable` is a reference type (i.e. a `class`), and one of the following
is true:
- Your type is not marked as `final`
- Your `decode` function is not marked as either `final` or `static`
In that case, you will need to explicitly set `DecodedType` to the type you
are returning in order for the compiler to be able to guarantee that this
protocol is being fully conformed to.
We expect the need for this typealias to be removed in a later version of Swift.
*/
associatedtype DecodedType = Self
/**
Decode an object from JSON.
This is the main entry point for Argo. This function declares how the
conforming type should be decoded from JSON. Since this is a failable
operation, we need to return a `Decoded` type from this function.
- parameter json: The `JSON` representation of this object
- returns: A decoded instance of the `DecodedType`
*/
static func decode(_ json: JSON) -> Decoded<DecodedType>
}