send email from swift osx application

拜拜、爱过 提交于 2019-12-03 12:41:40

This works for me:

class SendEmail: NSObject {
    static func send() {
        let service = NSSharingService(named: NSSharingServiceNameComposeEmail)!
        service.recipients = ["abc@dom.com"]
        service.subject = "Vea software"

        service.performWithItems(["This is an email for auto testing through code."])
    }
}

Usage:

SendEmail.send()

Swift 4.2:

class SendEmail: NSObject {
static func send() {
    let service = NSSharingService(named: NSSharingService.Name.composeEmail)!
    service.recipients = ["email@yourEmail.eu"]
    service.subject = "Email Subject"

    service.perform(withItems: ["Email Content"])
    }
}

Usage: SendEmail.send()

We solved this "the hard way" by not using the shared services, but the postfix utility. Note that postfix must be running and correctly configured before the following code can take effect.

Why do this the hard way? well, it is a slightly more 'universal' solution that should work (or can be modified easily) on other platforms than macOS. Also, it does not depend on 'mail'.

Note that this also uses our Log facility, but you should be able to easily extract the code you need.

/// This queue/thread will be used to transfer the mails.

fileprivate let mailQueue = DispatchQueue.global(qos: .background)


/// Sends an email using the postfix unix utility.
///
/// - Note: Ths function only works if postfix is running (and set up correctly)
///
/// - Parameters:
///   - mail: The email to be transmitted. It should -as a minimum- contain the To, From and Subject fields.
///   - domainName: The domain this email is sent from, used for logging purposes only.

public func sendEmail(_ mail: String, domainName: String) {


    // Do this on a seperate queue in the background so this operation is non-blocking.

    mailQueue.async { [mail, domainName] in

        // Ensure the mail is ok

        guard let utf8mail = mail.data(using: .utf8), utf8mail.count > 0 else {
            Log.atDebug?.log("No mail present")
            return
        }

        let options: Array<String> = ["-t"] // This option tells sendmail to read the from/to/subject from the email string itself.

        let errpipe = Pipe() // should remain empty
        let outpipe = Pipe() // should remain empty (but if you use other options you may get some text)
        let inpipe = Pipe()  // will be used to transfer the mail to sendmail


        // Setup the process that will send the mail

        let process = Process()
        if #available(OSX 10.13, *) {
            process.executableURL = URL(fileURLWithPath: "/usr/sbin/sendmail")
        } else {
            process.launchPath = "/usr/sbin/sendmail"
        }
        process.arguments = options
        process.standardError = errpipe
        process.standardOutput = outpipe
        process.standardInput = inpipe


        // Start the sendmail process

        let data: Data
        do {

            Log.atDebug?.log("\n\(mail)")


            // Setup the data to be sent

            inpipe.fileHandleForWriting.write(utf8mail)


            // Start the sendmail process

            if #available(OSX 10.13, *) {
                try process.run()
            } else {
                process.launch()
            }


            // Data transfer complete

            inpipe.fileHandleForWriting.closeFile()


            // Set a timeout. 10 seconds should be more than enough.

            let timeoutAfter = DispatchTime(uptimeNanoseconds: DispatchTime.now().uptimeNanoseconds + UInt64(10000) * 1000000)


            // Setup the process timeout on another queue

            DispatchQueue.global().asyncAfter(deadline: timeoutAfter) {
                [weak process] in
                Log.atDebug?.log("Sendmail Timeout expired, process \(process != nil ? "is still running" : "has exited already")")
                process?.terminate()
            }


            // Wait for sendmail to complete

            process.waitUntilExit()

            Log.atDebug?.log("Sendmail terminated")


            // Check termination reason & status

            if (process.terminationReason == .exit) && (process.terminationStatus == 0) {

                // Exited OK, return data

                data = outpipe.fileHandleForReading.readDataToEndOfFile()

                if data.count > 0 {
                    Log.atDebug?.log("Unexpectedly read \(data.count) bytes from sendmail, content: \(String(data: data, encoding: .utf8) ?? "")")
                } else {
                    Log.atDebug?.log("Sendmail completed without error")
                }

            } else {

                // An error of some kind happened

                Log.atError?.log("Sendmail process terminations status = \(process.terminationStatus), reason = \(process.terminationReason.rawValue )")

                let now = dateFormatter.string(from: Date())

                Log.atError?.log("Sendmail process failure, check domain (\(domainName)) logging directory for an error entry with timestamp \(now)")


                // Error, grab all possible output and create a file with all error info

                let e = errpipe.fileHandleForReading.readDataToEndOfFile()
                let d = outpipe.fileHandleForReading.readDataToEndOfFile()

                let dump =
                """
                Process Termination Reason: \(process.terminationReason.rawValue)
                Sendmail exit status: \(process.terminationStatus)
                Details:
                - Sendmail Executable  : /usr/sbin/sendmail
                - Sendmail Options     : -t
                - Sendmail Timeout     : 10,000 mSec
                - Sendmail Error output: \(e.count) bytes
                - Sendmail Output      : \(d.count) bytes
                Below the output of sendmail is given in the following block format:
                (----- Email input -----)
                ...
                (----- Standard Error -----)
                ...
                (----- Standard Out -----)
                ...
                (----- End of output -----)

                (----- Email input -----)
                \(mail)
                (----- Standard Error -----)
                \(String(bytes: e, encoding: .utf8) ?? "")
                (----- Standard Out -----)
                \(String(bytes: d, encoding: .utf8) ?? "")
                (----- End of output -----)
                """

                let errorFileName = "sendmail-error-log-" + now
                if
                    let errorFileUrl = Urls.domainLoggingDir(for: domainName)?.appendingPathComponent(errorFileName).appendingPathExtension("txt"),
                    let dumpData = dump.data(using: .utf8),
                    ((try? dumpData.write(to: errorFileUrl)) != nil) {
                } else {
                    Log.atError?.log("Cannot create sendmail error file, content is: \n\(dump)\n")
                }
            }

        } catch let error {

            Log.atError?.log("Exception occured during sendmail execution, message = \(error.localizedDescription)")
        }
    }
}

This routine should be called with a string formatted like:

    let email: String =
    """
    To: \(emailAddress)
    From: \(fromAddress)
    Content-Type: text/html;
    Subject: Confirm Account Creation at \(domain.name)\n
    \(message)
    """
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!