Hi
In this snippet, I've already added a button, the onDisplay
action simply adds another UIButton
with an addAction
event to the view controller
@IBAction func onDisplay(_ sender: UIButton) {
let semaphore = DispatchSemaphore(value: 1)
var didComplete = false
addButton("hello world") { result in
print("result: \(result)")
didComplete = result
semaphore.signal()
}
if semaphore.wait(timeout: .now() + 5) == .timedOut {
print("timeout: \(didComplete)")
}
print("didComplete: \(didComplete)")
}
func addButton(_ message: String, completion: @escaping (Bool) -> Void) {
let button = UIButton(type: .system)
button.frame = CGRect(x: 100, y: 100, width: 100, height: 40)
button.backgroundColor = .systemBlue
button.setTitle(message, for: .normal)
button.titleLabel?.tintColor = .white
button.addAction(UIAction(handler: { action in
completion(true)
}), for: .touchUpInside)
view.addSubview(button)
}
What I'm expecting to happen, is the new "hello world" button get created (which it does). If the user does nothing for 5 seconds (semaphore timeout), then print timeout: false.
If the user tapped the button, this would print result: true in the completion closure.
But what happens when I don't touch the button is didComplete: false prints out (the last line) and the semaphore.wait
never gets invoked.
How do I make the semaphore execute it's wait operation? Been struggling with this for days 😞
A better way to do this would be to add a timer that fires 5 seconds in the future but doesn’t block the main thread. Here’s an example:
final class MyViewController: UIViewController {
var timer: Timer? = nil
var result: Bool? = nil
@IBAction onDisplay(_sender: UIButton) {
addButton("hello world") { result in
// The button was pressed before the timer expired
self.result = result
timer?.invalidate()
timer = nil
processButtonResult()
}
timer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { timer in
// The timer fired before the button was pressed
self.timer = nil
result = false
processButtonResult()
}
}
func processButtonResult() {
// Remove the button from the UI
// ...
// Use the result
// ...
}
}