Skip to content

Instantly share code, notes, and snippets.

@michaelnisi
Last active July 21, 2023 13:49
Show Gist options
  • Save michaelnisi/f19bf39262ce11db4612 to your computer and use it in GitHub Desktop.
Save michaelnisi/f19bf39262ce11db4612 to your computer and use it in GitHub Desktop.
Consume a JSON HTTP API in Swift
// Consume a JSON HTTP API in Swift
import Foundation
struct Feed: Printable {
let author: String?
let image: String?
let language: String?
let link: String?
let summary: String?
let title: String?
let updated: String?
var description: String {
return "Feed: \(title)"
}
}
// TODO: Not sure if I like this.
enum Callback<NSError, T> {
case Error(() -> NSError)
case Response(() -> T)
}
struct MangerClientOpts {
let feedsURL: String
}
// A client for the Manger HTTP API
struct MangerClient {
let feedsURL: String
let conf: NSURLSessionConfiguration
let sess: NSURLSession
init (opts: MangerClientOpts) {
conf = NSURLSessionConfiguration.defaultSessionConfiguration()
sess = NSURLSession(configuration: conf)
feedsURL = opts.feedsURL
}
func parse (data: NSData) -> AnyObject? {
return NSJSONSerialization.JSONObjectWithData(
data
, options: NSJSONReadingOptions(0)
, error: nil)
}
func json (urls: Array<String>) -> NSData {
return NSJSONSerialization.dataWithJSONObject(
urls.map({
url -> Dictionary<String, String> in
["url": url]
})
, options: NSJSONWritingOptions(0)
, error: nil)
}
func req (urls: String[]) -> NSMutableURLRequest {
let url = NSURL(string: feedsURL)
let req = NSMutableURLRequest(URL: url)
req.HTTPMethod = "POST"
req.HTTPBody = json(urls)
return req
}
func feed (dict: NSDictionary) -> Feed {
func str (key: String) -> String? {
return dict.objectForKey(key) as? String
}
return Feed(
author: str("author")
, image: str("image")
, language: str("language")
, link: str("link")
, summary: str("summary")
, title: str("title")
, updated: str("updated")
)
}
func error (code: Int, reason: String, res: NSURLResponse, data: NSData?)
-> NSError {
let dataString = data != nil ? NSString(data: data, encoding: 4) : "nil"
let info = ["reason": reason, "response": res, "data": dataString]
return NSError(domain: "Manger", code: code, userInfo: info)
}
func feeds (urls: Array<String>, cb: (Callback<NSError, Feed[]>) -> ()) {
let task = sess.dataTaskWithRequest(req(urls)) {
(data, res, er) in
if er != nil {
return cb(Callback.Error({er}))
}
let code = (res as NSHTTPURLResponse).statusCode
func report (reason: String) -> NSError {
return self.error(code, reason: reason, res: res, data: data)
}
if data != nil && code == 200 {
if let json: AnyObject? = self.parse(data) {
let dicts = json as NSDictionary[]
let feeds = dicts.map {
dict -> Feed in
self.feed(dict)
}
cb(Callback.Response({feeds}))
} else {
cb(Callback.Error({report("received data not JSON")}))
}
} else {
cb(Callback.Error({report("no data received")}))
}
}
task.resume()
}
}
// Example
func urls () -> String[] {
return [
"http://feeds.muleradio.net/thetalkshow"
, "http://www.friday.com/bbum/feed/"
, "http://dtrace.org/blogs/feed/"
, "http://5by5.tv/rss"
, "http://feeds.feedburner.com/thesartorialist"
, "http://hypercritical.co/feeds/main"
]
}
func opts () -> MangerClientOpts {
return MangerClientOpts(feedsURL: "http://localhost:8080/feeds")
}
func client () -> MangerClient {
return MangerClient(opts: opts())
}
func req() -> Bool {
let sema = dispatch_semaphore_create(0)
client().feeds(urls()) {
res in
switch res {
case .Error(let er):
println("error \(er())")
case .Response(let feeds):
for feed in feeds() {
println(feed.title)
}
}
dispatch_semaphore_signal(sema)
}
return wait(sema)
}
func wait (sema: dispatch_semaphore_t, seconds: Int = 8) -> Bool {
let period = Int64(CUnsignedLongLong(seconds) * NSEC_PER_SEC)
let timeout = dispatch_time(DISPATCH_TIME_NOW, period)
return dispatch_semaphore_wait(sema, timeout) == 0
}
println(req() ? "ok" : "not ok")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment