iOSアプリチュートリアルとして、Qiitaのクライアントアプリを一緒に作っていきましょう。
今回は最終章です!
完成形はこちら
参考記事
チュートリアル上で出てくるいくつかのスキルは下記記事で詳細に解説してますので必要であれば参考にしてください。
【入門】WEBページをアプリで表示 WebView(WKWebView)の使い方 UITableViewの基本的な使い方を解説 【入門】Alamofireチュートリアル 【入門】Carthageとは?使い方解説
ライブラリ導入
今回のアプリではAPI通信をするためのAlamofireとURLから画像を取得するNukeというライブラリを使用します。
Carthage
を使用してこの2つのライブラリを導入していきましょう。
Carthage導入
まずは Cartfile
を作成します。
ターミナルで作成したプロジェクトのrootに移動して以下のコマンドを実行してください。
$ vim Cartfile
内容は以下のようにしましょう。
github "Alamofire/Alamofire" "4.8.2"
github "kean/Nuke" "7.6.3"
作成できたら以下のコマンドを実行しましょう。少し時間がかかります。
$ carthage update --platform iOS
AlamofireとNuke導入
Xcodeで設定していきます。
左メニューの一番上を選択して QiitaClient
-> General
を開いてください。
下の方に Linked Frameworks and Libraries
があるのでそこのプラスボタンを押しましょう。
Add Other...
から Carthage
-> Build
-> iOS
-> Alamofire.framework
を選択してください。
同様にして Nuke.framework
も選択してください。
すると Linked Frameworks and Libraries
にAlamofire.framework
と Nuke.framework
が追加されます。
次に General
タブから Build Phases
タブに移動してください。
赤枠で囲っているプラスボタンから New Run Script Phase
を選択しましょう。
次に作成された Run Script
を開き、一番上の黒枠には以下を入力して、
/usr/local/bin/carthage copy-frameworks
Input files
には以下を入力してください。
$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework
$(SRCROOT)/Carthage/Build/iOS/Nuke.framework
以下のようになればOKです
これでCarthageの設定は完了しました!
Qiita APIとの接続・連携
次はQiita APIとの接続と連携をしていきましょう!
まずはこのURLをブラウザなどで開いてみてください。
https://qiita.com/api/v2/tags/iOS/items
するとiOSタグのついた記事一覧のjsonデータが返ってきているかと思います。
この中で今回のプロジェクトで使用するのは以下のみです。
これらのモデルを作成していきます。
[
{
title: "title",
url: "https://xxx"
user: {
id: "user_id"
profile_image_url: "https://yyy"
}
}
]
モデル作成
まずはQiitaユーザーのモデルから作成していきます。
QiitaUser.swift
というファイルを作成して以下のようなstructを作成してください。
import Foundation
struct QiitaUser: Codable {
let id: String
let imageUrl: String // ①
enum CodingKeys: String, CodingKey {
case id
case imageUrl = "profile_image_url" // ②
}
}
今回は Codable
を使用しています。
user
の中で必要なのは id
と profile_image_url
のみなのでその他は定義していません。
また一般的にiOS開発ではスネークケースを使用しないので①のようにキャメルケースで定義することが多いです。
しかしレスポンスとしてはスネークケースであるため CodingKeys
を使用して profile_image_url
がきたら imageUrl
である、というように定義しています。
次は記事のモデルを作成します。
QiitaArticle.swift
というファイルを作成して以下のようなstructを入力してください。
import Foundation
struct QiitaArticle: Codable {
let title: String
let url: String
let user: QiitaUser // ⓵
}
記事モデルの中で必要な情報は title
と url
と先ほど定義した user
のみです。
CodableでもStringやIntの他に自分で定義したモデルも⓵のようにプロパティとして持たせることは可能ですが、その型もCodableである必要があります。
これでモデルの定義は完了です!
API繋ぎこみ
次はAlamofireを使用してAPIを叩きUITableViewへの反映まで実装しましょう。
まずは ViewController
クラスを以下のように変更してください。
import UIKit
import Alamofire // ➀importの追加
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
private var articles: [QiitaArticle] = [] // ②取得した記事一覧を保持しておくプロパティ
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
let nib = UINib(nibName: "QiitaTableViewCell", bundle: nil)
tableView.register(nib, forCellReuseIdentifier: "QiitaTableViewCell")
tableView.rowHeight = 80
loadQiita() // 関数呼び出し
}
// loadする関数の定義
private func loadArticles() {
// ③Qiita APIを叩く
Alamofire.request("https://qiita.com/api/v2/tags/iOS/items").response { response in
guard let data = response.data else {
return
}
let decoder = JSONDecoder()
do {
// ④レスポンスを[QiitaArticle]にデコード
let articles: [QiitaArticle] = try decoder.decode([QiitaArticle].self, from: data)
// ⑤取得した記事をarticlesに代入
self.articles = articles
// ⑥tableViewを更新
self.tableView.reloadData()
} catch {
print(error)
}
}
}
}
1つ1つ見ていきましょう。
①ではAlamofireをimportしています。これでAlamofireが使用できるようになります。
次に articles
という配列を用意します。これは記事一覧のレスポンスを保持しておくためです。
実際にAPIを叩いているのは loadArticles()
という関数です。③でAlamofireを使っています。④ではレスポンスデータを [QiitaArticle]
にデコードしています。
このデコードされた articles
を⑤で先ほど用意した ViewController
の articles
プロパティに代入して⑥で tableView
のデータに更新をかけています。
次に UITableViewDataSource
のdelegate関数も修正していきましょう。
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// ⑦cell数をarticlesの数に設定
return articles.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "QiitaTableViewCell", for: indexPath) as? QiitaTableViewCell else {
return UITableViewCell()
}
// ⑧indexPathを用いてarticlesから該当のarticleを取得する
let article = articles[indexPath.row]
// ⑨cellへの反映
cell.set(title: article.title, author: article.user.id)
return cell
}
}
⑦ではcellの数を返しています。cellの数は articles
と同じ数であって欲しいので articles.count
を返すようにします。
次に⑧では indexPath
がcellを上から数えた時の番号となります。
これを使用して articles
から1つの QiitaArticle
を取得しています。
⑨ではその article
をcellに反映させています。
ここで1度ビルドしてみましょう。
このような動きになればOKです!
画像取得
Nuke
次はNuke
を使ってユーザーの画像を取得していきます。
まずは QiitaTableViewCell
を以下のように修正してください。
import UIKit
import Nuke // ①Nukeをimport
class QiitaTableViewCell: UITableViewCell {
@IBOutlet weak var iconImageView: UIImageView!
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var authorLabel: UILabel!
// ②引数にimageUrlを追加
func set(title: String, author: String, imageUrl: String) {
// ③Nukeを使用して画像を取得
Nuke.loadImage(with: URL(string: imageUrl)!, into: iconImageView)
titleLabel.text = title
authorLabel.text = author
}
}
まずは①でNukeをimportします。
次に set()
関数の引数に imageUrl
を追加します。この imageUrl
を元に③ではNukeを使って画像を取得しています。
into
にImageViewを指定することで取得が完了すると画像が反映されます。
set()
関数の引数を変更したので呼び出し箇所の ViewController
も修正しましょう。以下は ViewController
の一部しか表示していません。
extension ViewController: UITableViewDataSource {
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "QiitaTableViewCell", for: indexPath) as? QiitaTableViewCell else {
return UITableViewCell()
}
let article = articles[indexPath.row]
// ④imageUrlの引数にarticle.user.imageUrlを追加
cell.set(title: article.title, author: article.user.id, imageUrl: article.user.imageUrl)
return cell
}
}
④で追加された imageUrl
の引数に article.user.imageUrl
を指定しています。
ここで一旦ビルドしてみましょう。
このような動きになればOKです!
WebView
QiitaのWebページを表示
最後にcellを選択した時にその記事を表示するように修正しましょう。
import UIKit
import WebKit
class WebViewController: UIViewController {
private let webView = WKWebView()
// ①表示するURLを持っておく
var url: String!
override func viewDidLoad() {
super.viewDidLoad()
webView.frame = view.frame
view.addSubview(webView)
// ②googleのページからプロパティのurlに変更
let url = URL(string: self.url)
let request = URLRequest(url: url!)
webView.load(request)
}
}
①で表示させるurlをプロパティとして持っておきます。
これは外部から変更する想定なのでpublicとしておきます。
②で今までgoogleのページを表示していた箇所を先ほど定義したurlに変更しています。
次にcellを選択した時の実装も修正していきます。
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let storyboard = UIStoryboard(name: "WebViewController", bundle: nil)
let webViewController = storyboard.instantiateInitialViewController() as! WebViewController
// ③indexPathを使用してarticlesから選択されたarticleを取得
let article = articles[indexPath.row]
// ④urlとtitleを代入
webViewController.url = article.url
webViewController.title = article.title
navigationController?.pushViewController(webViewController, animated: true)
}
}
③でindexPathを使用してarticlesから選択されたarticleを取得します。
④ではそのarticleを使用して先ほど定義した url
とナビゲーションの title
に代入しています。
それではここでビルドしてみましょう。
このような動きになればOKです!
最後に
お疲れ様でした。
今回は実際にAPIと連携してデータを表示し、WebViewに遷移して表示できるようにしました。
こちらのチュートリアルのUI/API連携は、iOSアプリ開発の基本になるので、ぜひ何回か作成したり他のAPIを利用したりしてみましょう!