Skip to content

Commit

Permalink
Present errors in a modal sheet
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Dec 28, 2019
1 parent 9390697 commit 8409ef6
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Gifski/ConversionCompletedViewController.swift
Expand Up @@ -145,7 +145,7 @@ final class ConversionCompletedViewController: NSViewController {
do {
try FileManager.default.copyItem(at: url, to: outputUrl, overwrite: true)
} catch {
self.presentError(error)
error.presentAsModalSheet(for: self.view.window)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Gifski/ConversionViewController.swift
Expand Up @@ -90,7 +90,7 @@ final class ConversionViewController: NSViewController {
} catch Gifski.Error.cancelled {
self.cancelConversion()
} catch {
self.presentError(error, modalFor: self.view.window)
error.presentAsModalSheet(for: self.view.window)
self.cancelConversion()
}
}
Expand Down
58 changes: 58 additions & 0 deletions Gifski/util.swift
Expand Up @@ -2783,3 +2783,61 @@ class BackButton: NSButton {
self.image = NSImage(named: NSImage.goBackTemplateName)
}
}


extension NSResponder {
// This method is internally implemented on `NSResponder` as `Error` is generic which comes with many limitations.
fileprivate func presentErrorAsSheet(
_ error: Error,
for window: NSWindow,
didPresent: (() -> Void)?
) {
final class DelegateHandler {
var didPresent: (() -> Void)?

@objc
func didPresentHandler() {
didPresent?()
}
}

let delegate = DelegateHandler()
delegate.didPresent = didPresent

presentError(
error,
modalFor: window,
delegate: delegate,
didPresent: #selector(delegate.didPresentHandler),
contextInfo: nil
)
}
}

extension Error {
/// Present the error as an async sheet on the given window.
/// - Note: This exists because the built-in `NSResponder#presentError(forModal:)` method requires too many arguments, selector as callback, and it says it's modal but it's not blocking, which is surprising.
func presentAsSheet(for window: NSWindow, didPresent: (() -> Void)?) {
NSApp.presentErrorAsSheet(self, for: window, didPresent: didPresent)
}

/// Present the error as a blocking modal sheet on the given window.
/// If the window is nil, the error will be presented in an app-level modal dialog.
func presentAsModalSheet(for window: NSWindow?) {
guard let window = window else {
presentAsModal()
return
}

presentAsSheet(for: window) {
NSApp.stopModal()
}

NSApp.runModal(for: window)
}

/// Present the error as a blocking app-level modal dialog.
func presentAsModal() {
NSApp.presentError(self)
}
}

0 comments on commit 8409ef6

Please sign in to comment.