-
-
Save robb/1639325aa8784c4bbc29 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
typealias JSONDictionary = [String: AnyObject] | |
/// Attempts to load a data from a given URL. | |
func loadData(URL: NSURL) -> Either<NSError, NSData> { | |
if let data = NSData(contentsOfURL: URL) { | |
return Either.Right(Box(data)) | |
} else { | |
// In a real world scenario, we'd have a more useful error :-) | |
return Either.Left(Box(NSError())) | |
} | |
} | |
/// Attempts to parse JSON from NSData. | |
func parseJSON(data: NSData) -> Either<NSError, JSONDictionary> { | |
var error: NSError? | |
if let dictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: nil) as? JSONDictionary { | |
return Either.Right(Box(dictionary)) | |
} else { | |
// In a real world scenario, we'd have a more useful fallback error :-) | |
return Either.Left(Box(error ?? NSError())) | |
} | |
} | |
let appIDs = [ "690046600" ] | |
let JSONResults = appIDs | |
/// Build the URLs: | |
|> map { NSURL(string: "https://itunes.apple.com/lookup?id=\($0)")! } | |
/// Download the data and attempt to parse the JSON, passing all errors | |
/// along: | |
|> map { loadData($0) |> flatMap(parseJSON) } | |
/// Strip all errors by filtering them out and force-unwrapping the | |
/// remaining elements: | |
|> filter { $0.right != nil } | |
|> map { $0.right! } | |
let info = JSONResults | |
/// Extract the data that we need from the remaining JSON dictionaries: | |
|> map { (JSON: JSONDictionary) -> (String, String, String) in | |
let appInfo = JSON | |
|> flatMap { $0["results"] as? [JSONDictionary] } | |
|> flatMap { $0[0] } | |
let name = appInfo | |
|> flatMap { $0["trackName"] as? String } | |
let price = appInfo | |
|> flatMap { $0["formattedPrice"] as? String } | |
let rating = appInfo | |
|> flatMap { | |
$0["averageUserRating"] ?? $0["trackContentRating"] as? NSNumber | |
} | |
|> map { "\($0)" } | |
return (name ?? "Unknown App", price ?? "Unknown Price", rating ?? "Rating not available") | |
} | |
println(info) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// The application operator allows us to chain free functions as if they were | |
/// member functions, taken from https://github.com/robrix/Prelude | |
infix operator |> { | |
associativity left | |
precedence 95 | |
} | |
public func |> <T, U> (left: T, @noescape right: T -> U) -> U { | |
return right(left) | |
} | |
/// Box let's us to have type-parameterized enums where more than one case has | |
/// a value, taken from https://github.com/robrix/Box | |
public final class Box<T> { | |
public let value: T | |
public init(_ value: T) { | |
self.value = value | |
} | |
} | |
/// Either allows us to specify that a value should be one of two types, or that | |
/// that a value of a single type should have one of two semantics. | |
/// From https://github.com/robrix/Either | |
public enum Either<T, U> { | |
case Left(Box<T>) | |
case Right(Box<U>) | |
public var left: T? { | |
switch self { | |
case let .Left(x): | |
return x.value | |
case let .Right(_): | |
return .None | |
} | |
} | |
public var right: U? { | |
switch self { | |
case let .Left(_): | |
return .None | |
case let .Right(x): | |
return x.value | |
} | |
} | |
} | |
/// Flipped variants of function we'll need. By having a closure as the first, | |
/// curried argument, we can chain them more elegantly with |>. | |
public func filter<S : SequenceType>(includeElement: (S.Generator.Element) -> Bool)(source: S) -> [S.Generator.Element] { | |
return filter(source, includeElement) | |
} | |
public func flatMap<T, U>(f: (T) -> U?)(x: T?) -> U? { | |
return flatMap(x, f) | |
} | |
public func flatMap<T, U, V>(transform: U -> Either<T, V>)(either: Either<T, U>) -> Either<T, V> { | |
switch either { | |
case let .Left(x): | |
return Either.Left(x) | |
case let .Right(x): | |
return transform(x.value) | |
} | |
} | |
public func map<T, U>(f: (T) -> U)(x: T?) -> U? { | |
return map(x, f) | |
} | |
public func map<S : SequenceType, T>(transform: (S.Generator.Element) -> T)(source: S) -> [T] { | |
return map(source, transform) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment