appfrosch
appfrosch

Reputation: 1356

Compose new mail from Swift script

I need a script to compose a new mail with given script arguments. Here's what I got so far:

#!/usr/bin/env xcrun swift
import Foundation
import AppKit

func printHelpMessage() {
    let helpMessage = "Script expects the following arguments: <recepient> <subject>"
    print(helpMessage)
}

func composeMail() {
    guard let service = NSSharingService(named: .composeEmail) else { return }
    service.recipients = [recepient]
    service.subject = subject

    service.perform(withItems: ["Test Mail Body"])

}

guard CommandLine.argc == 3 else {
    printHelpMessage()
    exit(0)
}

let recepient = CommandLine.arguments[1]
let subject = CommandLine.arguments[2]

composeMail()

The error message I get is

2020-06-10 12:05:34.938140+0200 ComposeMail[58079:3848327] [default] 0 is not a valid connection ID.
2020-06-10 12:05:34.952445+0200 ComposeMail[58079:3848327] [default] 0 is not a valid connection ID.
2020-06-10 12:05:34.952805+0200 ComposeMail[58079:3848327] [default] 0 is not a valid connection ID.

Could this be System Integrity Protection?

Upvotes: 0

Views: 199

Answers (2)

appfrosch
appfrosch

Reputation: 1356

Turns out that when using AppKit classes, an NSApplication instance has to be implemented with an AppDelegate, even though this is a script.

Here's what is working for me now, it's quick and dirty, but might be a starting point:

import AppKit

let app = NSApplication.shared

class AppDelegate: NSObject, NSApplicationDelegate {

    var recepient:String?
    var subject: String?
    var mailBody: String?
    var attachment: String?

    func applicationDidFinishLaunching(_ notification: Notification) {

        guard CommandLine.argc == 5 else {
            printHelpMessage()
            exit(0)
        }

        recepient = CommandLine.arguments[1]
        subject = CommandLine.arguments[2]
        mailBody = CommandLine.arguments[3]
        attachment = CommandLine.arguments[4]

        composeMail()

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            exit(1)
        }
    }

    func composeMail() {
        guard let recepient = recepient,
            let subject = subject,
            let mailBody = mailBody,
            let attachment = attachment,
            let service = NSSharingService(named: .composeEmail)
            else {
                exit(2)
        }

        let attmtUrl = URL(fileURLWithPath: attachment).absoluteURL

        service.recipients = [recepient]
        service.subject = subject

        service.perform(withItems: [mailBody, attmtUrl as URL])
    }

    func printHelpMessage() {
        let helpMessage = "Script expects the following arguments: <recepient> <subject> <mail body> </path/to/attachment>"
        print(helpMessage)
    }
}

let delegate = AppDelegate()
app.delegate = delegate
app.run()

Thanks @anthosr (see answer below) for pushing me into the right direction!

Disclaimer: please be aware that attaching will not work with MS Outlook – for reasons that are beyond me, the attachment is being ignored. With Apple Mail it works though.

Upvotes: 0

anthosr
anthosr

Reputation: 11

I just hit the same issue and traced it back to not having NSApplication initialized.

The NSApplication developer page mentions that this needs to be initialized before using most AppKit classes. I built a simple app, inserted my email code, and it works now. I have not yet figured out how to accomplish this as a pure command-line app though, which is my goal and it looks like yours as well.

Upvotes: 1

Related Questions