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:
The problem, as I think you are correctly trying to say, is that
return deferredTask()
is returned from the surrounding closure - not fromgetTimes
. 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:The point of this architecture is that the call to
f(whatever)
effectively goes back to whoever handed usf
in the first place - especially if that entity putself
into it somewhere.