Implementing Auto-Type on macOS
Auto-Type is a feature that simulates typing a sequence of keystrokes into another application. On macOS, you can achieve this by programmatically generating keyboard events. In this post, I’ll show you how to implement Auto-Type in Swift, covering permissions, event synthesis, character-to-keycode mapping, and practical tips for reliability and security.
Understanding the Approach
macOS does not provide a direct API for sending text to other apps. Instead, you need to
- Request Accessibility permissions
- Use Core Graphics (
CGEvent
) to synthesize key events - Map characters to key codes, including handling modifiers (Shift, Option, etc.)
- Ensure the target app is focused before sending events
Requesting Accessibility Permissions
Your app must be trusted for Accessibility to send events to other apps.
First, add the NSAppleEventsUsageDescription
key to your app’s Info.plist
file. This key provides a message shown to the user when the system asks for permission to control other apps.
<key>NSAppleEventsUsageDescription</key>
<string>This app requires access to control other applications for Auto-Type functionality.</string>
You should also check and prompt for permission in your code.
import ApplicationServices
let options: [String: Any] = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as String: true]
let isTrusted = AXIsProcessTrustedWithOptions(options as CFDictionary)
if !isTrusted {
// The system will show a prompt to the user to enable Accessibility for your app
// You can also show your own instructions here if needed
}
Note: The system prompt to grant Accessibility permission can only be shown once per app launch. If the user dismisses the prompt or does not grant permission, your app must be restarted to show the prompt again. Always provide clear instructions to users on how to enable the application in System Settings → Privacy & Security → Accessibility if the prompt does not appear.
Mapping Characters to Key Codes
macOS keyboard events use key codes, not characters. You need a mapping from characters to key codes and modifiers.
For ASCII letters and numbers, you can use a lookup table. For more complex input, use UCKeyTranslate
or TISInputSource
.
Example mapping for common characters
let keyCodeMap: [Character: (CGKeyCode, CGEventFlags)] = [
"a": (0, []), "A": (0, .maskShift),
"b": (11, []), "B": (11, .maskShift),
// ... add more as needed
"1": (18, []), "!": (18, .maskShift)
]
Synthesizing and Posting Key Events
Use CGEvent
to create key down and key up events, and post them to the system event tap.
import Cocoa
func sendKeystroke(keyCode: CGKeyCode, flags: CGEventFlags = []) {
guard let keyDown = CGEvent(keyboardEventSource: nil, virtualKey: keyCode, keyDown: true),
let keyUp = CGEvent(keyboardEventSource: nil, virtualKey: keyCode, keyDown: false) else {
return
}
keyDown.flags = flags
keyDown.post(tap: .cgAnnotatedSessionEventTap)
keyUp.flags = flags
keyUp.post(tap: .cgAnnotatedSessionEventTap)
}
Typing a String
Combine the mapping and event synthesis to type a string.
func typeString(_ string: String) {
for char in string {
guard let (keyCode, flags) = keyCodeMap[char] else {
continue // skip unsupported characters
}
sendKeystroke(keyCode: keyCode, flags: flags)
usleep(100_000) // 100ms delay to mimic human typing
}
}
Focusing the Target Application
Auto-Type only works if the target app or field is focused.
You can use AppleScript to bring an app to the front.
import Foundation
func activateApp(bundleIdentifier: String) {
let script = """
tell application id "\(bundleIdentifier)"
activate
end tell
"""
var error: NSDictionary?
if let scriptObject = NSAppleScript(source: script) {
scriptObject.executeAndReturnError(&error)
}
}
Security Considerations
Security is paramount when implementing Auto-Type. Only trigger Auto-Type after explicit user action, never log or store sensitive data, and always clearly inform users about the permissions required and what will be typed. This ensures users remain in control and sensitive information is never exposed unintentionally.
Handling Edge Cases
There are several edge cases to consider. For international layouts or special characters, you may need to use more advanced APIs for dynamic mapping, such as TISInputSource
or UCKeyTranslate
. It is also important to insert delays between keystrokes to avoid dropped input, especially in slower or virtualized environments. Modifier keys (such as Shift or Option) must be handled correctly for uppercase letters and symbols, ensuring that the correct key combination is sent for each character.
Typing a Password into Another App
// 1. Activate the target app (e.g., Safari)
activateApp(bundleIdentifier: "com.apple.Safari")
// 2. Wait for the app to become active
sleep(1)
// 3. Type the password
typeString("P@ssw0rd!")
Conclusion
By combining Accessibility permissions, key code mapping, and event synthesis, you can implement reliable Auto-Type functionality in any macOS app. This approach is flexible and can be adapted for various automation needs, but always keep user security and privacy in mind.
Enjoyed this article? Support my work by buying me a coffee! ☕️
Buy Me a Coffee