はじめに
こんにちは、学生でiOSアプリエンジニアをやっている者です。AdventCalendarは何を書こうかなーーっと思っている矢先、
コードを書いて丁度はや一年。
つまりプログラミングを始めて、一年ということなんですね。自分にとって。
そこで、いっっっちばん最初に作ったアプリのソースコードを見返すことにしました!良い機会だし。とても初心者の頃ですね。
この一年くらいで6つのプロジェクトに関わってきましたが、まだまだ至らない部分も多く感じると同時に成長した部分もあるなと感じます。
とりあえず、反面教師的な感じでみてください、、、。
見ていきますね〜。
概要
読んだ書籍を管理する「書籍管理アプリ」となります。
APIの部分では、ユーザー情報の管理と書籍情報の管理ができるようになっている仕様です。
今回は仕様やアーキテクチャー周りという難しい話よりは、実際にコードをみて懐かしんでいきます。
##Xcode開いてみました
ディレクトリの構成をしていない
ViewControllerのクラス、Cellのクラス、Requestなどのクラス群がバラバラで、スクリーン毎にさえディレクトリ構成がされていません。
個人開発や特に公開するつもりのないプロジェクトなら良いですが、特にチーム開発の際には酷く怒られる気がします(笑)
修正
そうですね。見返してみてみるとMVCでもMVVMでもないので(ViewControllerに全て書いていた)、とりあえずViewControllerとその他(StructやCustomClass)で分けておきました。
後述しますが、ViewController内で、
Viewを作成し、APIを叩き、データを保持し、リクエストを送り、Viewを更新する。
という、全ての処理を書いていたので正直分け様がなかったです。
図示するとこんな感じです。ViewControllerしかないという感じですね。
何がダメで、どうするべきなのか、ということは長くなるので記述しませんが、以下
iOS設計パターンの基本概念となるMVCです。
Webアプリケーションにおける原初MVCとAppleが提唱するCoCoa MVCは少々違うので、そこはご注意を。
モバイルアプリアーキテクチャ勉強会
Model-View-Controller
iOSアプリ設計パターン入門
##コード見ていこう〜
- viewControllerのClassと同じファイルに、データのオブジェクトが宣言されていますね。
まさに、ViewControllerでデータを扱おうとしています。ViewControllerが大きくなる予感がしますね。
struct BookDataModel {
var title: String
var value: Int
var date: String
var image: String
var id: Int
}
import UIKit
import APIKit
import Kingfisher
final class BookListViewController: UIViewController {
- 命名も気をつけたいところです。(未だに結構命名に苦しんでいる)
addButtonを例に出すと、プロパティの接頭辞に動詞(add)は避けたいです。
接頭辞が動詞の命名は、「〜する」という意味で用いられるメソッド(func )で使いたいな〜と思います。
addButtonだと、ボタンを追加する
というよく分からないメソッドが生まれますが。笑
final class BookListViewController: UIViewController {
private var addButton: UIBarButtonItem!
private var bookTableView: UITableView!
private var moreReadButton: UIButton!
- privateのメソッドは今でも積極的に使っています。(特にライフサイクルの中では)
一つのスコープ内で、色々な処理(UI作ったり、AutoLayout設定したり、API叩いたり)してしまうと一気に可読性を失っちゃいます。
何をしているかがわかる範囲でprivateメソッドを宣言してやると、分かりやすいですね。
(以下だと、UI作って、AutoLayout設定しているんだな〜と一発でわかる)
override func viewDidLoad() {
super.viewDidLoad()
// UI
applyNavigationItem()
applyBookTableView()
applyMoreButton()
// AutoLauout
setupConstraint()
}
private func applyNavigationItem() {
// 省略
navigationItem.rightBarButtonItem = addButton
}
private func applyBookTableView() {
bookTableView = UITableView(frame: view.frame, style: .plain)
// 省略
view.addSubview(bookTableView)
}
private func applyMoreButton() {
moreReadButton = UIButton()
// 省略
view.addSubview(moreReadButton)
}
- viewWillAppearで毎回FetchはするべきなのかはUX的に少し考えましたが、
最近MVVMやFluxで開発に携わっているのでこのような処理はViewControllerに直に書きたくないなと思えるようになっています。
(自身(ViewController)でリクエストを送り、そのままresponseを自身で持つ。んで何はともあれ自身でUIを更新。)
このプロジェクトは小さいものなので正直全然気にならないのですが、これが大規模でチーム開発をするとなると上記したアーキテクチャの部分はしっかりと考えるべきだなと身に締めて感じます。
override func viewWillAppear(_ animated: Bool) {
fetchBookList(limit: Const.limit*currentPage, page: Const.page)
}
private func fetchBookList(limit: Int, page: Int) {
Session.send(BookListRequest(limit: limit, page: page)) { [weak self] result in
switch result {
case .success(let response):
print(response)
self?.books = []
self?.books = response.result
self?.bookTableView.reloadData()
case .failure(let error):
print(error)
}
}
}
-
狭いスコープ内で宣言する類ならVCとか略称つけたいのですが(nextVC)、クラスが持つ時などの大きなスコープで宣言する時(showBookAddVC)は略称やめたいですね。忘れたり、他人が見ると、追うのがだるくなっちゃいます。
-
個人的な好みですけど、self.present(navBar, animated: true)と最近は、selfをつけたくなっちゃいます。笑
present -> 画面遷移! ってイメージよりも、
self.present -> 私(ViewControllerClass(型)のインスタンス(実体))が画面遷移します!
という文脈の方が、インスタンスを毎回意識するようになってて好きです。笑
@objc func showBookAddVC(_ : UIBarButtonItem) {
let nextVC = BookAddViewController()
let navBar = UINavigationController(rootViewController: nextVC)
present(navBar, animated: true)
}
キリがないと感じたのでこの辺で
まだまだあるんですけど、一旦この辺で。笑
一番最初このプロジェクトに、Swift自体を始めて1週間で手を出しましたが(当時ほぼスキルなし)、最初はとりあえずやってみるというコトが大事だったので全然これはこれで良かったと思っています。
ですか、丁度一年後見返した際に、もっといろんな手段があるよね〜。と全く違う視点(文法、ライブラリ、設計とか)で見れるようになったことは、良いことでした。
勉強は嫌いですが、頑張った先に違う景色が広がっているのはやっぱり面白いです。
これからも頑張ります!!
時間ができたら、機能追加しつつ、リファクタして、ライブラリとか設計とか試していきたい。