Edited at
SwiftDay 7

AlamofireとSwiftyJSONでAPIを叩くチュートリアル

More than 1 year has passed since last update.


はじめに

動作環境は Xcode7.x, Swift2.x となります。

職業柄、入門者に教えることが多いのですが、APIを触るのは少しハードルが高いのかなと感じ、チュートリアルを書くに至りました。

今回はQiitaのAPIを叩き、最新記事一覧を取得・表示することを通して、

AlamofireとSwiftyJSONの仕様・使い方を紹介したいと思います。

以下は完成イメージです。

iPhone_5s_-_iPhone_5s___iOS_9_1__13B137_.png

それではアプリを作る前に、まずはAPIについて簡単に説明をしてから始めます。


APIは怖くない

APIと聞くとなんだか凄いものと思う方がいるかもしれません。

ですがAPIはあなたが思っているほど難しいものではありません。

APIをとても簡単に説明すると 指定のURLにアクセスすると、配列や辞書型のようなデータを返してくれるもの です。

 

QiitaのAPIを例に見てみましょう。

https://qiita.com/api/v2/items

こちらのURLにアクセスすると、Qiitaの最新記事一覧が取得できます。

実際にブラウザで開いてみるとこのようなページが開きます。

https___qiita_com_api_v2_items.png

何やら文字が羅列されていますが、これらが記事一覧のデータです。

 

少し見づらいので、これを単純な形で表現してみると以下のようになります。

[

{ title: "AlamofireとSwiftyJSONで〜",
body: "はじめに〜",
created_at: "2015-12-03 22:17:32"
},
{ title: "Optionalを〜",
body: "この記事は〜",
created_at: "2015-12-04 11:41:26"
}
]

まず、一番外側に [ ] がありますね。

これは 配列 です。記事の一覧がこの配列に入っています。

そしてその中にある { title: "AlamofireとSwiftyJSONで〜", ... }辞書型 です。

1つの辞書型が1つの記事を表しています。

Swiftの辞書型とは少し形が異なりますが、

{ title: "タイトル" } という辞書型があった場合、

titleがキー、"タイトル"が値となります。

 

ちなみにこのような表現形式を JSON と言います。

つまりAPIはJSONを返していたんですね。

配列と辞書型のようなものが返ってくると分かればSwiftでも扱えそうですよね!

ということでここからは実際にプロジェクトをいきましょう。


プロジェクト作成からライブラリの導入


プロジェクト作成

Single View Applicationで新規プロジェクトを作成します。

今回のプロジェクト名は QiitaViewer としましょう。

※ ターミナルの操作がよく分からない方はここでプロジェクトをデスクトップに保存してください。


ライブラリ導入

プロジェクトが出来たらライブラリを導入します。

今回はCarthage(カーセッジ)というライブラリ管理ツールを使用しましょう。

ではまずターミナルを開いてください。


Homebrewの導入

Homebrewを入れてない方は以下のコマンドを実行してインストールしましょう。

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"


Carthageの導入

次に先ほどインストールしたHomebrew経由でCarthageをインストールします。

以下のコマンドを実行しましょう。

brew install carthage

これでCarthage自体を使用する準備が出来ました。


ライブラリの導入

まずはライブラリを導入したいプロジェクトのところまで移動します。

先ほどデスクトップにプロジェクトを保存した方は以下のコマンドで移動できます。

cd ~/Desktop/QiitaViewer

ここで導入したいライブラリを記述するファイルを作ります。

以下のコマンドを実行してください。

touch Cartfile

作成が出来たらファイルを編集するので以下のコマンドを実行してください。

vim Cartfile

ターミナル上からCartfileを以下のように編集し、保存してください。

github "Alamofire/Alamofire"

github "SwiftyJSON/SwiftyJSON"

念のため編集方法も書いておきます。

iを押すと入力モードになります。

入力モードから抜ける場合はescを押します。

その状態で:wqを実行すると、保存して終了が出来ます。

