ユーザー向けにデータを準備し、MailCore2を使用してこの情報をSMTPサーバーに送信するアプリのアクション拡張機能に取り組んでいます。データの準備は非常に高速ですが、メールの送信には時間がかかる場合があります(サイズによって異なります)。そのため、送信アクティビティをバックグラウンドで処理する方法を探しています。しかし、これにより、さまざまなソリューションでいくつかの問題が発生します。
URLSessionを使用します。これは、iOS拡張機能で大きなアップロードまたはダウンロードを処理するための最初の方法です。ただし、問題は、MailCore2がURLSessionsを使用してデータをメールサーバーに送信しないことです。したがって、これは使用できません。または、この呼び出しをURLSessionで「ラップ」することは可能ですか?
UNUserNotificationCenterを使用して、データの準備後に拡張機能からローカル通知を送信します。この通知を受け取った後、メインアプリで送信タスクを開始するというアイデアです。問題:通知userNotificationCenter didReceive
メソッドは、ユーザーが通知をクリックしたときにのみ呼び出されます。単純なバッジ通知は、デリゲートメソッドを呼び出しません。
Background Fetchを使用します。これはシミュレーターでは正常に機能していますが、iOSデバイスではSMTPサーバーに接続するときにエラーが発生します。 github.com/MailCore/mailcore2/issues/252 で説明されているのと同じHELOの問題に遭遇しました。この問題の場合、解決策はMCOSMTPSession.isUseHeloIPEnabled = true
ですが、これはすべてのサーバーで機能するとは限りません。したがって、完璧な解決策でもありません。
何かアイデア、そのようなタスクを処理する方法は?または、上記の解決策の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()
のタスクが同期的に実行されることが重要です。ブロックの終わりに達すると、スレッドは終了し、タスクの実行を終了します。
同様のアプローチがメインアプリでも機能しています。 iOSのバックグラウンドタスク の簡単な要約で詳細に説明されています。
あなたは拡張機能からバックグラウンドで長時間実行されているタスクをスケジュールすることができないようです。参照- ここ(一部のAPIはアプリ拡張機能で使用できません)
あなたが提案した解決策の中で、私の提案はサイレントプッシュ通知を使用してみることです、あなたは準備でAPIを呼び出すことができますデータステージでは、サーバーからサイレントプッシュを送信し、サイレントプッシュが到着したときにバックグラウンドタスクを実行します。
あなたの反応から判断すると、あなたの困難はあなたがバックグラウンド活動を許可する必要があるということです。 このようなチュートリアルに従う でそれを行うことができます。
最も便利な解決策は、プッシュ通知を使用することです。自分のアプリケーションで使用したソリューション:
func applicationWillEnterForeground(_ application: UIApplication) {
inside 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.
}
}
サーバー側では、単純な時間またはループを使用します。
短所、
プロジェクトを設定することを忘れないでください: