Last active
March 3, 2026 10:25
-
-
Save stephancasas/c801e4fa7918067c19bb548520725053 to your computer and use it in GitHub Desktop.
An extension on `NSAlert` for `NSTextField` drawing/management
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // NSAlert+NSTextField.swift | |
| // | |
| // Created by Stephan Casas for OpenAI on 2/26/26. | |
| // | |
| import Foundation | |
| import AppKit | |
| extension NSAlert { | |
| // MARK: - NSTextField Additions | |
| /// Create and add a text field to the alert. The text field will become the first responder when the alert is displayed. | |
| /// - Parameters: | |
| /// - title: An optional label string which is drawn above the text field. | |
| /// - placeholder: An optional placeholder string drawn within the text field. | |
| /// - Returns: the added text field instance | |
| @discardableResult | |
| func addTextField(title: String? = nil, placeholder: String? = nil) -> NSTextField? { | |
| let selector = NSSelectorFromString("addTextFieldWithTitle:") | |
| guard | |
| responds(to: selector), | |
| let textField = perform(selector, with: title).takeUnretainedValue() as? NSTextField | |
| else { | |
| return nil | |
| } | |
| textField.placeholderString = placeholder | |
| return textField | |
| } | |
| /// Create and add a secure text field to the alert. The text field will become the first responder when the alert is displayed. | |
| /// - Parameters: | |
| /// - title: An optional label string which is drawn above the text field. | |
| /// - placeholder: An optional placeholder string drawn within the text field. | |
| /// - Returns: the added secure text field instance | |
| @discardableResult | |
| func addSecureTextField(title: String? = nil, placeholder: String? = nil) -> NSSecureTextField? { | |
| let selector = NSSelectorFromString("addSecureTextFieldWithTitle:") | |
| guard | |
| responds(to: selector), | |
| let textField = perform(selector, with: title).takeUnretainedValue() as? NSSecureTextField | |
| else { | |
| return nil | |
| } | |
| textField.placeholderString = placeholder | |
| return textField | |
| } | |
| /// The text fields managed by this alert. | |
| var textFields: [NSTextField] { | |
| let key = "textFields" | |
| return if responds(to: NSSelectorFromString(key)) { | |
| value(forKey: key) as? [NSTextField] ?? [] | |
| } else { | |
| [] | |
| } | |
| } | |
| // MARK: - Attributed String Additions | |
| private static let attributedInformativeTextKey = "attributedInformativeText" | |
| private static let attributedInformativeTextSetter = NSSelectorFromString("setAttributedInformativeText:") | |
| /// As an attributed string, the descriptive text that provides more details about the reason for the alert. | |
| var attributedInformativeText: NSAttributedString? { | |
| get { | |
| if responds(to: NSSelectorFromString(Self.attributedInformativeTextKey)) { | |
| value(forKey: Self.attributedInformativeTextKey) as? NSAttributedString | |
| } else { | |
| nil | |
| } | |
| } | |
| set { | |
| if responds(to: Self.attributedInformativeTextSetter) { | |
| setValue(newValue, forKey: Self.attributedInformativeTextKey) | |
| } | |
| } | |
| } | |
| private static let attributedMessageTextKey = "attributedMessageText" | |
| private static let attributedMessageTextSetter = NSSelectorFromString("setAttributedMessageText:") | |
| /// As an attributed string, the text that is displayed prominently in the alert. | |
| var attributedMessageText: NSAttributedString? { | |
| get { | |
| if responds(to: NSSelectorFromString(Self.attributedMessageTextKey)) { | |
| value(forKey: Self.attributedMessageTextKey) as? NSAttributedString | |
| } else { | |
| nil | |
| } | |
| } | |
| set { | |
| if responds(to: Self.attributedMessageTextSetter) { | |
| setValue(newValue, forKey: Self.attributedMessageTextKey) | |
| } | |
| } | |
| } | |
| // MARK: - Convenience | |
| /// Runs the alert modally as a sheet attached to the specified window. | |
| /// - Parameter sheetWindow: The window on which to display the sheet. | |
| /// - Returns: A tuple value containing both the modal response and an ordered array of strings for each alert-managed text field. | |
| @_disfavoredOverload | |
| func beginSheetModal(for sheetWindow: NSWindow) async -> (responseValue: NSApplication.ModalResponse, inputValues: [String]) { | |
| (await beginSheetModal(for: sheetWindow), textFields.map(\.stringValue)) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment