0.前提条件
私なりに、本テーマを扱う上で事前に理解しておいた方がいい内容をまとめました。
合わせて読めるようにリンク貼っておきます。
・https://qiita.com/Hiromu9915/items/5b255df7224346890681
1.はじめに
今回はAPI通信の理解について、まとめました。うまくまとめられているか不安な気持ちもありつつ、API通信を行う際の流れを理解できたかなと考えています。
どのような手順でAPI通信を実現させたのか、を個人的に振り返れるようまとめました。
2.Codableを活用したModel作成
まずはAPIが生きているか、JSON構造を確認します。
確認の仕方としては、
Mac>ターミナル
※下記を打ち込む
curl -H "Accept: application/json" https://icanhazdadjoke.com/
memo:コマンドの意味
・curl(カール) → サーバとデータをやりとりするためのツール。コマンドだけで、Webサイトにアクセスし中身を確認できる。
・-H → ヘッダー指定オプションコマンド 追加の条件(リクエストヘッダー)をつけるための記号。
・"Accept: application/json" → データを受け取る(Accept)ときは、JSON形式(application/json)でくださいと依頼している
※全体の確認したイメージとしては、「icanhazdadjoke.com というサイトにアクセスして、結果をJSON形式のデータとして受け取って表示して!」になるかなと。
下記のようにJSON形式で返ってくる事を確認します。
{
"id":"WLJJRKJeqjb",
"joke":"I used to work in a shoe recycling shop. It was sole destroying.",
"status":200}
上記の確認をしてから、
Xcodeに戻り、Modelを作成します。
Modelフォルダ配下を選択した状態で、
『Command + N』を押し、Swift.Fileを選択する。
SwiftはCodableプロトコルを採用しており、APIから届くJSONの各項目(Key)と名前を合わせるのがポイントです。
struct JokeData: Codable {
let id: String //文字列
let joke: String //文字列
let status: Int //整数
}
2-1.各型について
上記のStringやIntの種類について表にしました。
| 型の名前(Swift) | データの種類 | JSONでの例 | 主な用途・特徴 |
|---|---|---|---|
| String | 文字列 |
"London", "abc123"
|
テキスト情報。名前、ID、メッセージなど |
| Int | 整数 |
200, 800, 29
|
小数点のない数字。ステータスコード、個数、年齢 |
| Double | 浮動小数点数 |
21.5, 139.76
|
小数点を含む精密な数字。気温、緯度・経度 |
| Bool | 真偽値 |
true, false
|
2択のフラグ。「お気に入りか?」「成功したか?」など |
| [型名] | 配列(リスト) | [ { "id": 1 }, { "id": 2 } ] |
同じ種類のデータの詰め合わせ。天気リストなど |
| 独自構造体 | ネスト(入れ子) | "main": { "temp": 21 } |
JSONの中で { } でグループ化されている塊を受け取る |
| 型名? | オプショナル型 |
null または データあり |
データが「空(nil)」になる可能性がある場合に使用 |
Swiftは非常に安全性を重視する言語です。
「このデータは計算に使うのか(Int)」「画面に表示する文字なのか(String)」を明確に決めないと、プログラムを動かすことを許してくれません。
3.Managerの作成(通信ロジック)※API通信の肝
次は、先ほどと同様にModelフォルダの配下を選択し、Managerの作成実施。
①成功時は画面にジョークを表示させ、失敗時はエラー理由を教えてもらうルールを決める。
protocol JokeManagerDelegate {
func didUpdateJoke(_ jokeManager: JokeManager, joke: String) //通信成功した時のルール
func didFailWithError(error: Error) //通信失敗した時のルール
}
②Swiftで使用できる通信の型(URL)へ変更し、JSON形式でもらえるように準備・通信する。もし、送信できない場合はその旨を連絡する。
func fetchJoke() {
if let url = URL(string: jokeURL) {
var request = URLRequest(url: url)
// curl -H "Accept: application/json" の部分
request.addValue("application/json", forHTTPHeaderField: "Accept")
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
self.delegate?.didFailWithError(error: error!)
return
}
if let safeData = data {
// 3. 届いたデータを解析する
if let jokeString = self.parseJSON(safeData) {
self.delegate?.didUpdateJoke(self, joke: jokeString)
}
}
}
task.resume()
}
}
③機械語で届いたデータをSwiftで使えるようにする。
func parseJSON(_ jokeData: Data) -> String? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(JokeData.self, from: jokeData)
return decodedData.joke
} catch {
delegate?.didFailWithError(error: error)
return nil
}
}
}
続いて、表示させたいView Controller.swiftに戻り、
// Storyboardで配置したラベルと繋ぐ
@IBOutlet weak var jokeLabel: UILabel!
var jokeManager = JokeManager()
// 1. 報告先を自分(この画面)に設定する
jokeManager.delegate = self
// 2. ギャグを取りに行く
jokeManager.fetchJoke()
//元々のコードを壊さずに、ギャグ用の通信を受けられるルールを作り、
//ギャグが届いたら表示、ギャグの取得が難しい場合はエラーを表示させる
extension WeatherViewController: JokeManagerDelegate {
func didUpdateJoke(_ jokeManager: JokeManager, joke: String) {
// UIの更新は必ずメインスレッドで行う
DispatchQueue.main.async {
self.jokeLabel.text = joke
}
}
func didFailWithError(error: Error) {
print("ギャグ取得エラー: \(error)")
}
}
4.まとめ
APIの通信の全体像は以下であることを理解しました。
①どこの(URL)、どんな形式で(Header)頼むか決める
②通信を実行する(URLSession)
③届いたJSON(ただの文字列)をSwiftの型(構造体)に変換する
④取得したデータを画面に表示する(メインスレッド)
また、今回はAPI通信を学んだこともあり、
MVCモデルのModelの部分も触ることができました。