Last active
June 25, 2018 10:33
-
-
Save chrisschreiner/7aa30fade930774a73b8 to your computer and use it in GitHub Desktop.
Astronomy Picture of the Day with ReactiveCocoa 4
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
// APOD.swift | |
// Test with ReactiveCocoa 4 | |
// | |
// Created by Chris Patrick Schreiner on 17-11-2015. | |
import ReactiveCocoa | |
import Result | |
import XCPlayground | |
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true | |
// Get your own key at https://api.nasa.gov/index.html#apply-for-an-api-key/ | |
let APOD_API_KEY: String? = nil //when nil, use DEMO_KEY | |
//:#### Misc stuff used by the astronomyPictureOfTheDay-consumer | |
func mapDate(date: NSDate) -> String { | |
let formatter = NSDateFormatter() | |
formatter.dateFormat = "yyyy-MM-dd" | |
return formatter.stringFromDate(date) | |
} | |
extension Double { | |
var days: NSTimeInterval {return 86400*self} | |
} | |
extension NSImageView { | |
convenience init(image:NSImage) { | |
self.init() | |
self.image = image | |
self.frame = NSMakeRect(0,0,image.size.width, image.size.height) | |
} | |
} | |
func presentImage(image:NSImage) { | |
XCPlaygroundPage.currentPage.liveView = NSImageView(image: image) | |
} | |
func printError<T>(e:T) { | |
print("error: \(e)") | |
} | |
//:### Helper functions used by the astronomyPictureOfTheDay-producer | |
typealias JSONDict = [NSObject:AnyObject] | |
enum APODError: ErrorType { | |
case ErrorInURL | |
case ServerHappenstance(String) | |
case JSONHappenstance(String) | |
case ValidationHappenstance(String) | |
case ImageHappenstance | |
case NSError(String, Int, JSONDict?) | |
} | |
func makeUrl(date: NSDate, hd: String = "false", format: String = "JSON", apiKey: String, urlAPI: NSURL? = NSURL(string: "https://api.nasa.gov/planetary/apod")) -> NSURL? { | |
if let urlAPI = urlAPI, components = NSURLComponents(URL: urlAPI, resolvingAgainstBaseURL: false) { | |
components.queryItems = [ | |
"api_key": apiKey, | |
"format": format, | |
"hd": hd, | |
"date": mapDate(date), | |
].map(NSURLQueryItem.init) | |
return components.URL | |
} | |
return nil | |
} | |
func validateResponse(data: NSData, response: NSURLResponse) -> Result<(NSData, NSURLResponse), APODError> { | |
guard let r = response as? NSHTTPURLResponse else { | |
return .Failure(APODError.ValidationHappenstance("NSHTTPURLResponse cast failed")) | |
} | |
guard 200..<299 ~= r.statusCode else { | |
return .Failure(.ValidationHappenstance("statuscode \(r.statusCode)")) | |
} | |
return .Success((data, response)) | |
} | |
func jsonAdaptor(data: NSData, response: NSURLResponse) -> Result<JSONDict, APODError> { | |
guard let json = try? NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) else { | |
return .Failure(.JSONHappenstance("got thrown at for serializing")) | |
} | |
guard let dict = json as? JSONDict else { | |
return .Failure(.JSONHappenstance("JSONDict cast failed \(json)")) | |
} | |
return .Success(dict) | |
} | |
func performRequest(request: NSURLRequest) -> SignalProducer<(NSData, NSURLResponse), APODError> { | |
return NSURLSession.sharedSession() | |
.rac_dataWithRequest(request) | |
.mapError { | |
error in .NSError(error.domain, error.code, error.userInfo) | |
} | |
} | |
func extractUrl(dict: JSONDict) -> Result<NSURL, APODError> { | |
guard let urlString = dict["url"] as? String, url = NSURL(string:urlString) else { | |
if let error = dict["error"] as? String { | |
return .Failure(.ServerHappenstance(error)) | |
} | |
return .Failure(.JSONHappenstance("url-field not found")) | |
} | |
return .Success(url) | |
} | |
func imageFromData(data: NSData, response: NSURLResponse) -> Result<NSImage, APODError> { | |
guard let image = NSImage(data: data) else { | |
return .Failure(.ImageHappenstance) | |
} | |
return .Success(image) | |
} | |
//:### Core | |
func astronomyPictureOfTheDay(date date: NSDate = NSDate(), apiKey: String? = nil) -> SignalProducer<NSImage, APODError> { | |
if let url = makeUrl(date, apiKey: apiKey ?? "DEMO_KEY") { | |
return SignalProducer(value: url) | |
.map(NSURLRequest.init) | |
.flatMap(.Latest, transform: performRequest) //fetch some JSON | |
.attemptMap(validateResponse) //make sure we get a 200 | |
.attemptMap(jsonAdaptor) //convert NSData to JSON | |
.attemptMap(extractUrl) //extract from JSON a URL | |
.map(NSURLRequest.init) | |
.flatMap(.Latest, transform: performRequest) //fetch an NSImage | |
.attemptMap(validateResponse) //make sure we get a 200 | |
.attemptMap(imageFromData) //convert NSData to NSImage | |
} else { | |
return SignalProducer(error: APODError.ErrorInURL) | |
} | |
} | |
astronomyPictureOfTheDay(date:NSDate(timeIntervalSinceNow: 0.days), apiKey: APOD_API_KEY) | |
.observeOn(QueueScheduler.mainQueueScheduler) | |
.on(next: presentImage) | |
.on(failed: printError) | |
.start() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment