SwiftでconnpassAPIを叩いて記事を取得してみた



#はじめに
オイシックス・ラ・大地（以下オイラ大地）の新卒エンジニアの城間です。
この記事はオイラ大地株式会社アドベントカレンダーの19日目の記事になります。
 [Oisix ra daichi Inc. Advent Calendar 2018](https://adventar.org/calendars/3112)

11月からiosエンジニアとして所属しており、本記事では課題として作ったアプリを
書いていきたいと思います。
Swiftは初学者で色々とつまづいていたので、参考になれば幸いです。

#使用したAPI
 connpass APIを使用しました。(https://connpass.com/about/api/)
レスポンスフィールドとしては下記を取得します。

| フィールド | 型 | 説明 |
|:-------:|:------:|:------------:|
| events  |  配列(複数要素) |  検索結果のイベントリスト |
| title  | 文字列(UTF-8)	  |  タイトル |
| event_url  | 文字列(UTF-8)	  |  connpass.com 上のURL |


https://connpass.com/api/v1/event/?keyword=python

###完成図
<img width="360" alt="スクリーンショット 2018-12-18 19.14.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/122719/36476a70-7b18-3cd9-a75c-43fc756301e5.png">


#JSONの値を取得する
###Codable
レスポンスの取得には[Codable](https://developer.apple.com/documentation/swift/codable
)を使用しています。
SwiftyJSONなどのライブラリを使用してJSON形式のデータを構造体に変換するなどの処理をする方法もありますが、CodableはSwiftが提供しておりライブラリを使わずシンプルに記述できるので使いました。

CodableでconnpassAPIのデータを変換します。

```swift
struct Resultsfield: Codable {
    var events: [Events]
    struct Events: Codable {
        var title: String
        var event_url: String
    }
}
```

###APIを叩く
connpassAPIを叩くためにURLSessionを使いました。
URLSessionとはリクエストを受けて通信を行うクラスです。

```swift

struct Connpass {
    //非同期処理の後にクロージャを実行する
    static func fetchEvent(completion: @escaping (Resultsfield) -> Swift.Void) {        
        let url = "https://connpass.com/api/v1/event/?keyword=python" 
        //URLComponentsはクエリストリングの追加などが可能       
        let urlComponents = URLComponents(string: url)
        //非同期で通信を行う
        let task = URLSession.shared.dataTask(with: (urlComponents?.url!)!) { data, response, error in            

            guard let jsonData = data else {
                return
            }
            
            do {
                //jsonのマッピング
                let resultsfields = try JSONDecoder().decode(Resultsfield.self, from: jsonData)
                completion(resultsfields)

            } catch {
                print(error.localizedDescription)
            }
        }
        ////task.resume()メソッドを実行すると通信が開始される
        task.resume()
    }
}

```

#tableViewにデータを表示させる

今回はextensionを使いクラスに定義を追加しました。
拡張先ではcellに取ってきた値を代入する処理をしています。

```swift

extension ViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
        let resultsfield = resultsfields.events[indexPath.row]
        cell.textLabel?.text = resultsfield.title
        cell.detailTextLabel?.text = resultsfield.event_url
        return cell
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return resultsfields.events.count
    }  
}
```

###SafariServices
遷移先webページをSafariビューっぽい感じにするために
SFSafariViewControllerを使用します。
SafariServicesをimportして下記のように記述します。


```swift

extension ViewController: UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let resultsfield = resultsfields.events[indexPath.row]
        let webPage = resultsfield.event_url
        let safariVC = SFSafariViewController(url: NSURL(string: webPage)! as URL)
        present(safariVC, animated: true, completion: nil)
    }
}
```

#ソースコード
全体としては下記のようになります。

```viewcontroller.swift

import UIKit
import SafariServices

struct Resultsfield: Codable {
    var events: [Events]
    struct Events: Codable {
        var title: String
        var event_url: String
    }
}

struct Connpass {
    
    static func fetchEvent(completion: @escaping (Resultsfield) -> Swift.Void) {        
        let url = "https://connpass.com/api/v1/event/?keyword=python"        
        let urlComponents = URLComponents(string: url)
        let task = URLSession.shared.dataTask(with: (urlComponents?.url!)!) { data, response, error in            

            guard let jsonData = data else {
                return
            }
            
            do {
                let resultsfields = try JSONDecoder().decode(Resultsfield.self, from: jsonData)
                completion(resultsfields)

            } catch {
                print(error.localizedDescription)
            }
        }
        task.resume()
    }
}


class ViewController: UIViewController {
        
    private var tableView = UITableView()
    var resultsfields: Resultsfield = Resultsfield(events: [])

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.frame = view.frame
        tableView.dataSource = self
        view.addSubview(tableView)

        Connpass.fetchEvent(completion: { (resultsfields) in
            self.resultsfields = resultsfields
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        })
        
    }    
}

extension ViewController: UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell")
        let resultsfield = resultsfields.events[indexPath.row]
        cell.textLabel?.text = resultsfield.title
        cell.detailTextLabel?.text = resultsfield.event_url
        return cell
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return resultsfields.events.count
    }  
}
extension ViewController: UITableViewDelegate {
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let resultsfield = resultsfields.events[indexPath.row]
        let webPage = resultsfield.event_url
        let safariVC = SFSafariViewController(url: NSURL(string: webPage)! as URL)
        present(safariVC, animated: true, completion: nil)
    }
}
```

#最後に
最初は基礎文法などをわかっておらずコピペ頼りな所もあったのですが
このメソッドはどういう意味をもっているという理解や自分で一から作り上げる事が
やっぱり一番の近道だなと作っていて感じました。

追加機能として検索機能やソート機能
RxSwiftに書き換えなどしているので、出来次第また記事をあげたいと思います！