That is how a modal window works: the window is presented synchronously, and the code after NSApp.runModal(for: window)
is executed after the modal window session is stopped.
Under the hood, the modal window runs in a special modal event loop, so that only the events happen in the specified window are processed.
In code, it does stop the execution at the point of NSApp.runModal(for: window)
, but you can still dispatch jobs from the window. For example, a button on the modal window that triggers a download task.
You can use:
- performSelector(onMainThread:with:waitUntilDone:modes:) to dispatch a job.
- GCD to dispatch a job.
One common scenario that you feel the modal window blocks code execution can be:
// present a modal window in GCD main queue asynchronously:
DispatchQueue.main.async {
NSApp.runModal(for: window)
// only executes after modal window is dismissed
}
and somewhere else tries to dispatch using GCD. For example:
// modal window's button action handler:
button.actionHandler = {
DispatchQueue.main.async {
// some button action...
}
}
Because NSApp.runModal(for: window)
doesn't leave the dispatched block, the GCD main queue won't execute following blocks until the modal window is dismissed.
To avoid this blocking issue, you could:
- Avoid calling
NSApp.runModal(for: window)
in DispatchQueue.main
.
- Use performSelector(onMainThread:with:waitUntilDone:modes:) to dispatch a job.
For the 2nd solution, I made a convenient helper for myself:
public func onMainThread(waitUntilDone: Bool = false, block: @escaping BlockVoid) {
_ = MainRunloopDispatcher(waitUntilDone: waitUntilDone, block: block)
}
private final class MainRunloopDispatcher: NSObject {
private let block: BlockVoid
init(waitUntilDone: Bool = false, block: @escaping BlockVoid) {
self.block = block
super.init()
performSelector(onMainThread: #selector(execute), with: nil, waitUntilDone: waitUntilDone)
}
@objc private func execute() {
block()
}
}
So that when the GCD main queue is blocked, you can use to call some code on the main thread
onMainThread {
// here is on main thread, update UI...
}
To read more, this article helped me.