web-dev-qa-db-ja.com

URLSession dataTaskを再試行するためのパターン?

私はiOS/Swift開発にかなり慣れておらず、REST APIにいくつかのリクエストを行うアプリに取り組んでいます。「メッセージ」を取得する呼び出しの1つのサンプルを次に示します。

func getMessages() {

    let endpoint = "/api/outgoingMessages"

    let parameters: [String: Any] = [
        "limit" : 100,
        "sortOrder" : "ASC"
    ]

    guard let url = createURLWithComponents(endpoint: endpoint, parameters: parameters) else {
        print("Failed to create URL!")
        return
    }

    do {
        var request = try URLRequest(url: url, method: .get)

        let task = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in

            if let error = error {
                print("Request failed with error: \(error)")
                // TODO: retry failed request
            } else if let data = data, let response = response as? HTTPURLResponse {                
                if response.statusCode == 200 {
                    // process data here
                } else {
                    // TODO: retry failed request
                }
            }
        }

        task.resume()

    } catch {
        print("Failed to construct URL: \(error)")
    }
}

もちろん、このリクエストはさまざまな理由で失敗する可能性があります(サーバーに到達できない、リクエストがタイムアウトした、サーバーが200以外のものを返すなど)。リクエストが失敗した場合は、おそらく次の試行までに遅れて、再試行できるようにしたいと思います。 Appleのドキュメントにはこのシナリオに関するガイダンスはありませんでしたが、SOに関するいくつかの関連する議論を見つけました。残念ながら、どちらも数年前のもので、Objective-Cで使用したことはありません。 Swiftでこのようなことを行うための一般的なパターンや実装はありますか?

7
bmt22033

この質問は意見に基づいた側で放送されており、かなり広いですが、私はほとんどが似ているに違いないので、ここに行きます。

Iの変更をトリガーするデータ更新の場合:

(たとえば、データが入力されたテーブル、または画像の読み込み)一般的な経験則は、次のように、邪魔にならない方法でユーザーに通知することです。

そして、プルトゥリフレッシュコントロールまたはリフレッシュボタンがあります。

ユーザーのアクションや動作に影響を与えないバックグラウンドデータの更新の場合:

コードによっては、リクエスト結果に再試行カウンターを簡単に追加できますが、これには注意して、よりインテリジェントなロジックを構築します。たとえば、次のステータスコードがある場合、処理を変えることができます。

  • 5xx:サーバーに問題があります。再試行を30秒または1分遅らせたい場合がありますが、それが3〜4回発生した場合は、バックエンドのハンマー処理を停止する必要があります。

  • 401:認証されたユーザーがAPIを呼び出すことを許可されなくなった可能性があります。これを再試行する必要はまったくありません。代わりに、ユーザーをログアウトして、次にユーザーがアプリを使用するときに、再認証を求めるプロンプトを表示することをお勧めします。

  • ネットワークのタイムアウト/接続の喪失:接続が再確立されるまで、再試行は関係ありません。到達可能性ハンドラーの周りにロジックを記述して、次にネットワーク接続が利用可能になったときにアクションを実行するためのバックグラウンド要求をキューに入れることができます。

そして最後に、コメントで触れたように、通知駆動型のバックグラウンドアプリの更新を確認することをお勧めします。これは、サーバーをポーリングして変更を確認する代わりに、アプリがフォアグラウンドで実行されていない場合でもアプリ自体を更新するように通知を送信できる場所です。十分に賢い場合は、アプリが受信を確認するまでサーバーにアプリへの通知を繰り返すことができます。これにより、接続障害やその他の無数のサーバー応答エラーコードが一貫した方法で解決されます。

5
brandonscript

再試行を処理するための3つの方法を分類します。

  1. 到達可能性の再試行
    • 到達可能性は、「ネットワーク接続が変更されたときに知らせてください」という空想的な言い方です。 Appleにはこのためのスニペットがいくつかありますが、見るのは面白くありません—私の推奨はAshley Millの 到達可能性 置換のようなものを使用することです。
    • 到達可能性に加えて、Appleは waitsForConnectivity (iOS 11+)プロパティを提供します。これはURLSession構成で設定できます。これを設定すると、タスクがネットワーク接続を待機しているときにURLSessionDataDelegateを介してアラートが表示されます。この機会を利用して、オフラインモードを有効にしたり、ユーザーに何かを表示したりできます。
  2. 手動で再試行
    • リクエストをいつ再試行するかをユーザーが決定できるようにします。これは、「プルして更新」ジェスチャ/ UIを使用して最も一般的に実装されていると思います。
  3. 時限再試行
    • 数秒待ってから、再試行してください。
    • From Apple Docs:URLセッションのライフサイクル ...ただし、アプリは[リクエスト]をすぐに再試行しないでください。代わりに、到達可能性APIを使用してサーバーが到達可能かどうかを判断し、到達可能性が変更されたという通知を受信した場合にのみ新しい要求を行う必要があります。
2
jarrodparkes