#14 Airtableを利用してiOSアプリを開発する

今回は、以前作成した地図クイズアプリを基に発展させて行います。このアプリはAirtableからデータを取得し、UITableViewに地図の名称と画像を表示します。ネットワークリクエストにはURLSessionを使用し、サードパーティライブラリのKingfisherを使ってネットワーク画像を読み込みます。

![Simulator Screen Recording - iPhone 15 Pro - 2024-05-20 at 16.52.57.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3703986/bdc48ca5-a62d-61f3-87e6-58e3197880a6.gif)

## Airtableデータベースの設定
まず、Airtableでデータベース（ベース）を作成し、必要なテーブルとフィールドを追加しました。この例では、mapInfoという名前のテーブルがあり、以下のフィールドを含んでいます：

###### areaName：エリアの名称
###### areaInfo：エリアの情報（例：紹介リンク）
###### areaImage：エリアの画像（添付ファイルタイプ）

![airtable.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3703986/47047258-703c-50d6-0910-3f04c6af8765.png)

## データ構造の定義
Airtableのデータ構造に対応するSwift構造体を定義します。Struct.swiftという名前のファイルを作成します：
```
import Foundation

struct MapInfo: Codable {
    let records: [Record]
}

struct Record: Codable {
    let id, createdTime: String
    let fields: Fields
}

struct Fields: Codable {
    let areaImage: [AreaImage]
    let areaInfo: String
    let areaName: String
}

struct AreaImage: Codable {
    let id: String
    let width, height: Int
    let url: String
    let filename: String
}

```
## Airtable APIの実装
Airtableからデータを取得し、それをMapInfo構造体にデコードする必要があります。NetworkManager.swiftという名前のファイルを作成します：

```
import Foundation

let apiKey = APIKey.default
var mapInfoData: MapInfo?

func fetchData(completion: @escaping (MapInfo?) -> Void) {
    if let url = URL(string: "https://api.airtable.com/v0/app1MfmI8sP5PmaSp/mapInfo") {
        var request = URLRequest(url: url)
        // 認証ヘッダーを設定
        request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
        
        // HTTPリクエストを送信
        URLSession.shared.dataTask(with: request) { data, response, error in
            if let data {
                // JSONデータを印刷
                let decoder = JSONDecoder()
                do {
                    // JSONデータをMapInfo構造体にデコード
                    let result = try decoder.decode(MapInfo.self, from: data)
                    mapInfoData = result // デコード結果をグローバル変数に保存
                    completion(result) // 完了クロージャを呼び出し
                } catch {
                    print(error)
                    completion(nil) // デコードに失敗した場合、nilを返す
                }
            } else if let error {
                print(error)
                completion(nil) // リクエストが失敗した場合、nilを返す
            }
        }.resume()
    }
}

```

## TableViewの設定
次に、Airtableから取得したデータを表示するためにUITableViewを設定します。MapInfoTableViewController.swiftという名前のファイルを作成します：

```
import UIKit
import Kingfisher

class MapInfoTableViewController: UITableViewController {
    var mapInfoData: MapInfo?

    override func viewDidLoad() {
        super.viewDidLoad()
        fetchData { data in
            DispatchQueue.main.async {
                if let data = data {
                    // データを他の処理に使用
                    self.mapInfoData = data
                    self.tableView.reloadData() // テーブルビューを再読み込み
                } else {
                    // データ読み込み失敗の処理
                    let alert = UIAlertController(title: "Error", message: "Failed to load data", preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
                    self.present(alert, animated: true, completion: nil)
                }
            }
        }
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return mapInfoData?.records.count ?? 0
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MapInfoTableViewCell", for: indexPath) as! MapInfoTableViewCell

        if let record = mapInfoData?.records[indexPath.row] {
            cell.mapName.text = record.fields.areaName
            cell.url = record.fields.areaInfo
            
            if let url = record.fields.areaImage.first?.url {
                cell.mapImage.kf.setImage(with: URL(string: url))
            }
        }
        return cell
    }
}

```
## TableViewセルの設定
MapInfoTableViewCell.swiftという名前のファイルを作成します：
```
import UIKit
import SafariServices

class MapInfoTableViewCell: UITableViewCell, SFSafariViewControllerDelegate {

    @IBOutlet weak var mapName: UILabel!
    @IBOutlet weak var mapImage: UIImageView!
    var url: String?

    override func awakeFromNib() {
        super.awakeFromNib()
    }
    
    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }

    func openUrl() {
        guard let urlString = url, let url = URL(string: urlString) else { return }
        let safariVC = SFSafariViewController(url: url)
        safariVC.delegate = self
        UIViewController().present(safariVC, animated: true, completion: nil)
    }
}

```
## 結語
このアプリを通して、Airtable APIと連携して地図情報を取得し、それをTableViewに表示する方法を学びました。また、Kingfisherライブラリを利用してネットワーク画像を読み込む方法も学びました。さらに、TableViewのデリゲートとデータソースを設定し、データの表示と管理を実現しました。次に、APIのリンクをボタンにしてSafariで表示することに挑戦したいと思います。