初めての"0→1"でiOSアプリを作成
自己紹介
こんにちはshuです。
現在Swiftを用いたiOSのアプリ開発をしています。
概要
案件にアサインされる前の腕試しということで
仕様に従ってアプリを作成する"模擬案件"に挑戦しました。
開発を終えての感想・得られた技術についてまとめていきます。
得られた技術に関しては
調べていて記事が少なかった
"Delegateを用いたエラー処理"
に関する内容をまとめていきます。
作成したアプリ
Qiitaのクライアントアプリ
GitHub
Qiita APIを用いて、記事を簡単に見れるアプリを作成しました。
そもそもDelegateとは
Delegateの概念に関してわかりやすかった記事を紹介します。
【swift】イラストで分かる!具体的なDelegateの使い方。
「あるクラスは、他のクラスのインスタンスに、処理を任せることができる。」
この概念を理解するのに少々時間がかかりました。
今回エラー処理実装に関係するクラスの説明
FeedViewController
サブクラス:UIViewController
記事一欄画面をコントロールするクラス
AirticleDataNetworkService
記事一覧のAPIを叩くクラス
FeedViewControllerでインスタンス化して使用している
ErrorView
サブクラス:UIView
エラーが出たことをユーザーに知らせるクラス
実現させたいエラー処理の内容
Qiita API ドキュメントに記載がある通り
アクセストークンなしのリクエストには回数制限があります。
アプリで連続してリクエストを送って上限回数を超えたら
記事一欄画面の上にErrorViewを表示させます。
Delegateを用いたエラー処理
1. protocolの用意
Delegateメソッドの命名規則や引数の名前に関して
考慮せず作成しました。お許しください。
protocol ErrorDelegate: AnyObject {
func segueErrorViewController(qiitaError: QiitaError)
func backToLoginViewController()
func reload()
}
今回は
segueErrorViewControllerメソッドをどう利用したかについて
説明していきます。
2. FeedViewControllerに準拠させる
extension FeedViewController: ErrorDelegate {
func segueErrorViewController(qiitaError: QiitaError) {
let errorView = ErrorView.make() //ErrorViewインスタンス化
errorView.checkSafeArea(viewController: self) //レイアウト設定
errorView.errorDelegate = self
errorView.qiitaError = qiitaError
errorView.setConfig()
view.addSubview(errorView) //記事一覧画面の上に表示させる
}
}
FeedViewControllerでしかできないことを意識して実装していきます。
3. AirticleDataNetworkServiceに紐付け
FeedViewControllerのviewDidLoadメソッド内で
AirticleDataNetworkServiceのインスタンス化後に紐付けを行います。
override func viewDidLoad() {
super.viewDidLoad()
//記事データ取得
articleListDataRequest = AirticleDataNetworkService(searchDict: nil)
//紐付け
articleListDataRequest.errorDelegate = self
getData(requestAirticleData: articleListDataRequest)
}
4. AirticleDataNetworkServiceクラスでDelegateメソッドを呼び出す
func showErrorView(qiitaError: QiitaError) {
if let errorDelegate = self.errorDelegate {
errorDelegate.segueErrorViewController(qiitaError: qiitaError)
} else {
print("⚠️ ErrorDelegate: nil")
}
}
//データ取得
func fetch(success: @escaping ((_ result: [AirticleModel]?) -> Void),
failure: @escaping ((_ error: NSError?) -> Void)) {
//↓URLの設定
let reqParamModel = RequestParametersCreater(
dataType: .article,
pageNumber: pageNumber,
searchDict: searchDict,
sortdict: nil)
let urlText = reqParamModel.assembleItemURL(pageNumber: pageNumber)
guard let url = URL(string: urlText) else { return }
//↑URLの設定
let qiitaRequest = QiitaRequest()
//↓QittaRequestのメソッドrequestは引数にheadersを設置しなくてもリクエスト時にヘッダー情報を組み込むようにしている
qiitaRequest.request(url: url).response { response in
guard let data = response.data else {
return
}
//取得したデータを格納
guard let exportData = try? JSONDecoder().decode([AirticleModel].self, from: data) else {
print("An error occurred during decoding.")
if let exceptionData = try? JSONDecoder().decode(ErrorModel.self, from: data) {
//1日のリクエスト数が上限に達した場合
if let message = exceptionData.message,
let type = exceptionData.type {
print("message: \(message), type: \(type)")
self.showErrorView(qiitaError: .rateLimitExceededError)
} else {
self.showErrorView(qiitaError: .unexpectedError)
}
} else {
print("Failed to get error message.")
self.showErrorView(qiitaError: .unexpectedError)
}
failure(response.error as NSError?)
return
}
success(exportData)
}
}
1日のリクエスト数が上限に達した場合に
delegateメソッドを呼び出しています。
動作確認
開発終えての感想
途中何度かつまずくこともありましたが、
コードをレビューしていただいたKAORU MATARAIさんのおかげで
最後まで作りきることができました。
ありがとうございました。
今回得られた貴重な経験を個人アプリ・インターン採用・就活に生かそうと思います。
今回のアプリ開発で携わった方々
KAORU MATARAIさん→アプリ設計・デザイン・コードレビュー
PlayGround内の方々→テスト
shu(私)→主な実装