我正在开发一个应用的动作扩展,为用户准备一些数据,然后使用MailCore2将此信息发送到SMTP服务器。准备数据的速度非常快,但发送电子邮件可能需要一些时间(取决于其大小)。这就是为什么我正在寻找一种在后台处理发送活动的方法。但这导致了不同解决方案的一些问题:
使用 URLSession 。这是在iOS扩展中处理大型上传或下载的第一种方法。但问题是MailCore2不使用URLSessions将数据传输到邮件服务器。所以这不可用。或者是否有可能以某种方式将此调用“包装”在URLSession中?
使用 UNUserNotificationCenter 并在数据准备后从扩展程序发送本地通知。我们的想法是在收到此通知后在主应用程序中启动发送任务。问题:仅当用户单击通知时才会调用通知userNotificationCenter didReceive
方法。简单的徽章通知不会调用委托方法。
使用后台抓取。这在模拟器中工作正常,但在iOS设备上连接到SMTP服务器时出错。我遇到了与github.com/MailCore/mailcore2/issues/252中描述的相同的HELO问题。对于此问题,解决方案应为MCOSMTPSession.isUseHeloIPEnabled = true
,但这可能不适用于所有服务器。所以也不是一个完美的解决方案。
任何想法,如何处理这样的任务?或者如何处理上述解决方案之一?
编辑,因为此问题尚未收到任何答案:哪些不清楚或需要哪些其他信息?
答案 0 :(得分:4)
您似乎 不能 在扩展程序中在后台安排长时间运行的任务。请参阅here(Some APIs Are Unavailable to App Extensions)
在您建议的解决方案中,我的建议是尝试使用 silent push notifications ,您可以在准备数据阶段调用api,然后从服务器发送静默推送并执行静默推送到达时执行后台任务。
答案 1 :(得分:2)
从您的回答来看,您的困难在于您需要允许后台活动。您可以通过following a tutorial such as this one完成此操作。
答案 2 :(得分:2)
最有用的解决方案是使用推送通知。 我在自己的应用程序中使用的解决方案:
func applicationWillEnterForeground(_ application: UIApplication) {
内的AppDelegate
。例如,
@UIApplicationMain
class AppDelegate: UIResponder {
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid
func application(_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if UIApplication.shared.applicationState != .active {
doFetch(completionHandler: completionHandler)
} else {
// foreground here
completionHandler(UIBackgroundFetchResult.noData)
}
}
func application(_ application: UIApplication,
performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
doFetch(completionHandler: completionHandler)
}
private func doFetch(completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
sendTheRequestToWakeApp()
backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
self?.endBackgroundTask()
}
/// do work here
completionHandler(UIBackgroundFetchResult.noData)
}
private func endBackgroundTask() {
print("Background task ended.")
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = UIBackgroundTaskInvalid
}
private func sendTheRequestToWakeApp() {
/// Implement request using native library or Alamofire. etc.
}
}
在服务器端使用简单的时间或循环。
缺点
别忘了设置项目:
答案 3 :(得分:1)
经过多次测试和失败之后,我发现以下解决方案可在扩展程序的后台执行长期执行的任务。即使扩展名已经完成,它也能按预期工作:
func performTask()
{
// Perform the task in background.
let processinfo = ProcessInfo()
processinfo.performExpiringActivity(withReason: "Long task") { (expired) in
if (!expired) {
// Run task synchronously.
self.performLongTask()
}
else {
// Cancel task.
self.cancelLongTask()
}
}
}
此代码使用ProcessInfo.performExpiringActivity()
在另一个线程中执行任务。 performLongTask()
中的任务必须同步执行,这一点很重要。当到达块的末尾时,线程将终止并结束任务的执行。
主应用程序中也有类似的方法。在background tasks in iOS的小摘要中对此进行了详细说明。