Skip to content

Instantly share code, notes, and snippets.

@chrisschreiner
Last active June 25, 2018 10:33
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisschreiner/7aa30fade930774a73b8 to your computer and use it in GitHub Desktop.
Save chrisschreiner/7aa30fade930774a73b8 to your computer and use it in GitHub Desktop.
Astronomy Picture of the Day with ReactiveCocoa 4
// 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