保存したくない場合は:q!で保存せず終了もできます。

編集が出来たら以下のコマンドからライブラリをビルドします。

carthage update --platform iOS

少し時間がかかりますが、完了すると以下のようになります。

1__bash.png

 

次にプロジェクトの方でライブラリを追加する作業を行います。

すでにプロジェクトを開いている方は一度再起動してください。

まずプロジェクトファイルのGeneralタブから、

「Linked Frameworks and Library」にある+ボタンを押します。

QiitaViewer_xcodeproj_と_Swift_-_Carthageを使ってビルド時間を短縮しよう_-_Qiita_と_AlamofireとSwiftyJSONでAPIを叩くチュートリアル.png

Frameworksを選択する画面になるので、「Add Other...」を選択。

QiitaViewer_xcodeproj.png

プロジェクトのディレクトリ内から、Carthage/Build/iOSと移動し、

ライブラリのAlamofire.frameworkとSwiftyJSON.frameworkを選択します。

QiitaViewer_xcodeproj.png

ライブラリが追加できたら、Build Phasesタブに移動し、

+ボタンから、「New Run Script Phase」を選択します。

OtherViews_と_QiitaViewer_xcodeproj.png

すると「Run Script」が現れます。

ここでまず「Shell」の下にある黒い部分に以下のコマンドを記述します。

/usr/local/bin/carthage copy-frameworks

そして次に「input Files」にて+ボタンを押し、以下のようにframeworkの情報を記述します。

$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework

$(SRCROOT)/Carthage/Build/iOS/SwiftyJSON.framework

QiitaViewer_xcodeproj.png

これでライブラリの追加は完了です。

次は画面を作成していきましょう!


記事一覧を表示する画面の作成

まず、最初からあるViewController.swiftは消します。

そして新たに ArticleListViewController.swift を作りましょう。

ArticleListViewController_swift.png

Storyboardの方ではArticleListViewControllerをInitial View Controllerにし、

Editor > Embed InからNavigation Controllerを選択します。

すると以下のようになるかと思います。

Main_storyboard_—_Edited.png

 

さて、ここからはArticleListViewControllerでコードを書いていきます。

はじめに不要な行を消し、ファイルを以下のような状態にしておきましょう。


ArticleListViewController

import UIKit

class ArticleListViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

}
}


ここで記事の一覧を表示するTableViewを作っていきます。


ArticleListViewController

class ArticleListViewController: UIViewController {

let table = UITableView() // プロパティにtableを追加

override func viewDidLoad() {
super.viewDidLoad()

table.frame = view.frame // tableの大きさをviewの大きさに合わせる
view.addSubview(table) // viewにtableを乗せる
}
}


ついでにNavigation Barに文字も入れておきましょう。


ArticleListViewController

