# HTTPによる通信

Swiftは、Foundationフレームワークを使うことでHTTPによる通信を行うことができます。通常、APIサーバーなどに接続するために Alamofire/AlamofireAFNetworking/AFNetworking などのライブラリを使うことが一般的ですが、Foundationの URLSession でもGETやPOSTなどによる通信を実現することができます。

# URLSessionConfiguration

URLSessionConfiguration は、URLSession を使用するときの動作とポリシーを定義します。URLSessionConfigurationの設定は常に最初に行う必要があります。データをアップロードまたはダウンロードするとき、この設定を使用して、タイムアウト値、キャッシュ、クッキーの使用や端末回線での接続の許可など、を制御します。

URLSessionConfiguration の設定は3種類あります。

# default

default は、キャッシュ、認証情報、クッキーなどを使用するデフォルトの設定オブジェクトを作成します。URLSessionConfiguration.default で設定を作成します。

# ephemeral

ephemeral は、default の設定と似ていますが、セッションに関するデータをディスクに書き込まないでメモリに保存します。プライベートモードで動作し、セッションが破棄されるとキャッシュなどのデータも無くなります。URLSessionConfiguration.ephemeral で設定を作成します。

# background

background は、バックグラウンドでコンテンツのアップロードとダウンロードを実行することができます。URLSessionConfiguration.backgroundSessionConfiguration() メソッドで設定を作成します。

# URLSession

URLSession クラスは、URLで指定されたエンドポイントからデータをダウンロードしたり、アップロードすることができます。それぞれのリクエストはタスクと呼ばれ、URLSessionはそれをまとめるクラスになります。

HTTP通信

# タスク

URLSessionクラスが取り扱うリクエストのタスクは、次のような種類があります。

# URLSessionDataTask

URLSessionDataTask はリクエストの基本のタスクで、Dataオプジェクトを経由してデータの送受信を行います。iOSアプリだとバックグランドの処理ができないので主に小さいデータのやりとりをする際に使います。

# URLSessionUploadTask

URLSessionUploadTask はデータのアップロードを行います。URLSessionDataTaskと異なり、バックグランドの処理が可能で、時間を要する通信の際に使います。

# URLSessionDownloadTask

URLSessionDownloadTask は、サーバーからデータをダウンロードします。URLSessionUploadTask同様に、バックグランドの処理が可能で、ダウンロードしたデータをファイルに保存します。

# GET

GETメソッドを用いた基本的な通信は、DataTaskを使います。

簡単な例として、iTunes Search APIにアクセスして情報を取得してみましょう。

Playgroundで実行するため次の設定をします。

import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

次に、URLSessionConfiguration の設定をします。今回は、default の設定を使います。

// URLSessionConfiguration の設定をする
let config = URLSessionConfiguration.default

作成した設定を元に URLSession セッションのインスタンスを生成します。

// URLSession セッションのインスタンスを生成
let session = URLSession(configuration: config)

次に、URLを作ります。

// URLを作成
var urlComponents = URLComponents(string: "https://itunes.apple.com/search")
urlComponents?.query = "media=music&entity=song&term=justin"
let url = urlComponents?.url

最後に、.dataTask でGET通信をしてデータを取得します。

import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

// URLSessionConfiguration の設定をする
let config = URLSessionConfiguration.default

// URLSession セッションのインスタンスを生成
let session = URLSession(configuration: config)

// URLを作成
var urlComponents = URLComponents(string: "https://itunes.apple.com/search")
urlComponents?.query = "media=music&entity=song&term=justin"
let url = urlComponents?.url

// GET通信を実行
let task = session.dataTask(with: url!) { data, response, error in
  if let error = error {
    print(error.localizedDescription)
    return
  }

  guard let data = data, let response = response as? HTTPURLResponse else {
    print("データがありませんでした。")
    return
  }

  if response.statusCode == 200 {
    do {
      let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)
      print(json)
    } catch {
      print("不正なデータです")
    }
    // 処理...
  }
}
task.resume()

