Build Mail app extensions
Description: Meet MailKit: the best way to build amazing experiences on top of Mail. MailKit enables apps to easily and securely interact with the Mail app for macOS. We'll deep dive into the MailKit API, and show you how to create extensions for composing messages, message actions, secure email, and content blocking.
- Mail plug-ins will stop working in a future macOS release
- New MailKit framework, macOS 12.0+
- Mail extensions are built on the same underlying foundation as other app extensions, like Safari app extensions and share sheet extensions
- can be distributed standalone, bundled in an app (even notarized ones)
Extension types:
- compose extensions - allow new workflows when composing mail messages
- action extensions - help people manage their inbox by providing custom rules on incoming messages
- content blocking extensions - provide WebKit content blockers for Mail messages
- message security extensions - provide further security by signing, encrypting, and decrypting messages when people send and receive mail
Creating a new Mail Extension
When you create a new Mail extension, you will be asked which types (among the ones above) your extension will be. This will enable/disable capabilities on your extension accordingly.
The principal class of your extension must conform to the MEExtension
protocol.
MEExtension
exposes optional handler methods for each of the four types of extensions- All the methods in
MEComposeSessionHandler
have aMEComposeSession
argument which provides information about a compose window. Mail creates a uniqueMEComposeSession
instance for every Mail compose window. Each window has aMEMessage
property that exposes various details of the message being edited.
Compose extensions
Four ways your extension can interact with a Mail compose window:
- validate/annotate recipient email addresses; as the user is editing them
- present a view controller with additional context about the message being composed; the view controller must be a subclass of
MEExtensionViewController
- set additional headers on outgoing messages
- validate/alert the user of errors in the message before it is sent
For MEComposeSession
you will need to provide a MEComposeIcon
and MEComposeIconToolTip
entries in your extension Info.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>MEComposeSession</key>
<dict>
<key>MEComposeIcon</key> // 👈🏻
<string>lock</string>
<key>MEComposeIconToolTip</key> // 👈🏻
<string>Recipient Validation Extension</string>
</dict>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.email.extension</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).MailExtension</string>
</dict>
</dict>
</plist>
Example on how to validate recipient addresses:
func annotateAddressesForSession(_ session: MEMailComposeSession) async -> [String: MEAddressAnnotation] {
var annotations: [String: MEAddressAnnotation] = [:]
// Iterate through all the recipients in the message.
for address in session.mailMessage.allRecipientAddresses {
// Annotate invalid recipients with an error.
if address != "[email protected]" {
let message = "example.com is not a valid domain"
let annotation = MEAddressAnnotation.error(withLocalizedDescription: message)
annotations[address] = annotation
}
}
return annotations
}
Action extensions
Three types:
- mark messages as read and/or flag messages
- move to other standard system mailboxes (e.g., Junk, Trash or Archive)
- apply colors on messages in the message list
- Your action extension must implement
MEMessageActionHandler
'sdecideAction(for:completionHandler:)
decideAction(for:completionHandler:)
is called with aMEMessage
argument- Mail calls your handler's decideAction for message for every new message that it downloads before it is even visible in the inbox.
Content blocking extensions
- Your content blocking extension must implement an object conforming to
MEContentBlocker
- The content rule lists are specified using the same syntax as Safari content blockers, e.g.:
[
{
"action": { "type": "block" },
"trigger": { "url-filter": "*.acme.*" }
},
...
]
Message security extensions
- Your content blocking extension must implement an object conforming to
MEMessageSecurityHandler
- each time the sender or recipients change, Mail will call the
getEncodingStatus(for:completionHandler:)
method on the extension's message security handler - when the message is sent, Mail will take the RFC822 message data and pass it to the extension
- the extension will sign and encrypt the message as needed, and return the signed and encrypted RFC822 data back to Mail. Mail will then send this data to the outgoing server