I basically have to methods, which are being called in my viewDidLoad. The first one gets the user's search preferences and saves the preferences to variables at the top. After that I want to access and work with these variables in the second function. But now the variables are always nil when I want to access them in the second function.
How do I need to adjust my viewDidLoad, so the second function is only executed once and my data request has been performed successfully?
var searchLocation = String()
var searchLocationCoordinates = [String:Double]()
var searchRange = Int()
override func viewDidLoad() {
super.viewDidLoad()
// Gets the user's search preference
getTheSearchLocationAndRange()
// Loads the data from the database
loadDataFromDatabase()
}
I have read stuff so far with dispatch_asynch or completion handler. Maybe someone can post some code which I can use in my viewDidLoad and makes it work?
You can use one like this: var timer = NSTimer() override func viewDidLoad() { scheduledTimerWithTimeInterval() } func scheduledTimerWithTimeInterval(){ // Scheduling timer to Call the function "updateCounting" with the interval of 1 seconds timer = NSTimer.
Show activity on this post. Answer extracted from question: It seems the problem is, that swift does not exit the function instantly on a return statement in a void function and uses the consecutive void value as the function parameter and terminates as expected if it is non-void.
You can use Swift closures! They are made for that.
Please refer to the Apple guide: Closures
Here's the code you need in your particular case.
FinishedDownload is the closure. When getTheSearchLocationAndRange()
is called, its code is executed until the completed()
line which waits for all processes of the function to finish. Once the processes finish (downloads for example), completed()
calls the closure which activates the code defined in getTheSearchLocationAndRange { () -> () in
. Therefore, loadDataFromDatabase()
is only called once getTheSearchLocationAndRange()
has entirely finished executing and the data is present (not nil).
var searchLocation = String()
var searchLocationCoordinates = [String:Double]()
var searchRange = Int()
typealias FinishedDownload = () -> ()
override func viewDidLoad() {
super.viewDidLoad()
getTheSearchLocationAndRange()
}
func getTheSearchLocationAndRange(completed: FinishedDownload) {
// Code for searching Location Range HERE
completed()
}
getTheSearchLocationAndRange { () -> () in
loadDataFromDatabase()
}
I hope this solved your issue and answered your question :)
BTW, about the "leaving your GUI hanging" part, Alamofire takes automatically care of this for you. If you don't use Alamofire, then you will have to manually assign the asynchronous request to a background thread so your GUI doesn't become unresponsive.
I wrote a demo project and posted it on GitHub that simulates handling an asynchronous network download. Take a look at DuncanMC/SwiftCompletionHandlers.
Specifically look at the method asyncFetchImage(), which does almost exactly what this thread is talking about: Uses an asynchronous method internally, and takes a completion block that it calls once the asynchronous load is done.
That is the general pattern you should use. Write a method that takes a completion block/closure. Internally, have that method call whatever asynchronous function it needs and then call your completion closure from inside the asynchronous method call's completion closure.
The function asyncFetchImage
looks like this:
func asyncFetchImage(imageName imageName: String,
completion: (
image: UIImage?,
status: String) -> ())
{
print("Entering \(#function)")
//Simulate a network operation by waiting a few seconds before loading an image
let nSecDispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(3.0 * Double(NSEC_PER_SEC)))
let queue = dispatch_get_main_queue()
dispatch_after(nSecDispatchTime, queue)
{
() -> Void in
let result = UIImage(named: imageName)
print("Loading image in background")
let status = result != nil ? "image loaded" : "Error loading image"
print("About to call completion handler")
completion(image: result, status: status)
}
print("Leaving \(#function)")
}
Okay I've found an solution. I basically called the function at the end of the first one.
So basically:
var searchLocation = String()
var searchLocationCoordinates = [String:Double]()
var searchRange = Int()
override func viewDidLoad() {
super.viewDidLoad()
// Gets the user's search preference
getTheSearchLocationAndRange()
}
func getTheSearchLocationAndRange() {
// Code for getTheSearchLocationAndRange()
loadDataFromDatabase()
}
func loadDataFromDatabase(){
// Code for loadDataFromDatabase()
}
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