LoginSignup
21
17

More than 5 years have passed since last update.

SwiftのAlamofire+CodableでAPIクライアントの作成からUnitTestまでを実装する

Last updated at Posted at 2019-01-07

概要

iOSエンジニアの通信処理の練習用にこんなAPIを作成しました。
herokuで無料枠でデプロイしていますのでお金はかからないはずです。
その代わりにスリープモードなのでアクセスしてもレスポンスが遅いので、2,3回アクセスして起こしてもらう必要があります。

スクリーンショット 2018-12-30 20.48.03.png

{"items":[{"id":1,"first_name":"Taro","last_name":"Tanaka","title":"first_data","create_at":"2018-12-30T15:03:01.012345"},{"id":2,"first_name":"Hanako","last_name":"Yamada","title":"second-data","create_at":"2018-12-31T18:11:01.012345"}]}

[{"id":1,"first_name":"Taro","last_name":"Tanaka","title":"first_data","create_at":"2018-12-30T15:03:01.012345"},{"id":2,"first_name":"Hanako","last_name":"Yamada","title":"second-data","create_at":"2018-12-31T18:11:01.012345"}]

{"id":1,"first_name":"Taro","last_name":"Tanaka","title":"first_data","create_at":"2018-12-30T15:03:01.012345"}

{"id":2,"first_name":"Hanako","last_name":"Yamada","title":"second-data","create_at":"2018-12-31T18:11:01.012345"}

上記のURLに接続するとJSONが出力されます。

つまり、こちらがAPIとなります。

これらをそれぞれネイティブ側(iOS)でパースしてみましょう。

環境

ライブラリ・環境 バージョン
Xcode 10.1
Swift 4.2
Alamofire 4.8.0

モデルクラスの設計と実装

今回はモデルクラスの命名をTaskにしました。
今回は全てをデコードするのではなくidfirstlastをそれぞれデコードする。

Task.swift
struct Task: Codable {
    let id: Int
    let first: String
    let last: String

    enum CodingKeys: String, CodingKey {
        case id
        case first = "first_name"
        case last = "last_name"
    }
}

を使うとパラメータと変数名が一致するためCodingKeysが必要なくなります。
こちらは私なりの配慮です。

スクリーンショット 2018-12-30 20.34.59.png

スクリーンショット 2018-12-30 20.35.45.png

easy版を使う場合

Task.swift
struct Task: Codable {
    let id: Int
    let first: String
    let last: String
}

以上でモデルクラスの作成が完了します。

Controller側でAPIを呼び出してデコードする

ViewController.swift
import UIKit
import Alamofire

class ViewController: UIViewController {

    private var items: [Task]?

    override func viewDidLoad() {
        super.viewDidLoad()
        requestFirst()
        requestItems()
    }

    private func requestFirst() {
        Alamofire.request("https://todo-practice-app.herokuapp.com/first").response { response in
            guard let data = response.data else {
                return
            }
            let decoder = JSONDecoder()
            do {
                let task: Task = try decoder.decode(Task.self, from: data)
                print(task)
            } catch {
                print(error)
            }
        }
    }

    private func requestItems() {
        Alamofire.request("https://todo-practice-app.herokuapp.com/items").response { response in
            guard let data = response.data else {
                return
            }
            let decoder = JSONDecoder()
            do {
                let tasks: [Task] = try decoder.decode([Task].self, from: data)
                print(tasks)
            } catch {
                print(error)
            }
        }
    }
}

requestItems()を呼び出し場合

[TodoApp.Task(id: 1, first: "Taro", last: "Tanaka"), TodoApp.Task(id: 2, first: "Hanako", last: "Yamada")]

requestFirst()を呼び出し場合

MobileGestalt.c:890: MGIsDeviceOneOfType is not supported on this platform.
Task(id: 1, first: "Taro", last: "Tanaka")

さて、ここまではよくQiitaで見かける記事であります。

次にこれのModelのUnitTestを作ります。

ModelクラスのUnitTestを作成する

プロジェクトファイルの構成は下記の通りに実装します。

スクリーンショット 2018-12-30 21.48.21.png

ResourcesフォルダにJSONのオブジェクトを入れる形式が好みです。

one_task.json
{
    "id": 1,
    "first_name": "Taro",
    "last_name": "Tanaka",
    "title": "first_data",
    "create_at": "2018-12-30T15:03:01.012345"
}

テストを書きます。

TestTask.swift
import XCTest
@testable import TodoApp

class TestTask: XCTestCase {

    func test_Task()
    {
        guard let jsonObject = createDataObject() else {
            XCTFail("Dataの生成に失敗")
            return
        }
            let decoder = JSONDecoder()
        guard let task = try? decoder.decode(Task.self, from: jsonObject) else {
            XCTFail("Taskの生成に失敗")
            return
        }
        XCTAssertEqual(task.id, 1)
        XCTAssertEqual(task.first, "Taro")
        XCTAssertEqual(task.last, "Tanaka")
    }

    private func createDataObject() -> Data?
    {
        let testBundle = Bundle(for: type(of: self))
        let path = testBundle.url(forResource: "one_task", withExtension: "json")
        let data = try? Data(contentsOf: path!, options: .uncached)
        return data
    }
}

あとはユニットテストを実行してSuccessが表示されたら完了です。

これで無事にMockからJSONDecoderでCodableでデコードがしっかりできていることがわかりました。

21
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
17