The problem you have is that you are calling an asynchronous method and expecting to return the result synchronously. When your code is executed, the getBook
function completes and returns before even the GET
request has complete.
Basically, you have two options:
- Update your
getBook
method to be asynchronous and return the result with a completion block/callback
- Wait for the asynchronous call to complete, blocking the current thread (this is OK as long as it is not the main thread you are blocking), and return the result synchronously.
1. Update your method to be asynchronous
To do this, you must return the result on a block/callback function.
class BookGetter {
static let instance = BookGetter()
func getBook(bookId: String, complete: (book: Book?, error: NSError?) -> Void) {
let rootUrl = "https://www.someusefulbookapi.com/bookid=?"
let url = rootUrl + bookId
var title = ""
Alamofire.request(.GET, url).response { request, response, data, error in
// TODO: You should check for network errors here
// and notify the calling function and end-user properly.
if error != nil {
complete(book: nil, error: error as? NSError)
return
}
let jsonDict = JSON(data: response.2!)
title = String(jsonDict["items"][0]["volumeInfo"]["title"])
let book = Book(title: title)
print(book.title)
complete(book: book, error: nil)
}
}
}
As mentioned in the above code, ideally you should handle errors in the callback response (including exceptions while parsing the JSON). Once handled, you can update the callback parameters to (book: Book?, error: NSError?) -> Void
or similar, and check for book
or error
to be set on the caller function.
To call the function, you need to pass a block to handle the response:
BookGetter.instance.getBook("bookID") { (book, error) in
if error != nil {
// Show UIAlertView with error message (localizedDescription)
return
}
// Update User Interface with the book details
}
2. Wait for the asynchronous call to complete
As mentioned above, this is a good idea only if you were running this code on a background thread. It is OK to block background threads, but it is never OK to block the main thread on a graphic application, as it will freeze the user interface. If you do not know what blocking means, please use the option #1.
class BookGetter {
static let instance = BookGetter()
func getBook(bookId: String) -> Book {
let rootUrl = "https://www.someusefulbookapi.com/bookid=?"
let url = rootUrl + bookId
var title = ""
let semaphore = dispatch_semaphore_create(0)
Alamofire.request(.GET, url).response { response in
let jsonDict = JSON(data: response.2!)
title = String(jsonDict["items"][0]["volumeInfo"]["title"])
dispatch_semaphore_signal(semaphore)
}
//Wait for the request to complete
while dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW) != 0 {
NSRunLoop.currentRunLoop().runMode(NSDefaultRunLoopMode, beforeDate: NSDate(timeIntervalSinceNow: 10))
}
let book = Book(title: title)
print(book.title)
return book
}
}