override func viewDidLoad() {

super.viewDidLoad()
title = "新着記事" // Navigation Barのタイトルを設定

セルについてはまだですが、これでレイアウトは完成です。

次はAlamofireを利用して記事の一覧を取得します!


記事の一覧を取得

ここではAlamofireを使用してAPIを叩き、記事の一覧を取得していきます。

まずはファイルでAlamofireを使うためにインポートしましょう。


ArticleListViewController

import UIKit

import Alamofire // Alamofireをimport

記事の取得に関する処理はgetArticleメソッドを定義し、ここに書いていきます。


ArticleListViewController

func getArticles() {

}

 

まずはQiitaの新着記事を配信するURLへリクエストを送信します。

ここで使うのがAlamofireのrequestメソッドです。

ArticleListViewController_swift_—_Edited.png

requestメソッドはmethodURLStringの、2つの引数を取ります。

URLStringURLStringConvertible に準拠したものとなっていますが ここは単純にURLをString型で書けば大丈夫 です。

Method型の引数method通信の種類 を表す引数です。

基本的な通信の種類はGETPOSTです。

この2つを簡単に紹介すると、以下のような使い分けがされます。


  • 一方的に情報を受け取るときはGET

  • こちらが何か情報を送ってそれによって返される情報が変わるときはPOST

ここはAPIの使い方で書かれているはずなので、迷うことはありません。

問題はこの引数が Method型である というところです。

AlamofireにおいてMethod型は以下のようにenum(列挙型)で定義がされています。

QiitaViewer_xcodeproj.png

そのため、この引数は.GETのようにこのうちのいずれかのケースを書く必要があります。

今回アクセスするURLは https://qiita.com/api/v2/items で、

メソッドは GET です。

この情報から引数を埋めると以下のようになります。


ArticleListViewController

func getArticles() {

Alamofire.request(.GET, "https://qiita.com/api/v2/items") // APIへリクエストを送信
}

 

さて、ここまででリクエストを送ることが出来たので、次は返ってきた情報を取得しましょう。

そのためには、先ほどのrequestメソッドに続けて、responseJSONメソッドを使用します。

responseJSONメソッドは1つの関数を引数に取るので、その関数をクロージャで記述しましょう。

ArticleListViewController_swift_—_Edited.png

上の画像の通り、その関数では Response<AnyObject, NSError>型 の引数を使用できます。

ひとまずこの引数をresponseという名前で扱うこととしましょう。


ArticleListViewController

Alamofire.request(.GET, "https://qiita.com/api/v2/items")

.responseJSON { response in
// ここに処理を記述していく
}

 

さて、responseと定義したResponse型の引数は以下のような4つのプロパティを持ちます。

Alamofire_Response_swift_at_master_·_Alamofire_Alamofire.png

ですが記事の取得に使うのは、4番目のresultだけです。

 

このresultですが、型は Result<Value, Error> となっていますね。

Result型の定義元を見てみるとenumで実装されているようです。

Alamofire_Result_swift_at_master_·_Alamofire_Alamofire.png

列挙型ResultはSuccessもしくはFailureを値に取り、

Successの場合にはValueを持っていることが分かります。

そしてこのValueこそが私たちの求める記事の情報なのです!

AlamofireはこのValueを取得するためにvalueというプロパティを用意してくれています。

ではひとまず記事の情報を取得してprintで表示してみるとしましょう。

コードは以下のようになります。


ArticleListViewController

Alamofire.request(.GET, "https://qiita.com/api/v2/items")

.responseJSON { response in
print(response.result.value) // responseのresultプロパティのvalueプロパティをコンソールに出力
}

ここでviewDidLoadメソッドの最後にgetArticleメソッドの呼び出しを書いてから、実際にRunをしてみましょう。


ArticleListViewController

override func viewDidLoad() {

// 省略
getArticles()
}

実行後、コンソールを見てみると何やらデータが表示されていることが分かります。

ArticleListViewController_swift_—_Edited.png

ここまでで記事一覧の情報を取得することが出来ました!

ですが、TableViewにこんなにたくさんの情報を表示は出来ないので、ここから表示したい情報だけを取得しましょう。


記事のデータから欲しい情報のみを取得

さて、TableViewに表示するために、必要なデータのみを取り出したいのですが、先ほど表示された記事一覧はAnyObject型となっていて扱うのが面倒です。

そこでSwiftyJSONの出番ですね。

とりあえずSwiftyJSONをインポートしておきましょう。


ArticleListViewController

import UIKit

import Alamofire
import SwiftyJSON // SwiftyJSONをimport

 

SwiftyJSONはJSON型という、SwiftyJSONが定義した型にデータを変換してからそのデータを扱っていきます。

SwiftyJSON_SwiftyJSON_swift_at_master_·_SwiftyJSON_SwiftyJSON.png

JSON型はAnyObject型からイニシャライズすることが出来るので、

先ほどのresponse.result.valueをアンラップして引数に渡してあげましょう。


ArticleListViewController

Alamofire.request(.GET, "https://qiita.com/api/v2/items")

.responseJSON { response in
guard let object = response.result.value else {
return
}

let json = JSON(object)
}


guard文を使ってvalueがnilだった場合は早期リターンをさせています。

if letを使ったときと比べ、ネストが深くならないのが良いですね。

 

さて、これでJSON型に変換出来たので、まずは記事の一覧を1つ1つの記事に分解していきましょう。

これにはforEachメソッドを使用します。

forEachメソッドはSwiftの標準ライブラリにあるSequenceTypeプロトコルに定義されています。

このメソッドは引数に1つの関数をとり、複数の要素それぞれに、引数で渡した処理をしてくれます。

QiitaViewer_xcodeproj.png

そして JSON型はSequenceTypeプロトコルに準拠している ので、このメソッドが使えます。

QiitaViewer_xcodeproj.png

実際に使用してみましょう。

クロージャを記述しようとすると、引数にはString型とJSON型とのタプルが使えることが分かります。

ArticleListViewController_swift_—_Edited.png

ここで、Stringにはその要素が何番目かという情報が入り、

JSONには記事1つのデータが入ります。

今回は記事が何番目か、という情報は使わないので 変数名を付けずアンダースコアにしておきます。


ArticleListViewController

let json = JSON(object)

json.forEach { (_, json) in
// ここに処理を書いていく
}

 

それではここで記事のタイトルを取得してみましょう!

QiitaAPIのドキュメントによると、タイトルは"title"というキーで取得出来るようです。

JSON型は辞書型と同じように[ ]で特定のキーの値を取得することが出来ます。


ArticleListViewController

let json = JSON(object)

json.forEach { (_, json) in
json["title"] // jsonから"title"がキーのものを取得
}

これはJSON型に subscript が実装されているからですね。

subscriptは配列や辞書型で扱うように、[ ]をメソッドとして使えるようにするものです。

QiitaViewer_xcodeproj.png

また、上の定義から分かるように、subscriptの返り値はJSON型となっています。

TableViewに表示するために、文字列はStringとして扱いたいので型の変換をしましょう。

そのためにはJSON構造体に定義されているstringプロパティを使います。

SwiftyJSON_SwiftyJSON_swift_at_master_·_SwiftyJSON_SwiftyJSON.png

stringプロパティはString型にキャスト出来ればそれを、出来なければnilを返すメソッドで、返り値はString?型となります。

これを使ってさらにprintもしてみましょう。


ArticleListViewController

let json = JSON(object)

json.forEach { (_, json) in
print(json["title"].string) // 記事タイトルを表示
}

以下のように記事のタイトルだけを取得することが出来ました!

ArticleListViewController_swift.png

 

それでは続いて投稿者のユーザーIDも取得しましょう。

再度QiitaAPIのドキュメントを見ると、投稿者のユーザーIDは、"user"というキーの値の中の、"id"というキーの値に格納されているようです。

つまり二重の辞書型に入ってる訳ですね!

これもSwiftyJSONで簡単に書けます。


ArticleListViewController

let json = JSON(object)

json.forEach { (_, json) in
json["title"].string
print(json["user"]["id"].string) // 投稿者のユーザーIDを表示
}

何やらユーザーIDらしきものたちを表示することが出来ました。

ArticleListViewController_swift.png

さて、これで欲しい情報は全て取得できました!


記事の一覧をプロパティに保存

これらの情報をTableViewに表示するために、一度プロパティに保存しておきましょう。

まずはArticleListViewControllerにarticlesというプロパティを作ります。

型はキーがString、値がString?の配列としましょう。


ArticleListViewController

class ArticleListViewController: UIViewController {

var articles: [[String: String?]] = [] // 記事を入れるプロパティを定義

 

次にforEachメソッドの中でそれぞれの記事の情報をここに入れていきます。

また、保存の処理が終わったら全ての記事が保存出来たかを確認してみます。


ArticleListViewController

let json = JSON(object)

json.forEach { (_, json) in
let article: [String: String?] = [
"title": json["title"].string,
"userId": json["user"]["id"].string
] // 1つの記事を表す辞書型を作る
self.articles.append(article) // 配列に入れる
}
print(self.articles) // 全ての記事が保存出来たら配列を確認

以下のように表示がされていれば正常に保存が出来ています!

ArticleListViewController_swift_—_Edited.png


記事の一覧を表示

最後に記事の表示をします。

まずArticleListTableViewに UITableViewDataSourceプロトコルを採用 しましょう。


ArticleListViewController

class ArticleListViewController: UIViewController, UITableViewDataSource {


そしてtableView:numberOfRowsInSection:メソッドとtableView:cellForRowAtIndexPath:メソッドを定義していきます。

tableView:numberOfRowsInSection:から。

表示したいセルの数は記事数と等しいのでarticlescountプロパティを返しましょう。


ArticleListViewController

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return articles.count
}

そしてtableView:cellForRowAtIndexPath:ですね。

セルを作るのも面倒なので既存のスタイルのセルを使います。

その中でも今回はセルにタイトルとユーザーIDを表示したいので UITableViewCellStyle.Subtitle を使用します。


ArticleListViewController

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

let cell = UITableViewCell(style: .Subtitle, reuseIdentifier: "cell") // Subtitleのあるセルを生成

return cell // cellを返す
}


続いてセルのtextLabeldetailTextLabelにそれぞれ、titleuserIdを表示するよう設定していきましょう。


ArticleListViewController

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

let cell = UITableViewCell(style: .Subtitle, reuseIdentifier: "cell")
let article = articles[indexPath.row] // 行数番目の記事を取得
cell.textLabel?.text = article["title"]! // 記事のタイトルをtextLabelにセット
cell.detailTextLabel?.text = article["userId"]! // 投稿者のユーザーIDをdetailTextLabelにセット
return cell
}

 

