概要
この記事は初心者の自分がRESTfulなAPIとswiftでiPhone向けのクーポン配信サービスを開発した手順を順番に記事にしています。技術要素を1つずつ調べながら実装したため、とても遠回りな実装となっております。
前回のswiftでwebAPIを呼び出してjsonデータを表示させるでwebAPIからレスポンスしたデータからjsonをパースして取り出す処理が出来たので、次はjsonのクーポン情報をSwiftのTableViewに一覧表示させます。
(なお、現在のAPIで取得出来るクーポン情報は1件なのでTableViewである必要はありませんが、このあと複数件のクーポン情報を取得できるように改造するのでTableViewを使います。)
参考
- やさしくはじめる iPhoneアプリ作りの教科書 森 巧尚 著 マイナビ
- 【swift入門】apiを叩いてTableViewに表示させる
環境
Mac OS 10.15
Swift5
Xcode11.1
アプリの仕様
- アプリ起動時点で利用可能なクーポンの特典と利用期限をTableViewで一覧表示する。
手順
- Xcodeでアプリ画面をデザインする
- TableViewにクーポンの特典と利用期限を表示する。
Xcodeでアプリ画面をデザインする
Main.storyboard
を開いて画面のデザインをします。
右上の「+」ボタンを押すとUIツールキットなどの一覧が表示されるので、Table Viewを選択。
とりあえず画面いっぱいにテーブルを表示するよう、AutoLayoutで上下左右それぞれ縁からの距離を0ポイントに固定。実行すると画面いっぱいにテーブルが表示されます。
レイアウトしたTable ViewをViewController
に繋げます。繋げる際のプロトコルとしてdataSource
とdelegate
を選びます。手順はTable Viewをクリックすると線が表示されるので、上部のViewController
に線をドラッグし、メニューを表示させます。
次にdataSource
を選択します。同じ方法でdelegate
も選択します。
Table View で右クリック(Control + クリック)をしてdataSource
とdelegate
で接続されていることを確認します。
TableViewにクーポンの特典と利用期限を表示する。
ここからViewController.swift
を改造します。
まず、class ViewController
に2つのプロトコルUITableViewDataSource
とUITableViewDelegate
を追加します。
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
の部分にエラーが表示されますが、テーブルを表示するのに最低限必要な numberOfRowsInSection
(行数の指定)とcellForRowAt
(表示するデータ)が設定されていないのが原因です。この後の実装でエラーは消えるので一旦無視します。
次に、テーブルに表示するメンバー変数(クラス内で共有可能な変数)を下記の通り定義します。メンバー変数は多用したくないですが、引数としてテーブルに渡す事が難しいので、とりあえずメンバー変数を使います。
クーポン特典を保持する変数couponBenefit
と、有効期限を保持する変数couponDeadline
を定義します。
var couponBenefit: String = ""
var couponDeadline: String = ""
次に、テーブルを表示するのに最低限必要なnumberOfRowsInSection
(行数の指定)とcellForRowAt
(表示するデータ)を指定します。指定は関数で行います。
行数の指定はこちらです。現在はwebAPIから1件のクーポン情報しか取得しないので、行数は固定値1を返す仕様にします。
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
表示する情報の指定はこちらです。
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//セルを作る
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "couponCell")
//テキストにクーポン特典を設定
cell.textLabel?.text = self.couponBenefit
//サブテキストにクーポンの有効期限を設定
cell.detailTextLabel?.text = "有効期限:" + self.couponDeadline
return cell
}
次は、override funk viewDidLoad()
メソッドの中のJSONをパースする処理の後にクーポン特典と有効期限の情報をメンバー変数に格納する処理を追加します。
self.couponBenefit = couponData["coupon_benefits"] as! String
self.couponDeadline = couponData["coupon_deadline"] as! String
ここでアプリを実行してみます。シミュレータの画面には何も表示されません。
これはAPIからのレスポンスとテーブルが描画される時間のタイムラグが原因です。
APIからのレスポンスを受け取って変数のcouponBenefit
とcouponDeadline
へデータを格納しているうちに、テーブルが先に描画されています。
解消するには、APIからのレスポンスを受け取って各変数にデータを格納した後に、テーブルをリロードします。
まず、Main.storyboardのtableView
から ViewController
にラインを引っ張って、@IBOutlet
の設定をします。
@IBOutlet weak var tableView: UITableView!
次にメンバー変数の定義部分を改造します。変数のcouponBenefit
かcouponDeadline
のデータが変更された際にtableViewをリロードするようにdidSet{}
を使ってtableView.reloadData()
が実行されるようにします。
var couponBenefit: String = "" {
didSet{
tableView.reloadData()
}
}
var couponDeadline: String = "" {
didSet{
tableView.reloadData()
}
}
次に、テーブルの描画が必ずメインスレッドで実行されるようにします。
テーブルはメインスレッドで描画される必要がありますが、他の処理とのタイミングによってバックグラウンドで実行されてしまう事があります。下記のようにプログラムを修正します。
DispatchQueue.main.async() { () -> Void in
self.couponBenefit = couponData["coupon_benefits"] as! String
self.couponDeadline = couponData["coupon_deadline"] as! String
}
以上でViewController
の改造は終わりです。作成したプログラムはこちらになります。
import UIKit
class ViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
@IBOutlet weak var tableView: UITableView!
var couponBenefit: String = "" {
didSet{
tableView.reloadData()
}
}
var couponDeadline: String = "" {
didSet{
tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
let url: URL = URL(string: "http://127.0.0.1:8000/coupon/?coupon_code=0001")!
let task: URLSessionTask = URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in
do {
let couponData = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.allowFragments) as! [String: Any]
print(couponData)
DispatchQueue.main.async() { () -> Void in
self.couponBenefit = couponData["coupon_benefits"] as! String
self.couponDeadline = couponData["coupon_deadline"] as! String
}
}
catch {
print(error)
}
})
task.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//セルを作る
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "couponCell")
//テキストにクーポン特典を設定
cell.textLabel?.text = self.couponBenefit
//サブテキストにクーポンの有効期限を設定
cell.detailTextLabel?.text = "有効期限:" + self.couponDeadline
return cell
}
}
動作確認
djangoのサーバを起動してwebAPIが利用できる状態にし、Xcodeでアプリを実行します。すると、仕様とおりクーポンの特典と有効期限が表示されました。