I have created a utility class in my Swift project that handles all the REST requests and responses. I have built a simple REST API so I can test my code. I have created a class method that needs to return an NSArray but because the API call is async I need to return from the method inside the async call. The problem is the async returns void. If I were doing this in Node I would use JS promises but I can't figure out a solution that works in Swift.
import Foundation class Bookshop { class func getGenres() -> NSArray { println("Hello inside getGenres") let urlPath = "http://creative.coventry.ac.uk/~bookshop/v1.1/index.php/genre/list" println(urlPath) let url: NSURL = NSURL(string: urlPath) let session = NSURLSession.sharedSession() var resultsArray:NSArray! let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in println("Task completed") if(error) { println(error.localizedDescription) } var err: NSError? var options:NSJSONReadingOptions = NSJSONReadingOptions.MutableContainers var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: options, error: &err) as NSDictionary if(err != nil) { println("JSON Error \(err!.localizedDescription)") } //NSLog("jsonResults %@", jsonResult) let results: NSArray = jsonResult["genres"] as NSArray NSLog("jsonResults %@", results) resultsArray = results return resultsArray // error [anyObject] is not a subType of 'Void' }) task.resume() //return "Hello World!" // I want to return the NSArray... } }
You can pass callback, and call callback inside async call
something like:
class func getGenres(completionHandler: (genres: NSArray) -> ()) { ... let task = session.dataTaskWithURL(url) { data, response, error in ... resultsArray = results completionHandler(genres: resultsArray) } ... task.resume() }
and then call this method:
override func viewDidLoad() { Bookshop.getGenres { genres in println("View Controller: \(genres)") } }
Swiftz already offers Future, which is the basic building block of a Promise. A Future is a Promise that cannot fail (all terms here are based on the Scala interpretation, where a Promise is a Monad).
https://github.com/maxpow4h/swiftz/blob/master/swiftz/Future.swift
Hopefully will expand to a full Scala-style Promise eventually (I may write it myself at some point; I'm sure other PRs would be welcome; it's not that difficult with Future already in place).
In your particular case, I would probably create a Result<[Book]>
(based on Alexandros Salazar's version of Result
). Then your method signature would be:
class func fetchGenres() -> Future<Result<[Book]>> {
Notes
get
in Swift. It will break certain kinds of interoperability with ObjC.Book
object before returning your results as a Future
. There are several ways this system can fail, and it's much more convenient if you check for all of those things before wrapping them up into a Future
. Getting to [Book]
is much better for the rest of your Swift code than handing around an NSArray
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With