I make a web service call in one of my functions. I'm having trouble how to structure it so the logic is clean and not redundant. Here's what I have:
public func getTimes() -> [TimeName: MyResult] {
let deferredTask: () -> [TimeName: MyResult] = {
var computed = self.computeTimes()
// Do something
return computed
}
// Calculate timezone
if let tz = timezone {
timeZone = tz
return deferredTask()
} else {
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) {
(data, response, error) in
// Do something with data
return deferredTask() // Getting a compiler error here btw: is not convertible to 'Void'
}.resume()
}
}
Then I call this using MyInstance.getTimes()
. Something doesn't feel right though.. is setting up a deferredTask
variable a good way to avoid redundant code?
Also, when I call this from another function, I would use var test = MyInstance.getTimes()
. But this means test
is in an async state and I can't use it in my other function until the web service is finished in getTimes
.
What's a good way to handle this in Swift?
As you rightly say, you cannot do this:
public func getTimes() -> [TimeName: MyResult] {
let deferredTask: () -> [TimeName: MyResult] = {
var computed = self.computeTimes()
// Do something
return computed
}
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) {
(data, response, error) in
// Do something with data
return deferredTask()
}.resume()
}
The problem, as I think you are correctly trying to say, is that return deferredTask()
is returned from the surrounding closure - not from getTimes
. You cannot asynchronously return a value from a function call! That is the nature of asynchrony.
A typical standard solution is to write getTimes
itself to accept a callback function as its parameter. It itself returns nothing. When the asynchronous method is finished, it calls the callback, thus getting the info back, at some future time, to the original caller.
To show you what I mean, I'm going to remove your deferredTask
entirely from the story and just concentrate on the asynchronous task and the callback. So we would have this:
public func getTimes(f:(Any) -> Void) -> () {
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: url)!) {
(data, response, error) in
// ...
f(whatever)
}.resume()
}
The point of this architecture is that the call to f(whatever)
effectively goes back to whoever handed us f
in the first place - especially if that entity put self
into it somewhere.