iOS
Swift

最近Swift書いていて可読性を上げるために意識していること

More than 1 year has passed since last update.

Const構造体

class ViewController: UIViewController {
   func action() {
     let defaultKey = "ThisIsKey"
     ValueStore.get(defaultKey)
   }
}

メソッドの中にキーや数値情報を書くと、他のクラス利用者が止む無く変更したい場合にメソッドに手を加える必要が出たりそもそもの変数の場所を探す必要が出て来る。

class ViewController: UIViewController {
   private struct Const {
     static let defaultKey = "ThisIsKey"
   }

   func action() {
     ValueStore.get(Const.defaultKey)
   }
}

このようにConst構造体にまとめることで、定数を一箇所に集約させることが出来る。

+Extensions.swift

Extensionを生やす場合、クラス名+Extensions.swiftという名前にしてExtensionsグループに入れている。

setupメソッド

主にsetupAppearancesetupSubscriberというメソッドをほぼ必ず作るようにしている。
これはviewDidLoad等のメソッドの中でUI装飾メソッドとRx/Gesture/delegate等のコールバックのセットアップ等が混在する事が要因で、このようにsetupメソッドとして分割することで修正箇所が明確になる

DataSourceクラスを作る

ViewControllerに書きがちなUITableViewDelegateはDataSourceクラスに分けている。
理由はViewControllerが肥大化するから。

class ViewController: UIViewController {
  @IBOutlet weak var tableView: UITableView!
  lazy var dataSource: ViewControllerDataSource = .init(with: self.tableView)

  override func viewDidLoad() {
    super.viewDidLoad()
  }
}

class ViewControllerDataSource: NSObject, UITableViewDelegate, UITableViewDataSource {
  convenience init(with tableView: UITableView) {
    tableView.delegate = self
    tableView.dataSource = self
  }
}

こんな感じ。
実際はViewModelクラスと一緒に作ってreloadDataのタイミング等を管理している。

didSetを使わない

hoge.uppperPadding = 40.0
というメソッドを呼んで、実際は

var upperPadding: CGFloat = 0.0 {
  didSet {
    //めっちゃ重い処理
  }
}

だったら怖いから。
プロパティへの代入は代入に留めたい。
プロパティ:軽い処理
メソッド:重めの処理
という印象があると思うので、それには逆らわないようにする。

`self` = selfを使わない

closureでselfを使う際に

action = { [weak self] in
  guard let `self` = self else { return }
  self.update()
}

みたいな感じでself?を潰す手法があるんだけど、これは
・ そもそもguard letself= self else { return }を忘れた時に気がつけなくなる
・ selfと性質が厳密には違うのに同じ名前を使いたくない
という理由です。

@IBOutletにprivateを付ける

外から見た時にアクセス出来る必要が無いのに補完に出たりするのでprivateにしておく。
またIBOutletは厳密にはnilである可能性があるので、そういったタイミングでアクセスさせないという意図もある。
タップイベント等を流す時はちゃんとPublishSubjectを生やしてviewDidLoadとかでbindする

Optionalの定義には= nilを書く

class Button: UIView {
  var color: UIColor?
}

となっていると、イニシャライザで初期化すべきなのか混乱するから。
Button(color: color)みたいなのがあるのかと思ってしまう。(structでもそういう動作だし)

class Button: UIView {
  var color: UIColor? = nil
}

こうなっていると、とりあえずcolorに関しては気にしなくていいと分かるし、初期値が明確になる。

TODOにワーニングを付ける

ObjC時代はTODOはワーニングだったが、Swiftからはワーニングにならなくなった。

RunScriptに

if [ "${CONFIGURATION}" = "Debug" ]; then
TAGS="TODO:"
echo "searching ${SRCROOT} for ${TAGS}"
find "${SRCROOT}" \( -name "*.swift" \) -print0 | xargs -0 egrep --with-filename --line-number --only-matching "($TAGS).*\$" | perl -p -e "s/($TAGS)/ warning: \$1/"
fi

を追加してワーニングにするようにしている。

R.swiftの色情報をHexで管理する

カラーリテラルやR.SwiftデフォルトのClrではカラーコードが分からないので、txtにHexを書いてClrに変換している。
https://github.com/noppefoxwolf/HexToClr

分かりにくい処理はPlaygroundファイルを同梱する

Variableって最初ストリーム流れるんだっけ…?
みたいな疑問が出たらすぐPlaygroundで確かめてXcodeWorkspaceにいれて残しておく。

冗長な処理はServiceやFactoryに隠す

どうしてもFoundationのメソッドで冗長な書き方をしないといけない場合がある(VisionAPIとか…)ので、そういうのはServiceとかに隠す。

(クリーンコード警察みたいでヤダナコレ…)
なんか思いついたら追記します