我在从 osx swift 应用程序发送邮件时遇到问题。为了发送邮件,我使用了下面的代码
import Foundation
import Cocoa
class sendemail : NSObject, NSSharingServiceDelegate{
func sendEmail() throws
{
print("enter email sending")
let body = "This is an email for auto testing throug code."
let shareItems = [body] as NSArray
let service = NSSharingService(named: NSSharingServiceNameComposeEmail)
service?.delegate = self
service?.recipients = ["[email protected]"]
let subject = "Vea Software"
service?.subject = subject
service?.performWithItems(shareItems as [AnyObject])
}
}
我找到了此链接的来源:https://www.veasoftware.com/posts/send-email-in-swift-xcode-62-os-x-1010-tutorial
但是它不起作用。
我还尝试按照以下说明从终端发送邮件:
http://www.developerfiles.com/how-to-send-emails-from-localhost-mac-os-x-el-capitan/
它说:
postfix/postfix-script: fatal: the Postfix mail system is not running
请帮助我。
我可以从已配置的 Mac 邮件应用程序手动发送邮件。
我正在使用
xcode 7.3, osx el captain and swift 2.2
现代斯威夫特:
func sendEmail(to recipients: [String], subject: String, body: String) {
let service = NSSharingService(named: .composeEmail)!
service.recipients = recipients
service.subject = subject
service.perform(withItems: [body])
}
// Usage
sendEmail(
to: ["[email protected]"],
subject: "Vea software",
body: "This is an email for auto testing through code."
)
本地用户必须拥有 Mail.app 设置的帐户。您还需要一个
NSApplication
的实例来运行它。无法在 CLI 应用程序中执行此操作。如果您在控制台中看到以下错误,则意味着您没有活动的 NSApplication
实例。
[default] 0 is not a valid connection ID
这对我有用:
import Cocoa
class SendEmail: NSObject {
static func send() {
let service = NSSharingService(named: NSSharingServiceNameComposeEmail)!
service.recipients = ["[email protected]"]
service.subject = "Vea software"
service.performWithItems(["This is an email for auto testing through code."])
}
}
用途:
SendEmail.send()
斯威夫特 4.2:
class SendEmail: NSObject {
static func send() {
let service = NSSharingService(named: NSSharingService.Name.composeEmail)!
service.recipients = ["[email protected]"]
service.subject = "Email Subject"
service.perform(withItems: ["Email Content"])
}
}
用途:
SendEmail.send()
我们通过不使用共享服务而是使用 postfix 实用程序解决了这个“困难的方法”。请注意,postfix 必须运行并正确配置,下面的代码才能生效。
为什么要这么艰难?嗯,这是一个稍微更“通用”的解决方案,应该可以在 macOS 以外的其他平台上运行(或者可以轻松修改)。而且,它不依赖于“邮件”。
请注意,这也使用我们的日志工具,但您应该能够轻松提取所需的代码。
/// 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)")
}
}
}
应使用格式如下的字符串调用此例程:
let email: String =
"""
To: \(emailAddress)
From: \(fromAddress)
Content-Type: text/html;
Subject: Confirm Account Creation at \(domain.name)\n
\(message)
"""
您可以使用 SMTPKitten 从用 Swift (XCode) 编写的简单控制台应用程序以编程方式发送电子邮件,并使用您的 smtp 提供商,包括 iCloud。
import Foundation
import SMTPKitten
let smtpHost = "smtp.mail.me.com"
let myEmail = "[email protected]"
let password = "abc-def-ghi-lmn"
try await SMTPClient.withConnection(
to: smtpHost,
port: 587,
ssl: .startTLS(configuration: .default)
) { client in
// 1. Authenticate
try await client.login(
user: myEmail,
password: password
)
// 2. Send emails
print("About to send email!")
let mail = Mail(
from: MailUser(name: "First Last", email: myEmail),
to: [
MailUser(name: "Destination User", email: "[email protected]")
],
subject: "Message from my terminal app!",
content: .plain("Welcome ...")
)
try await client.sendMail(mail)
print("email sent successfully!")
}
目前,您不能只使用常规电子邮件密码,而是需要从 Apple ID 帐户页面注册特定应用程序专用密码。如果您使用 Google,则需要类似的过程,但端口号和 ssl 配置可能不同。