対象:
Swift4

HTTP GETとPOST(Swift)

アプリからサーバにHTTPでGET、あるいはPOSTリクエストすることは結構あると思う。しかし、それはiOSだとなかなか面倒だったりする。早速GETから。

HTTPリクエストに必要なものはURLRequestとURLSessionの2つである。まずはURLを指定してURLRequestを生成する。次に、URLSessionだが、Webページを取得する等の単純なGETならsharedメソッドから共有のセッションを取得すれば良いだろう。

そして、取得したセッションのdataTaskメソッドにURLRequestを渡して呼び出し、生成されたタスクのresumeメソッドを呼び出せばGETリクエストできる。

1
2
3
4
5
6
7
8
9
10
11
12
let url = URL(string: "http://192.168.0.xx:8080/")
let request = URLRequest(url: url!)
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
    if error == nil, let data = data, let response = response as? HTTPURLResponse {
        // HTTPヘッダの取得
        print("Content-Type: \(response.allHeaderFields["Content-Type"] ?? "")")
        // HTTPステータスコード
        print("statusCode: \(response.statusCode)")
        print(String(data: data, encoding: String.Encoding.utf8) ?? "")
    }
}.resume()

ただし、接続先がhttpsでなくhttpの場合は、セキュアでないアクセスとしてATSにブロックされてしまうので以下も必要となる。ここでは、すべてのhttpのアクセスを許可しているが、セキュリティ的に問題があるかも知れないので、実際には必要なドメインのみ許可しよう。ATSの詳細についてはこちらをご参照いただければと思う。

1
2
3
4
5
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true>
</true></dict>

dataTaskメソッドに渡すcompletionHandlerにはHTTPリクエスト完了時、Data、URLResponse、及びErrorの3つの引数が渡される。DataはHTTPリクエストが成功した場合にサーバから取得したデータ(Body)、URLResponseはHTTPヘッダやHTTPステータスコード、Errorはエラーがなければnilである。

completionHandler(クロージャ)の中ではHTTPレスポンスのうち、Body部分がData(バイト列)として渡される。このDataからHTMLを得るためには、エンコードを指定してStringに変換する必要がある。また、URLResponseはHTTPURLResponseにキャストすることによってstatusCodeを得ることができる。

GETだけでなく、もちろんPOSTも可能だ。GETとの違いはURLRequestのhttpMethodプロパティで"POST"を指定することと、httpBodyプロパティにPOSTするデータを設定することである。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let url = URL(string: "http://192.168.0.xx:8080/")
var request = URLRequest(url: url!)
// POSTを指定
request.httpMethod = "POST"
// POSTするデータをBodyとして設定
request.httpBody = "os=iOS&version=11&language=日本語".data(using: .utf8)
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
    if error == nil, let data = data, let response = response as? HTTPURLResponse {
        // HTTPヘッダの取得
        print("Content-Type: \(response.allHeaderFields["Content-Type"] ?? "")")
        // HTTPステータスコード
        print("statusCode: \(response.statusCode)")
        print(String(data: data, encoding: .utf8) ?? "")
    }
}.resume()

このPOSTデータをSinatra(Ruby)で書いたサーバ側で受け取った結果は以下である。

os=iOS
version=11
language=日本語
192.168.0.27 - - [28/Jan/2018:09:14:22 +0900] "POST / HTTP/1.1" 200 19 0.0009
192.168.0.27 - - [28/Jan/2018:09:14:22 JST] "POST / HTTP/1.1" 200 19
- -> /

もちろん、サーバからテキストだけでなくJSONを受け取ることもできる。GETで受け取ったJSON形式のDataをJSONSerialization.jsonObjectメソッドに渡して呼びだ出せば、結果をDictionaryで得ることもできる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let url = URL(string: "http://192.168.0.xx:8080/json")
let request = URLRequest(url: url!)
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
    if error == nil, let data = data, let response = response as? HTTPURLResponse {
        // HTTPヘッダの取得
        print("Content-Type: \(response.allHeaderFields["Content-Type"] ?? "")")
        // HTTPステータスコード
        print("statusCode: \(response.statusCode)")
        // JSONからDictionaryへ変換
        let dic = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
        print("name: \(dic["name"] as! String)")
        print("price: \(dic["price"] as! Int)")
    }
}.resume()

Xcodeのコンソールに表示された結果は以下である。

Content-Type: application/json
statusCode: 200
name: ラーメン
price: 500

確認に使用したRubyのスクリプト(httpserver.rb)も載せておく。これをruby httpserver.rb -o 0.0.0.0 -p 8080で起動した。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require 'sinatra'
 
get "/" do
  "Hello, World!"
end
 
post "/" do
  params.each do |key, value|
    puts("#{key}=#{value}")
  end
  "Hello, World!(POST)"
end
 
get "/json" do
  content_type :json
  { name: "ラーメン", price: 500 }.to_json
end
(2018/01/28)

新着情報
【オープンソースソフトウェア環境構築】Apple silicon Macで開発環境を構築
【Rust Tips】Actix webでJSONをPOSTする
【Rust Tips】コマンドライン引数を取得する

Copyright© 2004-2019 モバイル開発系(K) All rights reserved.
[Home]