API通信について理解する

# 前提
API通信について学習内容の整理も含め、今回記事にしてみようと思います。

内容については、GithubAPIからGithubのプロフィール情報を取得してアプリ内で表示するプロジェクトを作成し、その中でAPI通信について説明していければと思います。

また、サードパーティ製のライブラリは使わずに、Foundationでの実装となります。
# 対象者
- Swiftの基礎文法について理解している方
- 通信処理について学んだことがない方
# API通信の流れ
1. **URLの構築**
1. **URLRequestの作成**
1. **タスクを生成し実行**

1. **レスポンスを受け取る**
1. **データをデコードする**

# 1. URLの構築
まずは、サーバーから情報を取得するためのURLを定義する必要があります。
GithubのAPIを使ってユーザー情報を取得してみましょう。

> https://api.github.com/users/ユーザー名

このURLにユーザー名を入力してネット上で検索してみると、

```
{
    "login": "ユーザー名",
    "id": 214322498,
    "node_id": "U_kgDOBsEp7g",
    "avatar_url": "https://avatars.githubusercontent.com/u/853352418?v=4",
    "gravatar_id": "",
    "url": "https://api.github.com/users/ユーザー名",
    "html_url": "https://github.com/ユーザー名",
    "followers_url": "https://api.github.com/users/ユーザー名/followers",

    ~~(以下略)~~
}
```
こんな感じで値が返ってくると思います。
これが今回API通信でサーバー上から取得したい値となります。
# 2. URLRequestの作成
まずは、URLを格納する定数を定義しましょう。
```swift
let url = URL(string: "https://api.github.com/users/ユーザー名")
```
リクエストを送る際は、このURLを**URLRequest型**に変換してあげる必要があります。
```swift
let request = URLRequest(url)
```
HTTPリクエストは`URLRequest`クラスを使って構築します。
生成されたリクエストにはHTTPメソッド(postやgetなど)やヘッダーなどのパラメータを付与することができます。
### HTTPメソッドを変更する場合
HTTPメソッドを変更したい場合は、`httpMethod`プロパティにメソッドを文字列型で渡します。
デフォルトでは、「GET」に設定されています。
```swift
request.httpMethod = "POST"
```

# 3. タスクを生成し実行
`URLSession`の`dataTaskメソッド`を使ってURLにアクセスし、レスポンスを取得します。
```swift
let task = URLSession.shared.dataTask(with: reqest, completionHandler:{ data, response, error in }
```
`URLSession`の`dataTask`では引数として**URLRequest型**のURLと、クロージャとして**completionHandler**を受け取ります。

**completionHandler**から受け取る値として、 それぞれ`data`, `response`, `error`を引数として受け取ります。

## 生成したタスクを実行する
`URLSessionDataTask`はインスタンス化しただけだと実行されないので、`resume()`メソッドを使って実行します。

```swift
task.resume()
```

# 4. レスポンスを受け取る
`dataTask`の処理の流れとして簡単に解説すると、
```swift
dataTask(with: reqest,completionHandler:{ data, response, error in }
```
1つ目の引数にURLを渡しサーバー通信が終わると、
２つ目の引数「**completionHandler**」というクロージャに
- **dataにはサーバーから返されたデータ**
- **responseにはHTTPヘッダーやステータスコードなど**
- **errorにはリクエスト失敗時の理由**

が渡されます。

「**completionHandler**」は簡単に言うと、通信が終了した後の完了処理をするためのクロージャです。

また、この3つの値は全てオプショナル型であるため、nilが許容されています。


- **データ取得成功時:** 
    `data`に値が入り、`error`には`nil`が入る

- **データ取得失敗時:** 
`error`に値が入り、`data`には`nil`が入る

`URLSession`は、サーバ通信が終わると、この`completionHandler`の引数にレスポンスとして値が入るという処理の流れになります。

# 5. データをデコードする
取得したAPIデータをプロジェクト内で使用できるようにしてあげるには、デコードする必要があります。
今回はユーザー名、プロフィール画像、ユーザーIDを取得するとしてみましょう。
まず、JSON Decodeのための構造体を用意する必要があります。
```swift
//プロフィール情報
struct User: Decodable {
    let name: String
    let id: Int
    let avatarURL: URL

    enum CodingKeys: String, CodingKey {
        case name = "name"
        case id = "id"
        case avatarURL = "avatar_url"
    }
}

```
次に受け取ったデータを、JSONDecoder() を使って User オブジェクトにデコードしていきます。
```swift
let user: User = try JSONDecoder().decode(User.self, from: data)
```
JSONDecoderを使うと、try-catch構文を使用する必要が出てきますが、今回説明は省略します。
簡単に説明するとエラー発生時に、適切にエラー処理をしてくれる構文という認識でOKかと思います

ここまででAPI通信を実行し、Githubからプロフィール情報を取得してくる処理は完成です。
ここからは、取得したプロフィール情報をUIに表示していく処理になります

# 今回実装したサンプルコード
```swift
import UIKit
import Foundation

//プロフィール情報
struct User: Decodable {
    let name: String
    let id: Int
    let avatarURL: String

    enum CodingKeys: String, CodingKey {
        case name = "name"
        case id = "id"
        case avatarURL = "avatar_url"
    }

}

class ViewController: UIViewController {

    @IBOutlet weak var avatarImage: UIImageView!
    
    @IBOutlet weak var name: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        getGithubAPI()
    }

    func getGithubAPI() {
        guard let url = URL(string: "https://api.github.com/users/Githubのユーザー名") else { return }
        let reqest = URLRequest(url: url)

        let task = URLSession.shared.dataTask(with: reqest, completionHandler: { data, response, error in
            if let err = error {
                print("error: \(error)")
                return
            }
            guard let data = data else { return }
            do {
                let user: User = try JSONDecoder().decode(User.self, from: data)
                //UIの処理はメインスレッドで実行
                DispatchQueue.main.async {
                    self.name.text = user.name
                    //文字列として保持されているavatarURLから画像を取得する
                    if let avatarURL = URL(string: user.avatarURL) {
                        URLSession.shared.dataTask(with: avatarURL) { data, response, error in
                            if let data = data {
                                DispatchQueue.main.async {
                                    self.avatarImage.image = UIImage(data: data)
                                }
                            }
                        }.resume()
                    }
                }
                print(user)
            } catch let err {
                print("error: \(err)")
            }
        })
        task.resume()
    }
```
# 最後に
ご覧いただきありがとうございました。
今回は簡易的にAPI通信を実装してみました。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

# 参考

https://www.youtube.com/watch?v=dNKu-TtWXew&t=2034s

https://qiita.com/st43/items/552bbe865d97b56d213f#%E4%BB%8A%E5%9B%9E%E3%81%AE%E7%9B%AE%E6%A8%99