概要
- 弊社のiOSチームではコーディング規則がある程度細かく定められている
- コードの可読性を上げ、コーディング品質を属人化させない目的
- 転職当時「どの順序で宣言するのがいいのかわからない...!」のような悩みが大きかったので、参考としてまとめます
プロパティ名
- プロパティは Lower Camel Case (最初は小文字、語頭が大文字)とする
- 宣言時には
:
を変数名に続けて入力し、空白1文字ののち型を宣言する
var playerId: Int
- アルファベットの省略語は大文字を用いる
var webURL: URL?
- ただし、変数名の頭にきた場合には小文字とする
var urlString: String?
- アクセスレベルによって
_
をつけるといったことは行わない
private var someFlag: Bool
var otherFlag: Bool
// NG
private var _someFlag: Bool
クロージャ
- 関数等の下は1行空けて、下のクロージャの間は開けない
func functionA() -> String? {
return "Hello"
}
- 引数の型は基本的に省略しない
-
(
と{
の間、}
と)
の間は省略なし -
{
の後ろ、}
の前は離す - キャプチャの前後は離す
let names: [String] = ["田中", "佐藤", "鈴木"]
names.enumurated().foreach({ [unowned self] (index: Int, name: String) in
print("index: \(index), name: \(name), \(self.functionA())")
})
-
switch
のクロージャは例外的に{
の直下の行から始める
switch status {
case .good: print("good!")
case .bad: print("bad...")
}
- クロージャーが複数重なる場合には例外的に
}
の上に一行開ける
self.test(onSuccess: {
print("OK")
}, onError {
print("NG")
})
switch
- 全ての case の処理が1行で簡潔にかける場合には1行にする
switch result {
case .success: print("success")
case .failure: print("failure")
- case によって2行以上必要な場合には case から改行して記載
- この場合各 case の間は1行開ける
switch result {
case .success:
print("success")
case .failure:
self.presentDialog(title: "Error!!", message: "result is error", completion: {
self.dismiss()
})
}
プロトコル/クラス/構造体/列挙型の定義
- 基本的には1ファイルにつき1つの定義に留める
- VIPERなどの設計思想を取り入れると必然的にプロトコル/クラスなどが増えるが、まとめてしまわない
- 定義時には
// MARK:
をつける-
// MARK
の上は2行、下は1行空けることを基本とする
-
- 複数のプロトコルの適合時、なるべくメソッドに関しては
extension
で宣言を分割する- プロパティは
extension
で宣言できないため、宣言部分に// MARK:
をつけてわかるようにしておく
- プロパティは
PseudoViewInterface.swift
import Foundation
// MARK: - PseudoViewInterface
protocol PseudoViewInterface {
/// 仮プロパティ
var title: String? { get }
/// 仮関数
func pseudoFunction() -> String?
}
PseudoViewController.swift
import UIKit
// MARK: - PseudoViewController
final class PseudoViewController: UIViewController {
// MARK: - PseudoViewController
var title: String? = nil
}
// MARK: - PseudoViewInterface
extension PseudoViewController: PseudoViewInterface {
func pseudoFunction() -> String? {
return "Hello!"
}
}
宣言の順序
- アクセスレベルで大分する
- グループ開発において
private
以下の実装は意識する必要がないという状態を作る
- グループ開発において
-
static let
>lazy var
>@Outlet
>private(set) var
>var
>let
>init
>deinit
>function
>デリゲートメソッド
の順に宣言する -
private
はMARK: - Private
以下に宣言する private
にできるものはprivate
で宣言する-
@IBOutlet
はUIの配置順に上から下になるように宣言する- ただし
[NSLayoutConstraint]
などの配列は後に回す
- ただし
final class PseudoViewController: ViewController {
/// ViewController の ID などの static にすべきもの
static let identity: String = "PseudoViewController"
/// 外部に出す必要がある Observable など
var someSignal: Signal<Void> {
self.someRelay.asSignal()
}
deinit {
self.disposable?.dispose()
}
func inject(dependency: PseudoDependencyProtocol) {
self.dependency = dependency
}
// MARK: - UIViewController
override func viewDidLoad() {
super.viewDidLoad()
self.setupUI()
}
// MARK: - Private
private lazy var dependencyId: String? = {
guard let dependency = self.dependency else { return nil }
return "prefix ****" + dependency.id
}
@IBOutlet private dynamic weak var someButton: UIButton!
@IBOutlet private dynamic var someConstraints: [NSLayoutConstraint]!
private var dependency: PseudoDependencyProtocol?
private var someFlag: Bool = false
private let someRelay: PublishRelay<Void> = PublishRelay<Void>()
private func setupUI() {
self?.navigationController.setNavigationBarHidden(true, animated: false)
}
}
プロパティの宣言
-
///
を利用して外部から参照した際にプロパティの利用目的がすぐわかるように宣言する - プロトコルの宣言においては、
Signal
などの外部ライブラリによる型を返却値に利用する場合にはRxCocoa.Signal
のように出自を明確にする
PseudoPresentation.swift
import RxSwift
import RxCocoa
import Foundation
// MARK: - PseudoPresentation
protocol PseudoPresentation {
/// デバイス名を表す Driver.
var name: RxCocoa.Driver<String?> { get }
/// デバイスIDを表す Driver.
var id: RxCocoa.Driver<Int> { get }
/// 入力をバインドする.
func bind(input: PseudoInput)
}
Player.swift
import Foundation
// MARK: - Player
struct Player {
/// プレイヤー名
let name: String
/// プレイヤーID
let playerId: Int
/// 体力
let hitPoint: Int
/// 魔力
let magicPoint: Int
}
コメント
-
// MARK: -
// FIXME:
// OPTIMIZE:
// MEMO:
///
//
を使う - 文章になっている場合には最後に
.
をつける
// MARK: -
- クラス名などの宣言、デリゲートメソッド、 Private の区分けなどに用いる
- MiniMap 等に線が表示されるようになるため便利だが乱用はしないこと
-
// MARK: -
の上2行下1行は空ける
// MARK: - PseudoStruct
struct PseudoStruct {
}
// FIXME:
- 実装中の仮対応コードなどにつける
- リリース時には
// FIXME:
は存在しないことが基本
func fetchData() -> Single<Result<Void, Error>> {
// FIXME: API定義後修正すること.
return .just(.success())
}
// TODO:
- 実装中の仮対応コードなどにつける
- モジュールの実装順等で対応不能な箇所や将来的な追加実装を見越す
-
// FIXME:
// OPTIMIZE
との違いは「タスク」として残っている意味合いが強い点
-
// TODO: アイコン変更機能が実装された場合にはここで変更する.
self.iconImage = R.image.icon_id_1()
// OPTIMIZE:
- レビューにおいて正常に動くため工数などの都合で一旦パスしたがリファクタリングが必要とされた箇所
// OPTIMIZE: 命名を整理する.
private let somethingHasTooLongNameBehaviorRelay: BehaviorRelay<Bool>
// MEMO:
- 端末ごとの特殊処理や複雑なロジックなど「なぜこうなったのか」が一目でわからない箇所につける
// MEMO: フォントサイズを iPhone SE(2nd) のウィンドウ横幅(375)を基準として調整する.
let ratio: CGFloat = UIScreen.main.bounds.size.width / 375
///
- クラスの
internal
プロパティなど外部から参照するものにつける-
///
でコメントを書くことで参照時にそのコメントが表示される
-
/// プレイヤーID
let playerId: Int
//
- 複数行に渡ってコメントをする必要がある場面で利用
- どのコメントも並列的に重要、
// MEMO:
で強調するほどではないなど- 割と柔軟に使うイメージ、後で読んだ人が困りそうなところにはとりあえず書いておく
func fibonacciNumber(number: Int) -> Int {
// フィボナッチ数は2個前までの項を足した値.
// number が負の値の場合には -1 を返却する(不正).
if number < 0 { return -1 }
// 第0項は0とする(定義).
if number == 0 { return 0 }
// 第1項は1とする(定義).
if number == 1 { return 1 }
// 第n項(n>1)は f(n-1) + f(n-2).
return self.fibonacciNumber(number - 1) + self.fibonacciNumber(number - 2)
}