[Swift] コードレビュー方法・ベストプラクティス(その1)

2023/07/25に公開

コードレビューは、簡潔なコードベースを維持するために重要なだけでなく、異なるコーディングスタイル(同僚のコーディングスタイル)を学ぶ方法としても重要だと考えている。さらに、優れたコードレビューでは、PMやQAがテスト中に発見できないような問題を発見することができる。この記事は、iOSのレビューでチェックすべき項目を示すことを目的としてる、、、(理想的な世界では)。

不要な「import」が利用されているか

不要な import を使用しているかどうかを確認してください。
ビルドが壊れることはないが、どのライブラリにどのフレームワークが含まれているかを確認した方が良い。例えば、 Foundation はSwiftUIとUIKitに含まれている。なのでSwiftUIを使ったら、  Foundation import する必要がない。

レビューの前

import UIKit
import SwiftUI
import Foundation

class ViewController: UIViewController { ... }

レビューの後

import UIKit // FoundationはUIKitに含まれます。SwiftUIは使われていない。

class ViewController: UIViewController { ... }

型推論をさせているか

型推論を使用しているかどうかを確認してください。
Swiftで型推論を行うと、DoubleをIntとして解釈したり、FloatをDoubleとして解釈したりすることがあるので、変数を宣言するときには型を明示的に宣言することが最善である。特に数値の場合は、BoolやString、コレクション型よりも特に重要。

レビューの前

let myNumber = 0
let myDouble = 0.0 // 0.0じゃないとIntとして推論

レビューの後

let myNumber: Int = 0 // Int
let myDouble: Double = 0 // Double

protocol/delegateをextensionにリファクターしたか

デリゲートを以下のようにextensionとして分離しているか確認してください。必須ではないけど、読みやすくなる。

レビューの前

class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSourceDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        ...
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        ...
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        ...
    }
}

レビューの後

class HomeViewController: UIViewController { 
	override func viewDidLoad() {
		super.viewDidLoad()
		...
	}
 } 

extension HomeViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        ...
    }
}

extension NotificationViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        ...
    }
}

誤字があるか

当然だけど、誤字があるかどうか慎重に確認してください。

レビューの前

@IBOutlet var filtreButten: UIButton!

レビューの後

@IBOutlet var filterButton: UIButton!

高階関数を活用しているか(map, flatMap, reduce, filterなど)

Swiftの高階関数はとても便利だと思う。必須ではないが、読みやすくなると思う。

レビューの前

let words: [String] = ["room", "home", "train", "green", "heroe"]
var uppercasedWords: [String] = [String]()
 
for word in words {
    uppercasedWords.append(word.uppercased())
}

レビューの後

let words: [String] = ["room", "home", "train", "green", "heroe"]

let uppercasedWords = words.map({ word in
    return word.uppercased()
})

オプショナルを強制的アンラップしたか(@IBOutletを除く)

オプショナルを強制的アンラップすることは推奨されていない(クラッシュするリスクが上がるため)。
アンラップしたかどうかを確認すること。下記のようにアンラップするのは簡単でセーフコードになる。

レビューの前

var name: String? = nil

print("\(name!) letters")

レビューの後

// if let (条件が必要であれば)
// iOS16以下
if let unwrapped = name {
    print(unwrapped) 
} else {
    print("nil")
}

// iOS16以降
if let name {
    print(name)
} else {
    print("nil")
}

// guard let
guard let unwrapped = name else { return } 
print(unwrapped)

APIがdeprecatedか

Xcodeで警告が出るはずだが、要確認。
特に、SwiftUIのメソッドがすぐdeprecatedになる。

レビューの前

protocol yourDelegate: class { // Xcodeで警告が出るはず
...
}

レビューの後

protocol yourDelegate: AnyObject {
...
}

またこの続きがあるので、その2もお楽しみに👨‍🏫

Spacely, Inc. App Div.
Dean Thompson

参考

https://qiita.com/tsuta0856/items/4545bf8c2ffbfb05bb1f

https://medium.com/@ashokrawat086/code-review-checklist-483614b91821

https://youtube.com/watch?v=mV3bCwQtaVA&t=1697s

Discussion