Alamofireで取得したデータをTableViewで表示する


3年以上ぶりに、iOSの開発を行い始めた。今の時代は`swift4` なのね。

## 開発環境
OS : MacOS High Sierra Version 10.13.6 
Xcode : Version 9.4.1

## Server

サーバサイドは、`JSON`データを`Ruby on Rails`のように、DBスキーマから簡単に返すことができるので、`Elixir`言語で書かれた `Phoexnix framework` を利用しています。
その辺りは、後日に。

## 取得データ
リクエスト送信先から返ってくる`JSON`データは下記の感じ。

```
{
  "users" : [
    {
      "option" : "システムエンジニア",
      "age" : 46,
      "name" : "田島 啓之",
      "id" : 1
    },
    {
      "option" : "経理",
      "age" : 49,
      "name" : "都築 奏子",
      "id" : 2
    },
    {
      "option" : "ネットワークエンジニア",
      "age" : 45,
      "name" : "中村 栄人",
      "id" : 3
    },
    {
      "option" : "総務",
      "age" : 38,
      "name" : "千葉 博子",
      "id" : 4
    }
  ]
}

```

## ソースコード解説

`ListViewController.swift`のコードを説明する。

まずは、必要なライブラリを`import`する。ここでは、`Alamofire `と`SwiftyJSON `を利用する。

```
import UIKit
import Alamofire
import SwiftyJSON
```

次に、`Swift4`から、`Codable`というもので、構造体とJSONデータを簡単にマッピングできるようになったらしいので、`Codable`を利用して下記のような構造体を定義する。
これは、`JSON`データのネスト構造を見てわかるように、Users構造体の中に、User構造体のリストが定義されている構造となる。

```
struct User : Codable{
    let id : Int
    let name: String
    let age : Int
    let option : String
}
struct Users : Codable{
    let users : [User]
}
```

`ListViewController`クラスは、`UITableViewDelegate `と`UITableViewDataSource`を採用する。特に問題はない。

```
class ListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
```

各メソッドから参照可能なように、Users構造体を定義する。初期化時には値が決まらないため、nilを許すため、オプショナル型としている。

```
    var users : Users?
```

`viewDidLoad()`においてUIの初期化を行う。

```
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tableView : UITableView!
        tableView = UITableView(frame: view.frame, style: .grouped)
        tableView.delegate = self
        tableView.dataSource = self
        
        view.addSubview(tableView)

```


`Alamofire`の主要処理部分。まずは、対象サーバへリクエストを送信して、`JSON`で受け取る。レスポンスが`nil`の場合は、そのまま`return`。

レスポンスが`nil`でない場合は、`JSONDecoder`で、`Users`構造体へマッピングする。

`tableView.reloadData()`は、`Alamofire`で取得するデータは非同期のため、初回は、`tableView`は空のため、読み込みが完了した時点で、`tableView`をリロードする必要がある。

```
        //Alamofire
        Alamofire.request("http://localhost:4000/user")
            .responseJSON{res in
                guard let json = res.data else{
                    return
                }
                self.users = try! JSONDecoder().decode(Users.self, from: json)
                
            tableView.reloadData()
        }

```

ここでは、リストの数を返す必要がある。通信が非同期のため、この時点では構造体はnilなので、初回は0を返す必要がある。リロードされた時に、Users構造体にデータはセットされているので、`users.count` でデータ数が取得できる。

```
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let cnt = self.users?.users.count{
            return cnt
        }
        return 0
    }
```

ここでは、セルのデータを設定している。やはりここも、初回はnilのため、`let if` で、nilチェックを行っている。
データがあれば、cell.textLabel.text を名前を設定する。

```
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "CELL")
        if let user = self.users?.users[indexPath.row]{
            cell.textLabel?.text = user.name
        }
        return cell
    }

```

## 最後に

ここに記したコードは、初回時のデータ取得から表示までのサンプル的な最低限のコードです。もう少しリファクタリングして、構造を設計する必要がありますが、とりあえず動作原理をシンプルに理解するために記載しました。

間違い等がございましたら、ご指摘願います。

下記、全ソースコードです。

```ListViewController.swift
import UIKit
import Alamofire
import SwiftyJSON

struct User : Codable{
    let id : Int
    let name: String
    let age : Int
    let option : String
}
struct Users : Codable{
    let users : [User]
}

class ListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    var users : Users?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let tableView : UITableView!
        tableView = UITableView(frame: view.frame, style: .grouped)
        tableView.delegate = self
        tableView.dataSource = self
        
        view.addSubview(tableView)
        
        //Alamofire
        Alamofire.request("http://localhost:4000/user")
            .responseJSON{res in
                guard let json = res.data else{
                    return
                }
                self.users = try! JSONDecoder().decode(Users.self, from: json)
                
                //Debug
                print("--- User JSON ---")
                print(JSON(json))
                print("--- User Info ---")
                for user in self.users!.users{
                    print("NAME:" + user.name)
                    print("AGE:" + String(user.age))
                }
                
            tableView.reloadData()
        }
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let cnt = self.users?.users.count{
            return cnt
        }
        return 0
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "CELL")
        if let user = self.users?.users[indexPath.row]{
            cell.textLabel?.text = user.name
        }
        return cell
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
}

```

***Lovly Swift!!!***