在应用程序 运行 时在后台使用 SMTP Mailcore2 发送邮件
Sending mails with SMTP Mailcore2 in Background while App is running
我正在编写一个 iOS 应用程序,它需要在用户添加新数据(条码扫描仪、扫描码)后在后台发送定期更新。我找不到任何方法通过 SMTP 和 mailcore2 在后台发送邮件而没有问题和限制。
我已经尝试过:
用后台抓取试过了
https://developer.apple.com/documentation/uikit/core_app/managing_your_app_s_life_cycle/preparing_your_app_to_run_in_the_background/updating_your_app_with_background_app_refresh
但这是非常不规则的,需要一些时间才能触发。
在AppDelegate.swift中:
func applicationDidEnterBackground(_ application: UIApplication) {
BackgroundTask.run(application: application) { [...] }
}
但是只有当我 close/minimize 应用程序时才会发送数据,如果 BackgroundTask 未完成,应用程序将冻结并且我收到此错误:XPC 连接中断
我也 运行 遇到问题,因为我需要等待 sendOperation 到 return 但因为这是异步的,所以我构建了一个解决方法来保持线程 运行ning 并处理我的 "if success else ..."然后。更多完整代码:
typealias CompletionHandler = (Error?) -> Void
/// Provides syncronous access to results returned by
/// asynchronous processes with completion handlers
class SyncMaker {
var result: Error? = nil
/// Generates a synchronous-compliant completion handler
func completion() -> CompletionHandler{
return {
(error: Error?) in
// Store result, return control
self.result = error
CFRunLoopStop(CFRunLoopGetCurrent())
}
}
// Perform task (that must use custom completion handler) and wait
func run(_ task: @escaping () -> Void) -> Error? {
task()
CFRunLoopRun()
return result
}
}
func applicationDidEnterBackground(_ application: UIApplication) {
BackgroundTask.run(application: application) { backgroundTask in
if (scanManager.hasDataToSend()) {
let smtpSession = MCOSMTPSession()
let settings: Settings = scanManager.getSettings()
smtpSession.hostname = settings.credMailServer
smtpSession.username = settings.credMailSource
print(settings.credMailSource)
smtpSession.password = settings.credMailPassword
smtpSession.port = UInt32(settings.credMailPort)
[…] Setting auth and connection typ
smtpSession.isCheckCertificateEnabled = false
smtpSession.timeout = 100
smtpSession.connectionLogger = {(connectionID, type, data) in
if data != nil {
if let string = NSString(data: data!, encoding: String.Encoding.utf8.rawValue){
NSLog("Connectionlogger: \(string)")
}
}
}
let builder = MCOMessageBuilder()
builder.header.to = [MCOAddress(displayName: settings.credMailDest, mailbox: settings.credMailDest)!]
builder.header.from = MCOAddress(displayName: settings.credMailSource, mailbox: settings.credMailSource)
builder.header.subject = "ScanLMS"
builder.htmlBody = ""
guard let attachment = MCOAttachment(data: scanManager.getSendData().data(using: .ascii), filename: "scans.txt") else {
print("Cant init attachment!")
backgroundTask.end()
return
}
attachment.mimeType = "text/plain"
builder.addAttachment(attachment)
let rfc822Data = builder.data()
let sendOperation = smtpSession.sendOperation(with: rfc822Data!)
var sendingError: Bool = true
print("Trying to send mail...")
if (sendOperation != nil) {
print("Starting sendOperation...")
let syncMaker = SyncMaker() //full class on top of code
let result = syncMaker.run {
sendOperation?.start(
syncMaker.completion())
}
if (result != nil) {
print("Error sending email: \(result!)")
} else {
sendingError = false
print("Successfully sent email!")
}
} else {
print("Cant init sendOperation")
sendingError = true
}
if (sendingError) {
print("Error, returning")
} else {
print("Send done")
print("Updating scanManager with send data...")
scanManager.updateSendData()
print("Done, returning")
}
} else {
print("No new send data")
}
backgroundTask.end()
}
}
我将 smtpSession.timeout = 100 降低到 3(秒),现在它不再阻塞 UI。更多的是黑客而不是解决方案,但它有效。
我正在编写一个 iOS 应用程序,它需要在用户添加新数据(条码扫描仪、扫描码)后在后台发送定期更新。我找不到任何方法通过 SMTP 和 mailcore2 在后台发送邮件而没有问题和限制。
我已经尝试过:
用后台抓取试过了 https://developer.apple.com/documentation/uikit/core_app/managing_your_app_s_life_cycle/preparing_your_app_to_run_in_the_background/updating_your_app_with_background_app_refresh 但这是非常不规则的,需要一些时间才能触发。
在AppDelegate.swift中:
func applicationDidEnterBackground(_ application: UIApplication) {
BackgroundTask.run(application: application) { [...] }
}
但是只有当我 close/minimize 应用程序时才会发送数据,如果 BackgroundTask 未完成,应用程序将冻结并且我收到此错误:XPC 连接中断 我也 运行 遇到问题,因为我需要等待 sendOperation 到 return 但因为这是异步的,所以我构建了一个解决方法来保持线程 运行ning 并处理我的 "if success else ..."然后。更多完整代码:
typealias CompletionHandler = (Error?) -> Void
/// Provides syncronous access to results returned by
/// asynchronous processes with completion handlers
class SyncMaker {
var result: Error? = nil
/// Generates a synchronous-compliant completion handler
func completion() -> CompletionHandler{
return {
(error: Error?) in
// Store result, return control
self.result = error
CFRunLoopStop(CFRunLoopGetCurrent())
}
}
// Perform task (that must use custom completion handler) and wait
func run(_ task: @escaping () -> Void) -> Error? {
task()
CFRunLoopRun()
return result
}
}
func applicationDidEnterBackground(_ application: UIApplication) {
BackgroundTask.run(application: application) { backgroundTask in
if (scanManager.hasDataToSend()) {
let smtpSession = MCOSMTPSession()
let settings: Settings = scanManager.getSettings()
smtpSession.hostname = settings.credMailServer
smtpSession.username = settings.credMailSource
print(settings.credMailSource)
smtpSession.password = settings.credMailPassword
smtpSession.port = UInt32(settings.credMailPort)
[…] Setting auth and connection typ
smtpSession.isCheckCertificateEnabled = false
smtpSession.timeout = 100
smtpSession.connectionLogger = {(connectionID, type, data) in
if data != nil {
if let string = NSString(data: data!, encoding: String.Encoding.utf8.rawValue){
NSLog("Connectionlogger: \(string)")
}
}
}
let builder = MCOMessageBuilder()
builder.header.to = [MCOAddress(displayName: settings.credMailDest, mailbox: settings.credMailDest)!]
builder.header.from = MCOAddress(displayName: settings.credMailSource, mailbox: settings.credMailSource)
builder.header.subject = "ScanLMS"
builder.htmlBody = ""
guard let attachment = MCOAttachment(data: scanManager.getSendData().data(using: .ascii), filename: "scans.txt") else {
print("Cant init attachment!")
backgroundTask.end()
return
}
attachment.mimeType = "text/plain"
builder.addAttachment(attachment)
let rfc822Data = builder.data()
let sendOperation = smtpSession.sendOperation(with: rfc822Data!)
var sendingError: Bool = true
print("Trying to send mail...")
if (sendOperation != nil) {
print("Starting sendOperation...")
let syncMaker = SyncMaker() //full class on top of code
let result = syncMaker.run {
sendOperation?.start(
syncMaker.completion())
}
if (result != nil) {
print("Error sending email: \(result!)")
} else {
sendingError = false
print("Successfully sent email!")
}
} else {
print("Cant init sendOperation")
sendingError = true
}
if (sendingError) {
print("Error, returning")
} else {
print("Send done")
print("Updating scanManager with send data...")
scanManager.updateSendData()
print("Done, returning")
}
} else {
print("No new send data")
}
backgroundTask.end()
}
}
我将 smtpSession.timeout = 100 降低到 3(秒),现在它不再阻塞 UI。更多的是黑客而不是解决方案,但它有效。