Consider the following code:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let validator:NSPredicate = NSPredicate(format:"SELF MATCHES %@","[A-Za-z0-9- ]+")
if(validator.evaluateWithObject(string) || string == "" /* i.e. backspace */) {
self.process(textField)
return true
}
else {
return false
}
}
I want to actually run self.process(textField) AFTER the return statement, because before it, the text in the textField has not actually changed yet. This led me to wonder, why can't I just execute some code after the return statement? Why do functions always stop when the return statement happens?
I realize that's traditionally what return means, but is there an alternative? Like, is there a way to return a value from a function and then still keep going?
On the one hand this seems like a stupid question, but on the other hand, I feel like I can't be the first person to ever want to do this. It would be good enough if I could fire off something to run on the next cycle of the run loop, so maybe there's something in GCD that would help me.
Since Swift 2.0 we have the keyword called "defer". A keyword that allows us to specify a block of code, a segment inside our function that will be executed just before the program control is transferred outside of the scope. Maybe for cleanup or other needs, actions that need to happen even if an error is thrown.
The code execution inside a defer block is deferred until the penultimate statement is executed, assuming the case where the last one is a return statement.
Here's how you can use it:
func anyFunction(someParameter: Int) -> Int {
// Some code
defer {
// Code to be deferred.
}
return someValue
} // anyFunction
The position of the defer block should be placed anywhere within the curly braces and always before the return statement, for logical reasons and also to avoid the warning: "Code after 'return' will never be executed".
Some examples:
The answer to your question is no. But the solution to your problem is simple.
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
let validator:NSPredicate = NSPredicate(format:"SELF MATCHES %@","[A-Za-z0-9- ]+")
if(validator.evaluateWithObject(string) || string == "" /* i.e. backspace */) {
DispatchQueue.main.async {
print("about to process textField, timestamp: \(Date().timeIntervalSince1970)")
self.process(textField)
}
print("about to return true, timestamp: \(Date().timeIntervalSince1970)")
return true
}
else {
return false
}
}
DispatchQueue.main.async
will defer execution to the next iteration of the run loop. With those print()
statements you'll see this in the console. Your timestamps will be different but you'll see the small difference (about 15/1000 of a second in this case).
about to return true, timestamp: 1612578965.103658
about to process textField, timestamp: 1612578965.1188931
If you require a specific delay use DispatchQueue.main.asyncAfter
The best explanation I have found on this is Matt Neuburg's book "iOS 14 Programming Fundamentals with Swift". See the Delayed Performance section.
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