これで UITableViewDataSourceに準拠できた ので、

tabledataSourceプロパティにArticleListViewControllerを入れましょう。


ArticleListViewController

table.frame = view.frame

view.addSubview(table)
table.dataSource = self // dataSouceプロパティに自身を代入

 

そして最後に、全ての記事が保存された段階でreloadDataメソッドを実行すれば記事の一覧を表示してくれます!


ArticleListViewController

let json = JSON(object)

json.forEach { (_, json) in
let article: [String: String?] = [
"title": json["title"].string,
"userId": json["user"]["id"].string
]
self.articles.append(article)
}
self.table.reloadData() // TableViewを更新

このように表示されていれば完成です。

iPhone_5s_-_iPhone_5s___iOS_9_1__13B137_.png


完成ソースコード

https://github.com/yuta-t/QiitaViewer


ArticleListViewController

import UIKit

import Alamofire
import SwiftyJSON

class ArticleListViewController: UIViewController, UITableViewDataSource {
var articles: [[String: String?]] = []
let table = UITableView()

override func viewDidLoad() {
super.viewDidLoad()
title = "記事一覧"

table.frame = view.frame
view.addSubview(table)
table.dataSource = self

getArticles()
}

func getArticles() {
Alamofire.request(.GET, "https://qiita.com/api/v2/items")
.responseJSON { response in
guard let object = response.result.value else {
return
}

let json = JSON(object)
json.forEach { (_, json) in
let article: [String: String?] = [
"title": json["title"].string,
"userId": json["user"]["id"].string
]
self.articles.append(article)
}
self.table.reloadData()
}
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return articles.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .Subtitle, reuseIdentifier: "cell")
let article = articles[indexPath.row]
cell.textLabel?.text = article["title"]!
cell.detailTextLabel?.text = article["userId"]!
return cell
}
}



最後に

今回はAPIを叩いてみる、ということを目的にコードを書いてきました。

そのため、責務ごとにファイルを分けるといったことは行っていません。

もし、そういったアーキテクチャ・パターンに興味のある方は これが最強のMVC(iOS) などを参考にすると良いでしょう。


参考資料

Alamofire - Github

SwiftyJSON - Github

Qiita API ドキュメント

Carthageを使ってビルド時間を短縮しよう