iOS 操作扩展中的后台任务
Background task in iOS action extension
我正在为一个应用程序开发一个操作扩展,它为用户准备一些数据,然后使用 MailCore2 将此信息发送到 SMTP 服务器。准备数据的速度非常快,但发送电子邮件可能需要一些时间(取决于其大小)。这就是为什么我正在寻找一种方法来处理后台发送活动。但这会导致一些不同解决方案的问题:
使用 URLSession。这是在 iOS 扩展中处理大量上传或下载的第一种方法。但问题是 MailCore2 并没有使用 URLSessions 将数据传输到邮件服务器。所以这是不可用的。或者是否有可能以某种方式 'wrap' 在 URLSession 中进行此调用?
使用UNUserNotificationCenter并在数据准备好后从分机发送本地通知。想法是在收到这个通知后,在主应用中开始发送任务。问题:通知 userNotificationCenter didReceive
方法仅在用户单击通知时调用。一个简单的徽章通知不调用委托方法。
使用后台获取。这在模拟器中运行良好,但在 iOS 设备上连接到 SMTP 服务器时出现错误。我遇到了与 github.com/MailCore/mailcore2/issues/252 中描述的相同的 HELO 问题。对于这个问题,解决方案应该是 MCOSMTPSession.isUseHeloIPEnabled = true
,但这可能不适用于所有服务器。所以也不是一个完美的解决方案。
任何想法,如何处理这样的任务?或者如何处理上述解决方案之一?
编辑: 因为这个问题还没有收到任何答案:有什么不清楚或需要哪些额外信息?
从您的回答来看,您的困难在于您需要允许后台活动。您可以通过 following a tutorial such as this one.
看来你不能从一个extension.Refer here(Some APIs Are Unavailable to App Extensions)
在您提出的解决方案中,我的建议是尝试使用 silent push notifications ,您可以在准备数据阶段调用 api ,然后从服务器发送静默推送并在静默推送到达时执行后台任务。
最有用的解决方案是使用推送通知。
我在自己的应用中使用的解决方案:
- 创建一个简单的服务器,它可以检索一个简单的请求:设备令牌和您想要唤醒设备的时间。
- 当应用程序进入后台模式时,向服务器发送请求以稍后唤醒设备。例如。 5 minutes/20 分钟等
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.
}
}
在服务器端使用简单的时间或循环。
缺点,
- 需要互联网
- 它并不完美。当电池电量不足时,后台模式会受到限制。
不要忘记设置项目:
经过多次测试和失败后,我找到了以下解决方案来在扩展程序的后台执行长时间执行的任务。这按预期工作,即使扩展已经完成:
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.
的小摘要中对其进行了详细描述
我正在为一个应用程序开发一个操作扩展,它为用户准备一些数据,然后使用 MailCore2 将此信息发送到 SMTP 服务器。准备数据的速度非常快,但发送电子邮件可能需要一些时间(取决于其大小)。这就是为什么我正在寻找一种方法来处理后台发送活动。但这会导致一些不同解决方案的问题:
使用 URLSession。这是在 iOS 扩展中处理大量上传或下载的第一种方法。但问题是 MailCore2 并没有使用 URLSessions 将数据传输到邮件服务器。所以这是不可用的。或者是否有可能以某种方式 'wrap' 在 URLSession 中进行此调用?
使用UNUserNotificationCenter并在数据准备好后从分机发送本地通知。想法是在收到这个通知后,在主应用中开始发送任务。问题:通知
userNotificationCenter didReceive
方法仅在用户单击通知时调用。一个简单的徽章通知不调用委托方法。使用后台获取。这在模拟器中运行良好,但在 iOS 设备上连接到 SMTP 服务器时出现错误。我遇到了与 github.com/MailCore/mailcore2/issues/252 中描述的相同的 HELO 问题。对于这个问题,解决方案应该是
MCOSMTPSession.isUseHeloIPEnabled = true
,但这可能不适用于所有服务器。所以也不是一个完美的解决方案。
任何想法,如何处理这样的任务?或者如何处理上述解决方案之一?
编辑: 因为这个问题还没有收到任何答案:有什么不清楚或需要哪些额外信息?
从您的回答来看,您的困难在于您需要允许后台活动。您可以通过 following a tutorial such as this one.
看来你不能从一个extension.Refer here(Some APIs Are Unavailable to App Extensions)
在您提出的解决方案中,我的建议是尝试使用 silent push notifications ,您可以在准备数据阶段调用 api ,然后从服务器发送静默推送并在静默推送到达时执行后台任务。
最有用的解决方案是使用推送通知。 我在自己的应用中使用的解决方案:
- 创建一个简单的服务器,它可以检索一个简单的请求:设备令牌和您想要唤醒设备的时间。
- 当应用程序进入后台模式时,向服务器发送请求以稍后唤醒设备。例如。 5 minutes/20 分钟等
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.
}
}
在服务器端使用简单的时间或循环。
缺点,
- 需要互联网
- 它并不完美。当电池电量不足时,后台模式会受到限制。
不要忘记设置项目:
经过多次测试和失败后,我找到了以下解决方案来在扩展程序的后台执行长时间执行的任务。这按预期工作,即使扩展已经完成:
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.
的小摘要中对其进行了详细描述