以下が実行結果です。取得したデータをJSON化させています。

{
    resultCount = 50;
    results =     (
                {
            artistId = 466532;
            artistName = Korn;
            ...
        },
        ...
    )
}        

URLSessionConfigurationの設定が default の場合は、明示的にURLSessionConfiguration.defaultを設定しなくてもURLSessionクラスの shared を使って同じようにデータを取得することができます。

// URLSession.shared を使う
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
  // ...
}
task.resume()

# POST

# DataTask

DataTaskでは、POSTメソッドを使った通信をすることもできます。POST通信をする際には、メソッドの設定やボディの情報を変更するため URLRequest型 を使う必要があります。

次の例では、http://localhost:8080/users に対してPOST通信をしてUserオブジェクトをJSON形式で送信します。

まずは、URLRequest でリクエストを生成します。

var url = URL(string: "http://localhost:8080/users")!

// URLを渡してリクエストを作成する
var request = URLRequest(url: url)

次に、POSTメソッドとcontent-typeを設定します。

// POSTメソッドを追加する
request.httpMethod = "POST"

// content-type を application/json に設定する
request.addValue("application/json", forHTTPHeaderField: "content-type")

次に、ボディに送信するためのデータを設定します。request.httpBody に送信したいデータをJSON形式にエンコードして代入します。

// データをを用意する
struct User: Codable {
  let id: String
  let name: String
}

let user = User(id: "1", name: "Taro")

do {
  // JSON形式にエンコードして、データをhttpBodyにセットする
  request.httpBody = try JSONEncoder().encode(user)
} catch {
  print("不正なデータです")
}

最後に、データをPOST送信します。

import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

var url = URL(string: "http://localhost:8080/users")!

// URLを渡してリクエストを作成する
var request = URLRequest(url: url)

// POSTメソッドを追加する
request.httpMethod = "POST"

// content-type を application/json に設定する
request.addValue("application/json", forHTTPHeaderField: "content-type")

// データをを用意する
struct User: Codable {
  let id: String
  let name: String
}

let user = User(id: "1", name: "Taro")

do {
  // JSON形式にエンコードして、データをhttpBodyにセットする
  request.httpBody = try JSONEncoder().encode(user)
} catch {
  print("不正なデータです")
}

// データをPOST送信する
let task = URLSession.shared.dataTask(with: request) { data, response, error in
  if error != nil {
    print("エラー")
    return
  }

  print("成功")
}
task.resume()

# UploadTask

UploadTaskでもPOST通信をすることができます。アップロード可能なデータは、画像やファイルなど多様なデータをアップロードすることができます。

DataTask同様に、URLRequest でリクエストを生成します。

var url = URL(string: "http://localhost:8080/users")!

// URLを渡してリクエストを作成する
var request = URLRequest(url: url)

次に、POSTメソッドとcontent-typeを設定します。

// POSTメソッドを追加する
request.httpMethod = "POST"

// content-type を application/json に設定する
request.addValue("application/json", forHTTPHeaderField: "content-type")

そして、URLSession.shared.uploadTask() でデータをアップロードします。

import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

var url = URL(string: "http://localhost:8080/users")!

// URLを渡してリクエストを作成する
var request = URLRequest(url: url)

// POSTメソッドを追加する
request.httpMethod = "POST"

// content-type を application/json に設定する
request.addValue("application/json", forHTTPHeaderField: "content-type")

// データをを用意する
struct User: Codable {
  let id: String
  let name: String
}

let user = User(id: "1", name: "Taro")
var uploadData = try JSONEncoder().encode(user)

// uploadTaskでJSON形式のデータを送信する
let task = URLSession.shared.uploadTask(with: request, from: uploadData) { data, response, error in
  if error != nil {
    print("エラー")
    return
  }

  print("成功")
}
task.resume()