TechSwiftUIJapanese

[SwiftUI]Webアクセスの仕組み – URL Loading System

Tech

以前の投稿でWeb上のAPIからデータを取得する部分のサンプルコードをご紹介しましたが、そもそものSwiftUIがWebにアクセスする仕組みについて正しく理解をしたいと思いましたので、本投稿で整理したいと思います。

URL Loading System

[ads]

SwiftUIにおいてインターネット通信を行う際にはこの仕組みを用いることになります。

You use a URLSession instance to create one or more URLSessionTask instances, which can fetch and return data to your app, download files, or upload data and files to remote locations. 

https://developer.apple.com/documentation/foundation/url_loading_system

上記にあるように、URLSessionを一つ作成し、その配下にURLSessionTaskを1つ以上作成する、というのが基本的なコンセプトのようです。以下の図がわかりやすそうです。

Figure showing two scenarios, default browsing and private browsing, each with a URL Session creating multiple URL Session Tasks. In the default browsing case, the URL Session contains a default configuration. In the private browsing case, it contains an ephemeral configuration.
https://developer.apple.com/documentation/foundation/url_loading_system

つまり、流れとしては以下のようになります。

  1. URLSessionを作成
  2. タスク毎にURLSessionTaskを作成

ここで、URLSession、URLSessionTaskについて概要を整理していきます。

URLSession

URLSessionには以下のタイプがあるようです

  • 基本的なリクエスト用のshared session(Configuration Objectを持たない)
  • カスタマイズ可能な作成したsession(Configuration Objectを持つ)→ URLSessionConfigurationを活用

なお、公式ドキュメントでは、まずはConfiguration Objectを持たないshared sessionから始めることを推奨しています。これは、後述するshared sessionの利用方法が最もシンプルである、ということも大きな理由のようです。

URLSessionConfiguration

”カスタマイズ可能なsession”を作成する際にこちらを活用することになり、カスタマイズのパターンは以下の3つが用意されています。

  1. Default session:delegateを用いた段階的なデータ取得が可能
  2. Ephemeral session:キャッシュ、クッキー、認証情報をディスクに書き込まない
  3. Background session:アプリケーションが動作していないときの、バックグラウンドでのコンテンツのアップロードやダウンロードが可能

URLSessionTask

URL Session Taskに関しては、4種類のタスクが用意されている旨、URLSessionのドキュメントに記載されています。

  1. Data tasks: NSDataオブジェクトを使用してデータ送受信。ちょっとした(特にインタラクティブな)リクエスト向きタスク
  2. Upload tasks: バックグラウンドでのアップロードに対応した、ファイル送信向きタスク
  3. Download tasks: バックグラウンドでのダウンロード、アップロードに対応した、ファイル送受信向きタスク
  4. WebSocket tasks: RFC6455のWebSocketプロトコルを使った、TCP、TLSでのメッセージ交換向きタスク

ファイルのアップロードだけであれば、Upload Task、ファイルのダウンロードも必要であれば、Download Taks、サーバからクライアントに対して更新のポーリングが必要であれば、WebSocket Task、それ以外であれば、Data Task、といった使い分けでしょうか。

RFC6455

RFC 6455 - The WebSocket Protocol (日本語訳)

task state

URLSessionTaskは以下の4つの状態を持ち、タスクが生成された時点ではsuspended状態になるため、使用開始するためには、忘れずにresumeを用いてrunning状態にすることが重要です。状態別の概要は以下の通りです。

  • running: 現在実行中のタスク。タイムアウトの影響を受ける
  • suspended: タスクが再開されるまで処理を行わない。タイムアウトにはならない
  • canceling: cancelメッセージを受信したタスク。タイムアウトにはならない
  • completed: cancel以外で完了したタスク。errorプロパティはnil。タイムアウトにはならない

task resume

Newly-initialized tasks begin in a suspended state, so you need to call this method to start the task.

https://developer.apple.com/documentation/foundation/urlsessiontask/1411121-resume

実装に向けて

[ads]

以上のことから、下記投稿で紹介したような単純な通信は以下の組み合わせで実現するのがよさそうです。

  • URLSession: shared session
  • URLSessionTaks: Data task

shared sessionの利用

Unlike the other session types, you don’t create the shared session; you merely access it by using this property directly. As a result, you don’t provide a delegate or a configuration object.

https://developer.apple.com/documentation/foundation/urlsession/1409000-shared

上記の通り、shared sessionであれば、sessionの作成は不要で、プロパティにアクセスするだけでよいとのことです。

URLSessionTaskの利用

The URLSessionTask class is the base class for tasks in a URL session. Tasks are always part of a session; you create a task by calling one of the task creation methods on a URLSession instance. The method you call determines the type of task.

https://developer.apple.com/documentation/foundation/urlsessiontask

URLSessionTaskはURLSessionの一部に含まれていて、使いたいタスクタイプを指定すればよさそうです。

Data taskの指定

dataTaskの定義は以下のようになっていますので、URLオブジェクトを渡してやればよさそうです。

func dataTask(with url: URL) -> URLSessionDataTask

ここまででURLSession, URLSessionTaskの概要と、その利用方法が概ね理解できたと思います。次に実際の実装の検討に移りますが、AppleのDeveloperサイトにちょうどよい記事がありましたので、そちらを参考にすすめていこうと思います。

Fetching Website Data into Memory

[ads]

こちらは、Data taskを用いてウェブサイトからデータを取得するサンプルとなっています。少し説明を読んでみます。

The simplest way to fetch data is to create a data task that uses a completion handler. With this arrangement, the task delivers the server’s response, data, and possibly errors to a completion handler block that you provide.

https://developer.apple.com/documentation/foundation/url_loading_system/fetching_website_data_into_memory

CompletionHandlerを使うことで、Data taskは以下を取得してくれるようです。

  • サーバからのレスポンス
  • データ
  • 発生したエラー

Completion Handlerについて

The Swift Programming Language: Redirect

サンプルからこの部分に該当するコードは以下になります。

let url = URL(string: "https://www.example.com/")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in

下記投稿で作成したコードに、data, response, errorを出力する部分を追加して中身を見てみることにしました。

func getData() {
        guard let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1") else { return }
        URLSession.shared.dataTask(with: url) {(data, response, error) in
            do {
                print("data", data as Any)
                print("response", response as Any)
                print("error", error as Any)
data Optional(83 bytes)
response Optional(<NSHTTPURLResponse: 0x6000023991c0> { URL: https://jsonplaceholder.typicode.com/todos/1 } { Status Code: 200, Headers {
    Age =     (
        8728
    );
...
error nil

dataはAPIのレスポンスデータ部分、responseはHTMLのレスポンスがすべて含まれている感じです。

まとめ

[ads]

特別複雑でないWeb通信を実現するためには、以下を実施すればよさそうです。仕組みがわかってしまえば数行のコードでWebにアクセスできますね。

  1. shred sessionにアクセスし、dataTaskを生成
  2. dataTask生成時のCompletion handler内で、data, response, errorを取得
    1. responseの内容をチェック
    2. errorの内容をハンドリング
    3. dataの処理(デコードなど)
Ads