应用程序运行时在后台使用 SMTP Mailcore2 发送邮件

Posted

技术标签:

【中文标题】应用程序运行时在后台使用 SMTP Mailcore2 发送邮件【英文标题】:Sending mails with SMTP Mailcore2 in Background while App is running 【发布时间】:2019-10-03 01:38:04 【问题描述】:

我正在编写一个 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)  [...] 

但是只有当我关闭/最小化应用程序并且如果 BackgroundTask 未完成应用程序将冻结并且我收到此错误时才会发送数据:XPC 连接中断 我也遇到了问题,因为我需要等待 sendOperation 返回,但由于这是异步的,我构建了一个解决方法来保持线程运行并在之后处理我的“如果成功则......”。更多完整代码:

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()
        
    

【问题讨论】:

【参考方案1】:

我将 smtpSession.timeout = 100 降低到 3(秒),现在它不再阻塞 UI。更多的是破解而不是解决方案,但它确实有效。

【讨论】:

以上是关于应用程序运行时在后台使用 SMTP Mailcore2 发送邮件的主要内容,如果未能解决你的问题,请参考以下文章

React Native:关闭应用程序时在 iOS 上运行后台任务的最佳方法?

如何在应用程序运行 ios 时在后台进行服务调用

如何在启动时在后台运行django服务器?

收到推送通知时在后台执行代码

如何在python中运行其他代码时在后台运行循环

Android 连接时在后台发送数据