- 投稿日:2019-02-11T23:32:02+09:00
[cocoa][swift]作譜用言語PL/0 表駆動の構文解析
『Algorithms + Data Structures = Programs』は、ニクラウス・ヴィルト氏の著名な書籍で、翻訳された書籍の表題は『アルゴリズム+データ構造=プログラム』だ。サンプルは、Pascalで記述され構造化プログラミングのバイブル的な書籍だ。
その次の版は、『アルゴリズムとデータ構造』と『翻訳系構成法序論』の二冊に分かれ、Modula-2で記述されている。その次の版は、Oberonで記述されているらしいが、残念ながら入手は困難だ。
ニクラウス・ヴィルト氏は、AppleのObject Pascalの開発にも関与したという話を聞いたことがある。
『翻訳系構成法序論』の感想は、最少限度の知識のみを必要とし、簡素な内容となっていて、初学者の教科書としては理想的ではないかと思っている。
それでは、続きを始める。
前回は、ゴリゴリと記述していたコードを「表駆動の構文解析」では、汎用的なコードを処理するという内容だ。
読み取った記号が終端記号か非終端記号か、そして、次の処理に移動するのか、if分の条件分岐のように、別候補に移動するのかを保持する表、Swiftでは構造体やクラスがそれに対応するので、実装してみる。
class Node { public var successor: Node? = nil public var alternative: Node? = nil public var terminal: Bool = true public var terminalSymbol: Character = "\0" public var nonterminalSymbol: Header? = nil }上記では未定義だった見出しに相当するのが以下だ。
class Header { public var symbol: Character = "\0" public var entry: Node? = nil }これを使いと前回の分析子の処理は以下となる。
func parse(goal: Header, match: inout Bool) { var s: Node? = goal.entry repeat { if s!.terminal { if s!.terminalSymbol == ch { match = true readChar() } else { if s!.terminalSymbol == empty { match = true } else { match = false } } } else { parse(goal: s!.nonterminalSymbol!, match: &match) } if match { s = s!.successor } else { s = s!.alternative } } while s != nil }ソースコード
GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/mac/pl0 - GitHub【関連情報】
Cocoa.swift 2019-02
Cocoa.swift
Cocoa勉強会 関東
MOSA
Cocoa練習帳
Qiita
- 投稿日:2019-02-11T19:46:14+09:00
Swift タプルの扱い
タプル(tuple)
備忘録
swift v4.2.1
記述
let languages = ("English", "Japanese", "French", "German")型指定
型宣言する場合は値の数書く
let tupleType: (String, Int) = ("Swift", 1234);型推論
var tupleInference = (111, 222, 333) // 型推論で(Int, Int, Int) tupleInference = (1, "文字列", 999) // error(Cannot assign value of type '(Int, String, Int)' to type '(Int, Int, Int)')取り出し
let values = (11, 22) let (value1, value2) = values let result = (value2 / value1) print(value1, value2) // "11 22\n"インデックスでのアクセス
let values = (100, 200) let value1 = values.0 let value2 = values.1 print(value1, value2) // "100 200\n"var values = ("AAA", "bbb") values.1 = "BBB" print(values) // "("AAA", "BBB")\n"ラベル付き
let length = (height: 200, width: 300) let size = lenght.height * length.width print(size) // "60000\n"var person: (lastName: String, firstName: String, age: Int) person.lastName = "Blanks" person.firstName = "Billy" person.age = 63 print(person) // "(lastName: "Blanks", firstName: "Billy", age: 63)\n"
- 投稿日:2019-02-11T17:40:10+09:00
Firebaseを活用してUniversal Linksで他人にデータを渡す
活用した状況
checka!というiOSアプリを開発しています。このアプリはTrelloのような、グループで使えるチェックリスト管理アプリです。このなかで、自分のグループに知人を招待するためにはID文字列を渡す必要がありました。その際にUniversal Linksを使えば簡単に実現できることはわかっていたのですが、
- バックエンドを用意したくない
- ドメインもめんどくさいからとりたくない
- FirebaseHostingやAmazonS3のドメインそのままの感じは嫌だ
という感じでした。そのときにFirebase Dynamic Linksを使えば簡単に、しかもドメインもそれなりのもので実現することができた話です。
TL;DR
- Dynamic Linksのドメインは
*.page.link
なのでそれなりのドメインにできる- アプリがインストールされている場合はアプリが開かれ、インストールされていない場合のリダイレクト先を設定できる
- バックエンドいらずでお手軽Universal Linksができる
事前準備
Firebase側
Firebaseのセットアップを行ってください。下記が公式リファレンスなのでこれに従ってやれば詰まることはないかと思います。
https://firebase.google.com/docs/guides/?hl=ja
iOS側
Associated Domainsの設定が必要です。2つやることがあります。
1. AppIDにAssociated Domainsの設定をする
Apple Developerのサイトに行って、対象のApp IDを開いて下記のようにAssociated DomainsをEnabledにしてください。
2. XcodeからCapabilitiesでAssociatedDomainsの設定をする
この部分で、AssociatedDomainsをONにしておいてください。Dynamic Linksの設定が終わったら追加で記入します。
Dynamic Linksの設定
利用開始
Firebaseのコンソールを開き、対象のプロジェクトを選択して、左側のメニューから「Dynamic Links」を開いてください。この画面はころころ変わりそうな気もするのであれですが、下の画像のような内容が出てると思うので、「始める」を押してください。
サブドメインの設定
サブドメインの設定を求められるのでいい感じのサブドメインを決めましょう。checka!の場合は、
checka.page.link
です。そのままですね。この部分は早いものがちだと思うので、取られていたら仕方ないという感じですね。Dynamic Linkの作成
新規作成
こんな感じの画面になっていると思うので、「新しいダイナミックリンク」を押してください。
パスの設定
Dynamic Linksは短縮URLの作成ができるので、今回の用途的には少し違和感があるかもしれません。URL接頭辞として、すきな文字列を入力しましょう。checka!のグループ参加の場合は
/join_group
しました。ちなみに、2019/02/11現在ではパスを切ることはできませんので、/group/join
にはできませんでした、残念。ディープリンクの設定
今回は使わないので、ディープリンクURLはAppStoreのURLを入れて、リンク名もアプリ名を入れておきましょう。
iOS用の設定
下の「ディープリンクをiOSアプリで開く」にチェックを付けて、セレクトボックスから対象のアプリを開きましょう。ここで選択されたアプリのbundleIDが、端末にインストールされているアプリのbunldeIDと異なっていると、Universal Linksによるアプリ起動ができないのでご注意ください。また、AppStoreIDやTeamIDの入力が必要になります。AppStoreIDは、checka!だと
https://itunes.apple.com/jp/app/id1451433619
の数字の部分、TeamIDはAppleDevelopterのAppIDのPrefixの部分です。選択すると、下記のような項目がでてきます。ここでは、アプリがインストールされていない場合の挙動を設定できます。自分は上の「アプリのAppStoreページ」を選択しましたが、必要であればカスタムURLの設定を行うことができるようです。
Android用の設定
Androidでも利用する場合は設定を行う必要があります。今回は割愛します。
詳細オプション
checka! では、一番下のチェックボックスにはチェックを入れています。ここのチェックを入れていない場合、インストールされていない端末で開くと、Firebase側の画面に一度アクセスして、そこにボタンがあり、そのボタンを押すとAppStoreへアクセスすることになってしまい、自分としては微妙でした。よって、チェックをいれています。
完了
ここまででDynamic Linksの設定は完了です。ここまでやれば、
https://[your subdomain].page.link/apple-app-site-association
にアクセスすると、Universal Linksに必要な設定項目がJSONとして返却されているかと思います。アプリの設定
Associated DomainsのAppLinks設定
先程のXcodeのCapabilitiesの設定を開き、Associated Domainsの項目の+を押して、
applinks:[your subdomain].page.link
を追加しましょう。これがないとアプリで起動できません。試す
Dynamic Linksで設定した項目に応じたURL
https://[your subdomain].page.link/[your path]
に、今までの設定がされたアプリをインストールした端末でアクセスしてみましょう。アプリが起動するはずです。ハンドリング
Dynamic LinksのページにはSDKを使ってハンドリングしていますが、今回は不要です。
AppDelegate.swift
で下記のような感じでハンドリングできます。func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let incomingURL = userActivity.webpageURL else { return false } // incomingURLをゴニョゴニョする return true }checka!では
https://checka.page.link/join_group?id=[id]
でアクセスされた場合、パラメータに渡されたidを入力した状態でグループ参加の画面を開いくようにしています。ちなみに、この機能は2019/02/11審査に出したので、執筆時点ではまだリリースされていません。おわりに
自分としてはとても楽をして、ユーザー体験を高めることができたのでとてもありがたかったです。よければTwitterフォローしてください。
Twitter: @_mogaming
- 投稿日:2019-02-11T15:15:38+09:00
iOSアプリのデバッグ方法まとめ
とりあえずprintしてみる
基本ですね。
print(anyVariant) //変数の中身が見たいとき print(type(of: anyVariant)) //変数の型が知りたい中身が見たいときでなくても、そのロジック通ってるか見たいときにも使えます。
原始的なデバッグ法です。
あまりお行儀はよくない扱いみたいで、次に紹介するブレイクポイントを仕込むほうがまっとうなやり方でしょう。ブレイクポイントを仕込む
Xcodeの機能で、ブレイクポイントというものがあります。
使い方は簡単です。
↑この例では、15行目にブレイクポイントを仕込んでいます。
行番号をクリックすると、その行にブレイクポイントが設定されます。
画像だと15行の番号が青くなってますね。作成したプログラム動かすと、そのブレイクポイントに来たときに実行が一時停止して、
その時点の変数についての情報が見れます。便利なんですが、アプリの動作を一度止めてしまうのは、利点でもあり、欠点でもあります。
たとえば電卓アプリをつくっていて、シミュレータで各ボタンを押したときの変数の状態を見たいときに、
ボタン1つ押すたびに動作が止まってしまうと、いちいち解除してやらないといけないので、めんどくさいです。sleepを入れたい
デバッグ方法とはちょっと逸れるかもしれませんが、sleep入れたいときないですか?
そういうときはこれで止まります。処理がx秒止まるThread.sleep(forTimeInterval: 3.00) //3秒WaitただこのThreadのsleepを使うと、プログラムの処理が全停止します。
たとえばサーバから重いデータをダウンロードするときに、
一旦sleepで待たせて、ダウンロードを完了させて、後続処理につなぎたい、
という風なときは、sleep中はダウンロード処理も止まるので、その用途には使えません。
そんなときは下記で止めるといけました。x秒止まる(実行中の処理は動作)RunLoop.current.run(until: Date.init(timeIntervalSinceNow: 3.0))
- 投稿日:2019-02-11T14:46:52+09:00
UITableView の区切り線をカラフルにしたい!
こんな感じにしたい!
すべての区切り線が同じ色で良い場合
全部同じで良い場合には、
tableView.separatorColor
で対応できます。
例えば、下記の例では、区切り線は赤色になります。swiftoverride func viewDidLoad() { super.viewDidLoad() tableView.separatorColor = .red }ではそれぞれのセルで異なる色にするにはどうすれば良いか…
カスタムセルでやる!
実際のところ、カスタムセルでも区切り線の色を個別に変えることはできなそうです
なので、自分で区切り線 (っぽいUIview
) を作ることで対応します。下記のように、セルの下部に区切り線に見えるような
UIView
を配置し、区切り線に見せかけます。swiftclass CustomTableViewCell: UITableViewCell { // 区切り線に見せかける view private let separator: UIView = { let view = UIView() return view }() override init(style: UITableViewCellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(separator) // contentView の最下部に高さの値を小さくして配置することで、区切り線に見せかける separator.translatesAutoresizingMaskIntoConstraints = false separator.heightAnchor.constraint(equalToConstant: 1).isActive = true separator.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true separator.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true separator.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func setColor(color: UIColor) { separator.backgroundColor = color } }あとは、
TableViewController
内で…。
tableView
の区切り線を非表示にする (tableView.separatorStyle = .none
)。swiftoverride func viewDidLoad() { super.viewDidLoad() tableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "CustomTableViewCell") tableView.separatorStyle = .none }カスタムセルを使うようにする。
swiftoverride func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "CustomTableViewCell") as! CustomTableViewCell cell.textLabel?.text = String(indexPath.row + 1) cell.setColor(color: colors[indexPath.row]) // 好きな色を設定する return cell }これで、あたかもカラフルな区切り線ができたかのように見せることができます!
終わりに
今回の例のように区切り線をカスタマイズしたいという要望は少ないかもしれません。
ですが、どうしてもそういうデザインにしたいけれども、提供されているプロパティでは対処できない時には、
自分でそう見えるように工夫するという選択肢が頭の中にあると、解決に一歩近づけるのかなと思っています。
- 投稿日:2019-02-11T13:01:39+09:00
iOSでパスワードをPBKDF2でハッシュ化する
パスワードを安全に保存する
iOSでパスワードを保存したい時は、キーチェーンを利用する場合がほとんどだと思います。このキーチェーンはパスワードを「暗号化」して保存する仕組みになっています。
ただ、昨今パスワード漏洩が大きな問題となっている為、やはりクリティカルなパスワードは「ハッシュ化」して保存したいという場合があります。
(つい先日も大規模な漏洩があったので、急遽「パスワードはハッシュ化してくれ」って言われたのがこの記事のきっかけだとか・・・・)iOSの場合、PBKDF2であれば特に外部のライブラリを使うことなくハッシュ化を実装できます。
ただ、ちょっと実装がいるので記事としてまとめました。PBKDF2の実装
まず
CommonCrypto
というライブラリをインポートします。
(現在はこの一文だけでインポートは完了します)import CommonCrypto次にPBKDF2の設定値を決めておきます。
let iterations = UInt32(100000) // イテレーション回数 let prf = kCCPRFHmacAlgSHA256 // 利用するハッシュ関数 let saltLength = 64 // ソルトの長さ let hashedLength = 256 // 出力されるハッシュの長さ設定値によってセキュリティの強度が変わるので、ここの設定は案件毎にセキュリティに詳しい人と相談して決めましょう。
なお、ハッシュ関数については、
設定値 kCCPRFHmacAlgSHA1 HMAC-SHA1 kCCPRFHmacAlgSHA224 HMAC-SHA224 kCCPRFHmacAlgSHA256 HMAC-SHA256 kCCPRFHmacAlgSHA384 HMAC-SHA384 kCCPRFHmacAlgSHA512 HMAC-SHA512 から選択できます。
設定値が決まれば、いよいよハッシュ化の処理です。
ハッシュ化はCCKeyDerivationPBKDF
というメソッドを使いますが、元はCのAPIなのでSwiftからはちょっと使いにくいです。
それで、次のようなSwift用にラップしたメソッドを作ります。func pbkdf2(password: String, salt: Data, iterations: UInt32) -> Data { var hashed = Data(count: hashedLength) let saltBuffer = [UInt8](salt) let result = hashed.withUnsafeMutableBytes { data in CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2), password, password.count, saltBuffer, saltBuffer.count, CCPseudoRandomAlgorithm(prf), iterations, data, hashedLength) } guard result == kCCSuccess else { fatalError("pbkdf2 error") } return hashed }また、パスワードをハッシュ化する場合のソルトは、ランダムで生成されたものが良いのでソルトを生成するメソッドも用意します。
func generateSalt(length: Int) -> Data { return Data(bytes: (0..<length).map { _ in UInt8.random(in: 0...UInt8.max) }) }アプリで利用する
アプリ上でハッシュ化してパスワードを保存する場合、
- パスワードをハッシュ化する
- 入力されたパスワードと保存済のハッシュ化されたパスワードを比較
という2つの処理が必要です。
パスワードをハッシュ化する
ユーザに決めてもらったパスワードを保存する時のコードです。
func encode(password: String) -> (hash: String, salt: String) { // ランダムなソルトを生成する let salt = generateSalt(length: saltLength) // パスワードをハッシュ化する let hash = pbkdf2(password: password, salt: salt, iterations: iterations) // 保存しやすいようにBase64化しておく return (hash: hash.base64EncodedString(), salt: salt.base64EncodedString()) }アプリから呼び出す時は以下のようになります。
let password = "ユーザが入力したパスワード" let result = encode(password: password) print("ハッシュ化されたパスワード: ", result.hash) print("ソルト: ", result.salt)あとは、ハッシュ化されたパスワードとソルトの両方を保存します。
保存先はキーチェーンにしておくとより確実かと思います。なお、イテレーション回数が変わるとハッシュ化の結果が違ってきますので、もしイテレーション回数を可変する場合はイテレーション回数も保存が必要です。
パスワードを比較する
次に、保存されているパスワードとユーザが入力したパスワードを比較する時のコードです。
func verify(password: String, hash: String, salt: String) -> Bool { // Base64化されたソルトをDataに戻す guard let saltData = Data(base64Encoded: salt) else { return false } // 保存されていたソルトを使って入力したパスワードをハッシュ化する let inputHash = pbkdf2(password: password, salt: saltData, iterations: iterations) // ハッシュ化されたパスワードと保存済のハッシュ化されたパスワードを比較 return inputHash.base64EncodedString() == hash }アプリから呼び出す時は以下のようになります。
// let savedHash = 保存しておいたハッシュ化されたパスワード(Base64) // let savedSalt = 保存しておいたソルト(Base64) let ok = verify(password: "ユーザが入力したパスワード", hash: savedHash, salt: savedSalt) print("結果: ", ok) // true let ng = verify(password: "違うパスワード", hash: savedHash, salt: savedSalt) print("結果: ", ng) // false参考: ハッシュ化は必要か?
一般的にパスワードを安全に保存しておくには「暗号化」ではなく「ハッシュ化」しておくことが必要1だと言われています。
というのも、パスワードが漏れてしまった時に、ハッシュ化していれば簡単に元のパスワードに戻すことができませんが、暗号化したパスワードの場合は、復号する為の鍵も一緒に漏れると簡単に元のパスワードに戻されてしまうからです。
では、ハッシュ化せずにキーチェーンに保存しておくのは安全では無いのか?と言われると、一概にそうは言えません。詳しくは参考資料2やiOSのセキュリティガイドなどを参照していただきたいのですが、キーチェーンのデータの復号用の鍵はアプリ側で保管されていなかったり、暗号化専用のハードが用意されていたりと、暗号化したデータと一緒に鍵も漏れてしまうということが起こりにくいように色々と対策が取られています。
という訳で、一般的な個人のパスワード程度であれば、このキーチェーンへの保存で十分に安全と言えますし、Appleのリファレンスでもパスワードはキーチェーンへ保存するよう記載されています。
ただ、やっぱりパスワード系はハッシュ化していないと心配で眠れないとか、なんか気持ち悪いって場合は、そんなに実装コストのかかるものでも無いのでさくっとハッシュ化してしまいましょう!
ただし、ハッシュ化して保存すると元のパスワードに戻せなくなる為、平文のパスワードが必要な場合はハッシュ化ではなく暗号化での保存が必要です。例えば、Safariのパスワード保存のようにアプリから別サービスへパスワードを使ってログインをするような場合は、暗号化で保存しないといけません。 ↩
https://github.com/OWASP/owasp-mstg/blob/master/Document/0x06d-Testing-Data-Storage.md ↩
- 投稿日:2019-02-11T11:40:58+09:00
SwiftLintの全ルール一覧(Swift 4.2版)
はじめに
すでに SwiftLintのルールをまとめてくださっている方 がいらっしゃいますが、約2年ほど更新されていなかったので、自分でもまとめてみました。
SwiftLintの概要や導入については以下をご参照ください。
Swiftの静的解析ツール「SwiftLint」のセットアップ方法 - Qiita注意
公式ページのルールを簡単に翻訳してまとめたものです。
英語に慣れている方は公式ページを直接見るのがいいです。
https://github.com/realm/SwiftLint/blob/master/Rules.mdまた、間違っている箇所や不適切な箇所がありましたら教えていただけると嬉しいです。
環境
- Swift:4.2.1
- Xcode:10.1 (10B61)
- SwiftLint:0.30.1
Default
デフォルトで有効になっているルールの一覧です。
Block Based KVO
Swift 3.2以降の場合、新しいブロックベースのKVO APIとキーパスを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#block-based-kvo// bad class Foo: NSObject { override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) { } } // good let observer = foo.observe(\.value, options: [.new]) { (foo, change) in print(change.newValue) }Class Delegate Protocol
デリゲートプロトコルはクラスのみであるべきで、弱参照されることができます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#class-delegate-protocol// bad protocol FooDelegate { } // good protocol FooDelegate: class { }Closing Brace Spacing
右括弧で閉じ括弧を閉じる場合、間にスペースを含めるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#closing-brace-spacing// bad [].map({ } ) // good [].map({ })Closure Parameter Position
クロージャのパラメータは開き括弧と同じ行にあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#closure-parameter-position// bad [1, 2].map { number in number + 1 } // good [1, 2].map { number in number + 1 }Colon
:
は、型の指定時には識別子の後ろ、ディクショナリではキーの後ろにあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#colon// bad let abc:Void let abc :Void let abc : Void let abc: [String:Int] let abc: [String :Int] let abc: [String : Int] // good let abc: Void let abc: [String: Int]Comma Spacing
カンマの前にスペースがあるべきではなく、カンマの後ろには1つの半角スペースがあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#comma-spacing// bad func abc(a: String,b: String) { } func abc(a: String ,b: String) { } func abc(a: String , b: String) { } // good func abc(a: String, b: String) { }Compiler Protocol Init
ExpressibleByArrayLiteral
のようなコンパイルプロトコルで定義されているイニシャライザは直接呼び出すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#compiler-protocol-init// bad let set = Set(arrayLiteral: 1, 2) let set = Set.init(arrayLiteral: 1, 2) // good let set: Set<Int> = [1, 2] let set = Set(array)Control Statement
if
,for
,guard
,switch
,while
,catch
文は条件や引数を不必要に括弧で括るべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#control-statement// bad if (condition) { } for (item in collection) { } guard (condition) else { } switch (foo) { } while (condition) { } do { } catch (let error) { } // good if condition { } for item in collection { } guard condition else { } switch foo { } while condition { } do { } catch let error { }Custom Rules
正規表現を指定してカスタムルールを作成できます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#custom-rulesCyclomatic Complexity
関数内は複雑にすべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#cyclomatic-complexity// bad func f1() { if true { if true { if false { } } } if false { } let i = 0 switch i { case 1: break case 2: break case 3: break case 4: break default: break } for _ in 1...5 { guard true else { return } } } // good func f1() { if true { for _ in 1..5 { } } if false { } }Deployment Target
可用性のチェックまたは属性は、デプロイメントターゲットが満たす古いバージョンを使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#deployment-target// bad @available(iOS 6.0, *) class A { } if #available(iOS 6.0, *) { } // good @available(iOS 12.0, *) class A { } if #available(iOS 12.0, *) { }Discarded Notification Center Observer
ブロックを使って通知を登録するとき、返される不透明なオブザーバは後で削除できるように格納すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#discarded-notification-center-observer// bad nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil) { } // good let foo = nc.addObserver(forName: .NSSystemTimeZoneDidChange, object: nil, queue: nil) { }Discouraged Direct Initialization
有害な可能性がある型を直接初期化すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#discouraged-direct-initialization// good let foo = UIDevice.current let foo = Bundle.main let foo = Bundle(path: "bar") let foo = Bundle(identifier: "bar") // bad let foo = UIDevice() let foo = Bundle()Duplicate Imports
インポートは1回のみ行うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#duplicate-imports// bad import Foundation import Dispatch import Foundation // good import Foundation import DispatchDynamic Inline
dynamic
と@inline(__always)
を同時に使ってはいけません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#dynamic-inline// bad class C { @inline(__always) dynamic func f() { } } // good class C { dynamic func f() { } } class C { @inline(__always) func f() { } }Empty Enum Arguments
列挙型が連想型と一致しない場合、引数を省略すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-enum-arguments// bad switch foo { case .bar(_): break } switch foo { case .bar(): break } // good switch foo { case .bar: break }Empty Parameters
Void ->
より() ->
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-parameters// bad let abc: (Void) -> Void = { } // good let abc: () -> Void = { }Empty Parentheses with Trailing Closure
トレイリングクロージャを使う場合、メソッドの呼び出し後に空の括弧を記述すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-parentheses-with-trailing-closure// bad [1, 2].map() { $0 + 1 } // good [1, 2].map { $0 + 1 }File Line Length
ファイル内はあまりにも多くの行にまたがるべきではないです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#file-line-length// bad print("swiftlint") print("swiftlint") print("swiftlint") …For Where
for文内にif文が1つのみ存在する場合、
where
句を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#for-where// bad for user in users { if user.id == 1 { user.myFunction() } } // good for user in users where user.id == 1 { user.myFunction() }Force Cast
強制キャスト(
as!
)は使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#force-cast// bad NSNumber() as! Int // good NSNumber() as? IntForce Try
強制トライ(
try!
)は使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#force-tryfunc a() throws { } // bad try! a() // good do { try a() } catch { // エラー処理 }Function Body Length
関数内はあまりにも多くの行にまたがるべきではないです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#function-body-lengthFunction Parameter Count
関数の引数の数は少なくすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#function-parameter-count// bad func f(a: Int, b: Int, c: Int, d: Int, e: Int, f: Int) { } // good func f(a: Int, b: Int, c: Int, d: Int, e: Int) { }Generic Type Name
ジェネリック型は英数のみを含み、大文字で始まり、1〜20文字にすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#generic-type-name// bad func foo<T, U_Foo>(param: U_Foo) -> T { } func foo<T, u>(param: u) -> T { } // good func foo<T, U>(param: U) -> T { }Identifier Name
識別子名は英数のみを含み、小文字で始まるか、大文字のみを含むべきです。
上記以外では、変数名は静的かつ不変と定義されている場合は大文字から始まることがあります。
変数名は長過ぎたり短過ぎたりしてはいけません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#identifier-name// bad let MyLet = 0 let _myLet = 0 let id = 0 // good let myLet = 0Implicit Getter
読取専用のコンピューテッドプロパティとサブスクリプトには
get
キーワードを使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#implicit-getter// bad class Foo { var foo: Int { get { return 20 } } } // good class Foo { var foo: Int { return 20 } }Inert Defer
defer
が親スコープの終わりにある場合、その場所で実行されます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#inert-defer// bad func example() { print("other code") defer { /* deferred code */ } } // good func example() { defer { /* deferred code */ } print("other code") }Is Disjoint
Set.intersection(_:).isEmpty
よりSet.isDisjoint(with:)
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#is-disjoint// bad _ = Set(syntaxKinds).intersection(commentAndStringKindsSet).isEmpty // good _ = Set(syntaxKinds).isDisjoint(with: commentAndStringKindsSet)Large Tuple
タプルはあまりにも多くのメンバーを持つべきではありません。
代わりにカスタムタイプを作成すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#large-tuple// bad let foo: (Int, Int, Int) // good let foo: (Int, Int)Leading Whitespace
ファイルは先頭にスペースを含んではいけません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#leading-whitespace// bad // foo // good // fooLegacy CGGeometry Functions
構造体のエクステンションのプロパティとメソッドは、従来の関数より優先すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-cggeometry-functions// bad CGRectGetWidth(rect) CGRectGetHeight(rect) CGRectGetMinX(rect) CGRectGetMidX(rect) CGRectGetMaxX(rect) CGRectGetMinY(rect) CGRectGetMidY(rect) CGRectGetMaxY(rect) CGRectIsNull(rect) CGRectIsEmpty(rect) CGRectIsInfinite(rect) CGRectStandardize(rect) CGRectIntegral(rect) CGRectInset(rect, 10, 5) CGRectOffset(rect, -2, 8.3) CGRectUnion(rect1, rect2) CGRectIntersection(rect1, rect2) CGRectContainsRect(rect1, rect2) CGRectContainsPoint(rect, point) CGRectIntersectsRect(rect1, rect2) // good rect.width rect.height rect.minX rect.midX rect.maxX rect.minY rect.midY rect.maxY rect.isNull rect.isEmpty rect.isInfinite rect.standardized rect.integral rect.insetBy(dx: 5.0, dy: -7.0) rect.offsetBy(dx: 5.0, dy: -7.0) rect1.union(rect2) rect1.intersect(rect2) rect1.contains(rect2) rect.contains(point) rect1.intersects(rect2)Legacy Constant
構造スコープ定数は従来のグローバル定数より優先すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-constant// bad CGRectInfinite CGPointZero CGRectZero CGSizeZero NSZeroPoint NSZeroRect NSZeroSize CGRectNull CGFloat(M_PI) Float(M_PI) // good CGRect.infinite CGPoint.zero CGRect.zero CGSize.zero NSPoint.zero NSRect.zero NSSize.zero CGRect.null CGFloat.pi Float.piLegacy Constructor
Swiftのコンストラクタは従来のコンビニエンス関数より優先すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-constructor// bad CGPointMake(10, 10) CGPointMake(xVal, yVal) CGPointMake(calculateX(), 10) CGSizeMake(10, 10) CGSizeMake(aWidth, aHeight) CGRectMake(0, 0, 10, 10) CGRectMake(xVal, yVal, width, height) CGVectorMake(10, 10) CGVectorMake(deltaX, deltaY) NSMakePoint(10, 10) NSMakePoint(xVal, yVal) NSMakeSize(10, 10) NSMakeSize(aWidth, aHeight) NSMakeRect(0, 0, 10, 10) NSMakeRect(xVal, yVal, width, height) NSMakeRange(10, 1) NSMakeRange(loc, len) UIEdgeInsetsMake(0, 0, 10, 10) UIEdgeInsetsMake(top, left, bottom, right) NSEdgeInsetsMake(0, 0, 10, 10) NSEdgeInsetsMake(top, left, bottom, right) CGVectorMake(10, 10) NSMakeRange(10, 1) UIOffsetMake(0, 10) UIOffsetMake(horizontal, vertical) // good CGPoint(x: 10, y: 10) CGPoint(x: xValue, y: yValue) CGSize(width: 10, height: 10) CGSize(width: aWidth, height: aHeight) CGRect(x: 0, y: 0, width: 10, height: 10) CGRect(x: xVal, y: yVal, width: aWidth, height: aHeight) CGVector(dx: 10, dy: 10) CGVector(dx: deltaX, dy: deltaY) NSPoint(x: 10, y: 10) NSPoint(x: xValue, y: yValue) NSSize(width: 10, height: 10) NSSize(width: aWidth, height: aHeight) NSRect(x: 0, y: 0, width: 10, height: 10) NSRect(x: xVal, y: yVal, width: aWidth, height: aHeight) NSRange(location: 10, length: 1) NSRange(location: loc, length: len) UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 10) UIEdgeInsets(top: aTop, left: aLeft, bottom: aBottom, right: aRight) NSEdgeInsets(top: 0, left: 0, bottom: 10, right: 10) NSEdgeInsets(top: aTop, left: aLeft, bottom: aBottom, right: aRight) UIOffset(horizontal: 0, vertical: 10) UIOffset(horizontal: horizontal, vertical: vertical)Legacy Hashing
hashValue
をオーバーライドするのではなく、hash(info:)
関数を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-hashing// bad struct Foo: Hashable { let bar: Int = 10 public var hashValue: Int { return bar } } // good struct Foo: Hashable { let bar: Int = 10 func hash(into hasher: inout Hasher) { hasher.combine(bar) } }Legacy NSGeometry Functions
従来の関数より構造体のエクステンションのプロパティとメソッドを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-nsgeometry-functions// bad NSWidth(rect) NSHeight(rect) NSMinX(rect) NSMidX(rect) NSMaxX(rect) NSMinY(rect) NSMidY(rect) NSMaxY(rect) NSEqualRects(rect1, rect2) NSEqualSizes(size1, size2) NSEqualPoints(point1, point2) NSEdgeInsetsEqual(insets2, insets2) NSIsEmptyRect(rect) NSIntegralRect(rect) NSInsetRect(rect, 10, 5) NSOffsetRect(rect, -2, 8.3) NSUnionRect(rect1, rect2) NSIntersectionRect(rect1, rect2) NSContainsRect(rect1, rect2) NSPointInRect(rect, point) NSIntersectsRect(rect1, rect2) // good rect.width rect.height rect.minX rect.midX rect.maxX rect.minY rect.midY rect.maxY rect.isEmpty rect.integral rect.insetBy(dx: 5.0, dy: -7.0) rect.offsetBy(dx: 5.0, dy: -7.0) rect1.union(rect2) rect1.intersect(rect2) rect1.contains(rect2) rect.contains(point) rect1.intersects(rect2)Line Length
1行にはあまりにも多くの文字を含めるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#line-lengthMark
MARK
コメントは有効な形式であるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#mark// bad //MARK: bad // MARK:bad // MARK: -bad // MARK:- bad // MARK bad // good // MARK: good // MARK: - good // MARK: -Multiple Closures with Trailing Closure
複数のクロージャを引数とする場合、トレイリングクロージャを使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiple-closures-with-trailing-closure// bad foo.something(param1: { $0 }) { $0 + 1 } // good foo.something(param1: { $0 }, param2: { $0 + 1 })Nesting
型は最大1レベル、ステートメントは最大5レベルの深さでネストすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#nesting// bad class A { class B { class C { } } } // good class A { class B { } }No Fallthrough Only
case
に少なくとも1つのステートメントが含まれている場合のみ、fallthrouth
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#no-fallthrough-only// bad switch myvar { case 1: fallthrough case 2: var a = 2 } // good switch myvar { case 1: var a = 1 fallthrough case 2: var a = 2 } switch myvar { case 1, 2: var a = 2 }Notification Center Detachment
オブジェクトは
deinit
でのみ自分自身のオブザーバを削除すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#notification-center-detachment// bad class Foo { func bar() { NotificationCenter.default.removeObserver(self) } } // good class Foo { deinit { NotificationCenter.default.removeObserver(self) } }Opening Brace Spacing
{
は定義と同じ行で前に1つの半角スペースを置くべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#opening-brace-spacing// bad func abc(){ } func abc() { } // good func abc() { }Operator Function Whitespace
演算子の定義時、1つの半角スペースで囲まれるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#operator-function-whitespace// bad func <|(lhs: Int, rhs: Int) -> Int { } func <| (lhs: Int, rhs: Int) -> Int { } // good func <| (lhs: Int, rhs: Int) -> Int { }Private over fileprivate
fileprivate
よりprivate
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#private-over-fileprivate// TODO: 例を見ても法則がわからない。。
Private Unit Test
private
の単体テストは暗黙のうちにスキップされます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#private-unit-test// bad private class FooTests: XCTestCase { func test1() { } } class FooTests: XCTestCase { // bad private func test1() { } // good func test1() { } }Protocol Property Accessors Order
プロトコルでプロパティを定義するときは、アクセサの順番を「ゲッター→セッター」とすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#prohibited-calls-to-superprotocol Foo { // bad var bar: String { set get } // good var bar: String { set } var bar: String { get } var bar: String { get set } }Redundant Discardable Let
関数の戻り値を使わずに実行する場合、
let _ = foo()
より_ = foo()
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-discardable-let// bad let _ = foo() if let _ = foo() { } guard let _ = foo() else { return } // good _ = foo()Redundant @objc Attribute
冗長な
@objc
属性は避けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-objc-attribute// bad @objc @IBAction private func foo(_ sender: Any) { } // good @IBAction private func foo(_ sender: Any) { }Redundant Optional Initialization
オプショナル型の変数を
nil
で初期化するのは冗長です。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-optional-initialization// bad var myVar: Int? = nil // good var myVar: Int? let myVar: Int? = nil var myVar: Int? = 0Redundant Set Access Control Rule
プロパティのセッターのアクセスレベルは、変数のアクセスレベルと同様であれば明示的に指定すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-set-access-control-rule// bad private(set) private var foo: Int // good private(set) public var foo: IntRedundant String Enum Value
文字列の列挙型の値は、ケースと同名なら省略できます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-string-enum-value// bad enum Numbers: String { case one = "one" case two = "two" } // good enum Numbers: String { case one case two } enum Numbers: String { case one = "ONE" case two = "TWO" }Redundant Void Return
関数の定義で
Void
を返すのは冗長です。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-void-return// bad func foo() -> Void { } func foo() -> () { } // good func foo() { } let foo: Int -> VoidReturning Whitespace
戻り値の矢印と型は1つの半角スペースまたは別の行で区切るべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#returning-whitespace// bad func abc()-> Int { } func abc() ->Int { } func abc()->Int { } // good func abc() -> Int { } func abc() -> Int { } func abc() -> Int { }Shorthand Operator
省略形の演算子を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#shorthand-operator// bad foo = foo + 1 foo = foo - 1 foo = foo * 1 foo = foo / 1 // good foo += 1 foo -= 1 foo *= 1 foo /= 1Statement Position
else
とcatch
は、前の定義の1つの半角スペースの後ろで同じ行にあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#statement-position// bad }else { } catch { // good } else { } catch {Superfluous Disable Command
無効化されたルールが無効化された領域で違反を起こさなかった場合、SwiftLintの
disable
コマンドは不要です。
https://github.com/realm/SwiftLint/blob/master/Rules.md#superfluous-disable-commandSwitch and Case Statement Alignment
case
文はそれを囲むswitch
文と同じインデントにすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#switch-and-case-statement-alignment// bad switch someBool { case true: print("red") case false: print("blue") } // good switch someBool { case true: print("red") case false: print("blue") }Syntactic Sugar
糖衣構文を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#syntactic-sugar// bad let x: Array<String> let x: Dictionary<Int, String> let x: Optional<Int> let x: ImplicitlyUnwrappedOptional<Int> // good let x: [String] let x: [Int: String] let x: Int? let x: Int!Todo
TODO
とFIXME
は解決すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#todo// bad // TODO: // FIXME:Trailing Comma
配列やディクショナリの末尾のカンマは避けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-comma// bad let array = [1, 2, 3,] let dictionary = ["foo": 1, "bar": 2,] // good let array = [1, 2, 3] let dictionary = ["foo": 1, "bar": 2]Trailing Newline
ファイルは末尾に1つの改行を持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-newlineTrailing Semicolon
行の末尾にセミコロンを付けるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-semicolon// bad let a = 0; // good let a = 0Trailing Whitespace
行の末尾に半角スペースを付けるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-whitespace// bad let a = 1 // good let a = 1Type Body Length
型内はあまりにも多くの行にまたがるべきではないです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#type-body-lengthType Name
型名は英数のみを含み、大文字で始まり、3〜40文字にすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#type-name// bad class My_Type { } class myType { } class aa { } // good class MyType { }Unneeded Break in Switch
不要な
break
は避けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unneeded-break-in-switchswitch a { // bad case .foo: something() break // good case .bar: something() case .baz: break case .qux: for i in [0, 1, 2] { break } }Unused Closure Parameter
クロージャで使われていないパラメータは
_
に置き換えるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-closure-parameter// bad [1, 2].map { number in 3 } // good [1, 2].map { _ in 3 } [1, 2].map { number in number + 1 } [1, 2].map { $0 + 1 }Unused Control Flow Label
未使用の制御フローラベルは削除すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-control-flow-label// bad loop: while true { break } // good loop: while true { break loop } loop: while true { continue loop }Unused Enumerated
インデックスまたはアイテムが使われていない場合、
.enumerated()
を削除できます。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-enumerated// TODO: わからない。。
Unused Optional Binding
let _ =
より!= nil
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-optional-binding// bad if let _ = a { } // good if a != nil { }Unused Setter Value
セッターの値は使われるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-setter-valuevar aValue: String { get { return Persister.shared.aValue } set { // bad Persister.shared.aValue = aValue // good Persister.shared.aValue = newValue } }Valid IBInspectable
@IBInspectable
はサポートされている型の変数のみに使い、その型を明示的にすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#valid-ibinspectableclass Foo { // bad @IBInspectable private let count: Int @IBInspectable private var count = 0 @IBInspectable private var count: Int? @IBInspectable private var count: Int! // good @IBInspectable private var count: Int @IBInspectable private var count: Int = 0 }Vertical Parameter Alignment
関数の定義時、パラメータが複数行にまたがっている場合は垂直方向に揃えるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-parameter-alignment// bad func validateFunction(_ file: File, kind: SwiftDeclarationKind, dictionary: [String: SourceKitRepresentable]) { } func validateFunction(_ file: File, kind: SwiftDeclarationKind, dictionary: [String: SourceKitRepresentable]) { } // good func validateFunction(_ file: File, kind: SwiftDeclarationKind, dictionary: [String: SourceKitRepresentable]) { }Vertical Whitespace
空白行は1行に制限します。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-whitespaceVoid Return
-> ()
より-> Void
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#void-return// bad let abc: () -> Void = { } // good let abc: () -> () = { }Weak Computed Property
コンピューテッドプロパティに
weak
を追加しても効果はありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#weak-computed-propertyclass Foo { private weak var _delegate: SomeProtocol? // bad weak var delegate: SomeProtocol? { // good var delegate: SomeProtocol? { get { return _delegate } set { _delegate = newValue } } }Weak Delegate
デリゲートは循環参照を避けるために弱参照とすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#weak-delegateclass Foo { // bad var delegate: SomeProtocol? // good weak var delegate: SomeProtocol? }XCTFail Message
XCTFail
の呼び出しにはアサーションの説明を含めるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#xctfail-messagefunc testFoo() { // bad XCTFail() // good XCTFail("bar") }Opt-in
デフォルトで無効になっているルールの一覧です。
AnyObject Protocol
クラス専用のプロトコルでは、
class
よりAnyObject
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#anyobject-protocol// bad protocol SomeClassOnlyProtocol: class { } // good protocol SomeClassOnlyProtocol: AnyObject { }Array Init
シーケンスを配列に変換する場合、
seq.map { $0 }
よりArray(seq)
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#array-init// bad seq.map { $0 } // good Array(seq)Attributes
属性は関数や型では別の行にあるべきですが、変数やインポートでは同じ行にあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#attributes// bad @available(iOS 9.0, *) func animate(view: UIStackView) @available(iOS 9.0, *) class UIStackView @objc var x: String @testable import SourceKittenFramework // good @available(iOS 9.0, *) func animate(view: UIStackView) @available(iOS 9.0, *) class UIStackView @objc var x: String @testable import SourceKittenFrameworkClosure Body Length
クロージャ内はあまりにも多くの行にまたがるべきではないです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#closure-body-length// bad foo.bar { toto in let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 let a = 0 } // good foo.bar { $0 } foo.bar { toto in }Closure End Indentation
クロージャの終了は開始と同様のインデントを持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#closure-end-indentation// bad SignalProducer(values: [1, 2, 3]) .startWithNext { number in print(number) } // good SignalProducer(values: [1, 2, 3]) .startWithNext { number in print(number) } [1, 2].map { $0 + 1 }Closure Spacing
クロージャ内は各括弧の内側に1つの半角スペースがあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#closure-spacing// bad [].filter {$0.contains(location)} // good [].filter { $0.contains(location) }Collection Element Alignment
コレクション内の全要素は垂直方向に揃うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#collection-element-alignment// bad let abc = [ "alpha": "a", "beta": "b", "gamma": "g", "delta": "d", "epsilon": "e" ] // good let abc = [ "alpha": "a", "beta": "b", "gamma": "g", "delta": "d", "epsilon": "e" ] let abc = [1, 2, 3, 4] let abc = [ 1, 2, 3, 4 ]Conditional Returns on Newline
条件文では次の行でリターンすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#conditional-returns-on-newline// bad guard true else { return } // good guard true else { return true }Contains over first not nil
first(where:) != nil
よりcontains
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#contains-over-first-not-nil// bad myList.first { $0 % 2 == 0 } != nil myList.first(where: { $0 % 2 == 0 }) != nil // good myList.first { $0 % 2 == 0 } myList.first(where: { $0 % 2 == 0 })Convenience Type
静的メンバーのみをホストするために使われる型は、インスタンス化を回避するために、
case
のない列挙型として実装すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#convenience-type// bad struct Math { public static let pi = 3.14 } class Math { public static let pi = 3.14 } // good enum Math { public static let pi = 3.14 } // 継承を伴う場合はOK class MathViewController: UIViewController { public static let pi = 3.14 } // Obj-Cに見えるクラスはOK @objc class Math: NSObject { public static let pi = 3.14 } // 静的でない型もある場合はOK struct Math { public static let pi = 3.14 public let randomNumber = 2 }Discouraged Object Literal
オブジェクトリテラルよりイニシャライザを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#discouraged-object-literal// bad let image = #imageLiteral(resourceName: "image.jpg") let color = #colorLiteral(red: value, green: value, blue: value, alpha: 1) // good let image = UIImage(named: "image") let color = UIColor(red: value, green: value, blue: value, alpha: 1)Discouraged Optional Boolean
オプショナル型よりそうでない
Bool
型を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#discouraged-optional-boolean// bad var foo: Bool? // good var foo: BoolDiscouraged Optional Collection
オプショナルのコレクションより空のコレクションを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#discouraged-optional-collection// bad var foo: [Int]? var foo: [String: Int]? let foo: [Int]? = nil let foo: [String: Int]? = nil // good var foo: [Int] var foo: [String: Int] let foo: [Int] = [] let foo: [String: Int] = [:]Empty Count
count
を0
と比較するよりisEmpty
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-count// bad [Int]().count == 0 [Int]().count > 0 [Int]().count != 0 // good [Int]().isEmptyEmpty String
空の文字列と比較するより
isEmpty
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-string// bad myString == "" myString != "" // good myString.isEmpty !myString.isEmptyEmpty XCTest Method
空のXCTestメソッドを実装すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#empty-xctest-method// bad class TotoTests: XCTestCase { override func setUp() { } override func tearDown() { } func testFoo() { } } // good class TotoTests: XCTestCase { var foobar: Foobar? override func setUp() { super.setUp() foobar = Foobar() } override func tearDown() { foobar = nil super.tearDown() } func testFoo() { XCTAssertTrue(foobar?.foo) } func helperFunction() { } }Explicit ACL
全ての定義はアクセス制御レベルのキーワードを明示的に指定すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-acl// bad enum A { } // good internal enum A { }Explicit Enum Raw Value
列挙型はローバリューを明示的に割り当てるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-enum-raw-value// bad enum Numbers: Int { case one case two } // good enum Numbers: Int { case one = 1 case two = 2 }Explicit Init
.init()
を明示的に呼び出すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-init// bad [1].flatMap { String.init($0) } [String.self].map { Type in Type.init(1) } // good [1].flatMap(String.init) [String.self].map { $0.init(1) } [String.self].map { type in type.init(1) }Explicit Self
インスタンス変数と関数は
self.
で明示的にアクセスされるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-selfstruct A { let p1: Int func f1() { } func f2() { // bad f1() _ = p1 // good self.f1() _ = self.p1 } }Explicit Top Level ACL
最上位の定義はアクセス制御レベルを明示的に指定すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-top-level-acl// bad enum A { enum B { } } // good internal enum A { enum B { } }Explicit Type Interface
プロパティは型インターフェースを持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-type-interfaceclass Foo { // bad var myVar = 0 // good var myVar: Int? = 0 }Extension Access Modifier
エクステンションにはアクセス修飾子を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#explicit-type-interface// TODO: 例を見ても法則がわからない。。
Fallthrough
fallthrough
は使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#fallthrough// bad switch foo { case .bar: fallthrough case .bar2: something() } // good switch foo { case .bar, .bar2: something() }Fatal Error Message
fatalError
はメッセージを付けて呼び出すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#fatal-error-message// bad func foo() { fatalError() } // good func foo() { fatalError("Foo") }File Header
https://github.com/realm/SwiftLint/blob/master/Rules.md#file-header
// TODO: 例を見ても法則がわからない。。
File Name
ファイル名はファイル内で定義されている型またはエクステンションと一致すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#file-nameFirst Where
コレクションでは
.filter {} .first
より.first(where:)
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#first-where// bad myList.filter { $0 % 2 == 0 } .first // good myList.first(where: { $0 % 2 == 0 }) myList.first { $0 % 2 == 0 }Force Unwrapping
強制アンラップ(
!
)は使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#force-unwrapping// bad let url = NSURL(string: query)! // good if let url = NSURL(string: query) { }Function Default Parameter at End
関数でデフォルト値を持つ引数は後ろにまとめるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#function-default-parameter-at-end// bad func foo(y: Int = 0, x: String, z: CGFloat = 0) { } // good func foo(x: String, y: Int = 0, z: CGFloat = 0) { }Identical Operands
同一のオペランドを比較するのはおそらく間違いです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#identical-operands// bad foo == foo // good foo == barImplicit Return
クロージャでは暗黙のリターンを優先すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#implicit-return// bad foo.map { return $0 + 1 } // good foo.map { $0 + 1 }Implicitly Unwrapped Optional
暗黙的にアンラップされるオプショナル型はできる限り使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#implicitly-unwrapped-optional// bad private var label: UILabel! let int: Int! = 42 // good @IBOutlet private var label: UILabel! let int: Int? = 42Joined Default Parameter
デフォルトの区切り文字は明示的に使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#joined-default-parameter// bad let foo = bar.joined(separator: "") // good let foo = bar.joined() let foo = bar.joined(separator: ",")Last Where
コレクションでは
.filter {} .last
より.last(where:)
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#last-where// bad myList.filter { $0 % 2 == 0 } .last // good myList.last(where: { $0 % 2 == 0 })Legacy Random
従来の関数より
type.ramdom(in:)
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#legacy-random// bad arc4random(10) arc4random_uniform(83) drand48(52) // good Int.random(in: 0..<10) Double.random(in: 8.6...111.34) Float.random(in: 0..<1)Variable Declaration Whitespace
let
とvar
は他のステートメントと空白行で区切るべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#variable-declaration-whitespace// bad let a = 0 var x = 1 x = 2 // good let a = 0 var x = 1 x = 2Literal Expression End Indentation
配列とディクショナリのリテラルの末尾は、開始行と同様のインデントを持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#literal-expression-end-indentation// bad let x = [ 1, 2 ] // good [1, 2, 3] let x = [ 1, 2 ] let x = [1, 2 ] let x = [ 1, 2]Lower ACL than parent
定義が親よりも低いまたは同じアクセス制御レベルを持つようにすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#lower-acl-than-parent// bad struct Foo { public func bar() { } } // good public struct Foo { public func bar() { } }Missing Docs
定義はドキュメント化すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#missing-docs// good /// docs public class A { /// docs public func b() { } } /// docs public class B: A { // オーバーライドメソッドはOK override public func b() { } }Modifier Order
修飾子の順番は一貫しているべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#modifier-orderpublic class Foo { // bad convenience required public init() { } static public let bar = 42 // good public convenience required init() { } public static let bar = 42 }Multiline Arguments
引数は同じ行に入れるか1行に1つずつ入れるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-arguments// bad func foo( param1: "Param1", param2: "Param2", param3: "Param3" ) // good func foo(param1: "Param1", param2: "Param2", param3: "Param3") func foo( param1: "Param1", param2: "Param2", param3: "Param3" )Multiline Arguments Brackets
複数行の引数は、それらを括る大括弧を新しい行に持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-arguments-brackets// bad foo(param1: "Param1", param2: "Param2" ) foo( param1: "Param1", param2: "Param2") // good foo(param1: "Param1", param2: "Param2") foo( param1: "Param1", param2: "Param2" )Multiline Function Chains
関数を連鎖的に呼び出す場合、同じ行に入れるか1行に1つずつ入れるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-function-chains// bad let evenSquaresSum = [20, 17, 35, 4] .filter { $0 % 2 == 0 } .map { $0 * $0 } .reduce(0, +) // good let evenSquaresSum = [20, 17, 35, 4].filter { $0 % 2 == 0 } .map { $0 * $0 } .reduce(0, +) let evenSquaresSum = [20, 17, 35, 4] .filter { $0 % 2 == 0 } .map { $0 * $0 } .reduce(0, +)Multiline Literal Brackets
複数行のリテラルは、新しい行にそれを括る大括弧を入れるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-literal-brackets// bad let trio = ["harry", "ronald", "hermione" ] let trio = [ "harry", "ronald", "hermione"] // good let trio = ["harry", "ronald", "hermione"] let trio = [ "harry", "ronald", "hermione" ]Multiline Parameters
関数とメソッドの引数は、同様の行にあるか1行に1つずつあるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-parameters// bad func foo(param1: Int, param2: Bool, param3: [String]) { } // good func foo(param1: Int, param2: Bool, param3: [String]) { } func foo(param1: Int, param2: Bool, param3: [String]) { }Multiline Parameters Brackets
複数行の引数は、新しい行にそれを括る大括弧を入れるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#multiline-parameters-brackets// bad func foo(param1: "Param1", param2: "Param2", param3: "Param3" ) func foo( param1: "Param1", param2: "Param2", param3: "Param3") // good func foo(param1: "Param1", param2: "Param2", param3: "Param3") func foo( param1: "Param1", param2: "Param2", param3: "Param3" )Nimble Operator
フリーのmatcher関数よりNimble演算子のオーバーロードを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#nimble-operator//bad expect(seagull.squawk).toNot(equal("Hi")) // good expect(seagull.squawk) == "Hi!"No Extension Access Modifier
エクステンションにアクセス修飾子を付けるべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#no-extension-access-modifier// bad private extension String { } fileprivate extension String { } internal extension String { } public extension String { } open extension String { } // good extension String { }No Grouping Extension
エクステンションは同じソースファイル内のコードをグループ化するために使うべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#no-grouping-extension// bad enum Fruit { } extension Fruit { } // good // プロトコルのデフォルト実装はOK protocol Food { } extension Food { }NSLocalizedString Key
genstringsが機能するためには、静的文字列を
NSLocalizedString
のキーとして使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#nslocalizedstring-key// bad NSLocalizedString(method(), comment: nil) // good NSLocalizedString("key", comment: nil)Number Separator
_
は十進数で千の区切り文字として使うべきです。// bad let foo = 1000 let foo = 1000_000.000_000_1 let foo = 1_000_000.000000_1 let foo = 1_0_00_000.000_000_1 // good let foo = 1_000 let foo = 1_000_000.000_000_1Object Literal
画像や色はイニシャライザよりリテラルを使って生成すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#object-literal// bad let image = UIImage(named: "image") let color = UIColor(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1.0) // good let image = #imageLiteral(resourceName: "image.jpg") let color = #colorLiteral(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1.0)Operator Usage Whitespace
演算子を使うときは1つの半角スペースで囲まれるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#operator-usage-whitespace// bad let foo = 1+2 let foo=1+2 // good let foo = 1 + 2Overridden methods call super
一部のオーバーライドメソッドは常に親クラスのメソッドを呼び出すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#overridden-methods-call-super// bad class VC: UIViewController { override func viewWillAppear(_ animated: Bool) { } } // good class VC: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) } override func loadView() { } }Override in Extension
エクステンションでは定義をオーバーライドすべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#override-in-extensionextension Person { // good var age: Int { return 42 } func celebrateBirthday() { } // bad override var age: Int { return 42 } override func celebrateBirthday() { } }Pattern Matching Keywords
キーワードをタプルの外に出し、複数のパターンマッチングバインディングを組み合わせるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#pattern-matching-keywordsswitch foo { // bad case (let x, let y): // good case let (x, y): }Prefixed Top-Level Constant
最上位の定数はプリフィックスに
k
を付けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#prefixed-top-level-constant// bad let bar = 20.0 // good let kBar = 20.0 struct Foo { let bar = 20.0 }Private Actions
@IBAction
はprivate
にすべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#private-actions// bad class Foo { @IBAction func barButtonTapped(_ sender: UIButton) { } } // good class Foo { @IBAction private func barButtonTapped(_ sender: UIButton) { } }Private Outlets
@IBOutlet
はprivate
にすべきです。
∵上位レイヤーへUIKit
が漏洩するのを防ぐため
https://github.com/realm/SwiftLint/blob/master/Rules.md#private-outlets// bad class Foo { @IBOutlet var label: UILabel? } // good class Foo { @IBOutlet private var label: UILabel? }Prohibited Interface Builder
IBを使ってビューを生成するのは避けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#prohibited-interface-builderclass ViewController: UIViewController { // bad @IBOutlet var label: UILabel! @IBAction func buttonTapped(_ sender: UIButton) { } // good var label: UILabel! @objc func buttonTapped(_ sender: UIButton) { } }Prohibited calls to super
一部のメソッドは親クラスのメソッドを呼び出してはいけません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#prohibited-calls-to-super// bad class VC: UIViewController { override func loadView() { super.loadView() } }Quick Discouraged Call
Quickを使ったことがないのでわかりません。。
https://github.com/realm/SwiftLint/blob/master/Rules.md#quick-discouraged-callQuick Discouraged Focused Test
こちらも上記と同様にわかりません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#quick-discouraged-focused-testQuick Discouraged Pending Test
こちらも上記と同様にわかりません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#quick-discouraged-pending-testRedundant Nil Coalescing
??
演算子は左辺がnil
の場合のみ評価されるため、右辺にnil
を記述するのは冗長です。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-nil-coalescing// bad var myVar: Int? = nil myVar ?? nil // good var myVar: Int? myVar ?? 0Redundant Type Annotation
変数は冗長な型アノテーションを持つべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#redundant-type-annotation// bad var url: URL = URL() // good var url = URL() var url: CustomStringConvertible = URL()Required Deinit
クラスは明示的な
deinit
メソッドを持つべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#required-deinit// bad class Apple { } // good class Apple { deinit { } }Required Enum Case
特定のプロトコルに準拠した列挙型は特定のケースを実装する必要があります。
https://github.com/realm/SwiftLint/blob/master/Rules.md#required-enum-case// bad enum MyNetworkResponse: String, NetworkResponsable { case success case error } // good enum MyNetworkResponse: String, NetworkResponsable { case success case error case notConnected } enum MyNetworkResponse: String, NetworkResponsable { case success case error case notConnected(error: Error) }Single Test Class
テストファイルは単一の
QuickSpec
またはXCTestCase
クラスを含むべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#single-test-class// bad class FooTests: QuickSpec { } class BarTests: QuickSpec { } class FooTests: XCTestCase { } class BarTests: XCTestCase { } class FooTests: XCTestCase { } class BarTests: QuickSpec { } // good class FooTests: XCTestCase { } class FooTests: QuickSpec { }Min or Max over Sorted First or Last
sorted().first
やsorted().last
よりmin()
やmax()
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#min-or-max-over-sorted-first-or-last// bad myList.sorted().first myList.sorted().last let min = myList.sorted(by: >).first // good myList.min() myList.max() let min = myList.min(by: >)Sorted Imports
インポート文はソートされるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#sorted-imports// bad import AAA import CCC import BBB import DDD // good import AAA import BBB import CCC import DDDStatic Operator
演算子は自由関数でなく静的関数として定義すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#static-operator// bad func == (lhs: A, rhs: A) -> Bool { return false } func == <T>(lhs: A<T>, rhs: A<T>) -> Bool { return false } // good class A: Equatable { static func == (lhs: A, rhs: A) -> Bool { return false } } class A<T>: Equatable { static func == <T>(lhs: A<T>, rhs: A<T>) -> Bool { return false } }Strict fileprivate
fileprivate
は避けるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#strict-fileprivate// bad fileprivate extension String { } extension String { fileprivate func Something() { } } // good private extension String { } extension String { func Something() { } }Strong IBOutlet
@IBOutlet
は弱参照で定義すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#strong-iboutlet// bad class ViewController: UIViewController { @IBOutlet weak var label: UILabel? @IBOutlet unowned var label: UILabel! } // good class ViewController: UIViewController { @IBOutlet var label: UILabel? weak var label: UILabel! }Switch Case on Newline
switch
文の中のcase
文は常に改行すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#switch-case-on-newline// bad switch foo { case 1: return true } // good switch foo { case 1: return true }Toggle Bool
someBool = !someBool
よりsomeBool.toggle()
を使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#toggle-bool// bad isHidden = !isHidden // good isHidden.toggle()Trailing Closure
できる限りトレイリングクロージャを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#trailing-closure// bad foo.map({ $0 + 1 }) foo.reduce(0, combine: { $0 + 1 }) // good foo.map { $0 + 1 } foo.reduce(0) { $0 + 1 }Unavailable Function
未実装の関数は使用不可としてマークされるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unavailable-function// bad class ViewController: UIViewController { public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } } // good class ViewController: UIViewController { @available(*, unavailable) public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }Unneeded Parentheses in Closure Argument
クロージャ引数の定義時に括弧は不要です。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unneeded-parentheses-in-closure-argument// bad let foo = { (bar) in } let foo = { (bar, _) in } // good let foo = { (bar: Int) in } let foo = { bar in } let foo = { bar, _ in }Untyped Error in Catch
catch
文は型キャストなしでエラー変数を定義すべきではありません。
https://github.com/realm/SwiftLint/blob/master/Rules.md#untyped-error-in-catch// bad do { try foo() } catch let error { } // good do { try foo() } catch let error as MyError { } catch { }Unused Import
インポートされた全てのモジュールがファイルをコンパイルするために必要とされるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-import// bad import Foundation class A { } // good import Foundation @objc class A { }Unused Private Declaration
private
の定義はそのファイル内で参照されるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#unused-private-declaration// bad private let a = 0 // good private let a = 0 _ = aVertical Parameter Alignment On Call
関数の呼び出し時、パラメータが複数行にまたがっている場合は垂直方向に揃えるべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-parameter-alignment-on-call// bad validateFunction(_ file: File, kind: SwiftDeclarationKind, dictionary: [String: SourceKitRepresentable]) { } validateFunction(_ file: File, kind: SwiftDeclarationKind, dictionary: [String: SourceKitRepresentable]) { } // good validateFunction(_ file: File, kind: SwiftDeclarationKind, dictionary: [String: SourceKitRepresentable]) { }Vertical Whitespace Between Cases
SwitchのCase間は空白行を1行含んでください。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-whitespace-between-cases// bad switch x { case .valid: print("x is valid") case .invalid: print("x is invalid") } // good switch x { case .valid: print("x is valid") case .invalid: print("x is invalid") }Vertical Whitespace before Closing Braces
中括弧を閉じる前に空白行を入れないでください。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-whitespace-before-closing-braces// bad { { } } // good { { } }Vertical Whitespace after Opening Braces
中括弧を開いた後に空白行を入れないでください。
https://github.com/realm/SwiftLint/blob/master/Rules.md#vertical-whitespace-after-opening-braces// bad { { } } // good { { } }XCTest Specific Matcher
XCTAssertEqual
やXCTAssertNotEqual
より特定のXCTestマッチャーを使うべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#xctest-specific-matcher// bad XCTAssertEqual(foo, true) XCTAssertEqual(foo, false) XCTAssertEqual(foo, nil) XCTAssertNotEqual(foo, true) XCTAssertNotEqual(foo, false) XCTAssertNotEqual(foo, nil) // good XCTAssertFalse(foo) XCTAssertTrue(foo) XCTAssertNil(foo) XCTAssertNotNil(foo) XCTAssertEqual(foo, 2)Yoda condition rule
変数は比較演算子の左側、定数は右側に配置すべきです。
https://github.com/realm/SwiftLint/blob/master/Rules.md#yoda-condition-rule// bad if 42 == foo { } // good if foo == 42 { }おわりに
SwiftLintのルールを一通り眺めるだけでもSwiftyに書けるようになった気がします。
ただ、これらを他のメンバーにも守ってもらうには、やはりSwiftLintの導入が便利です。
- 投稿日:2019-02-11T07:36:07+09:00
ARアプリのCamera Permission取得テンプレート
はじめに
最近ARKitを使ったAR Cake Dividerというアプリをリリースしました。しかし、初回のリリース時にApple Human Interface GuidelinesのResuesting Permission(以下AHIG)に準拠していないという事でリジェクトを受けました。ARアプリを作る上でCamera Permissionは必須になりますが、XcodeのデフォルトARプロジェクトではAHIGに準拠した形のパーミッション要求を出してくれません。
アプリ作成の度に修正をするのは面倒なのでテンプレートプロジェクトを作成してGitHubで公開しました。AHIGのResuesting Permissionの要点
- アプリは位置情報や連絡先などの情報へのアクセスを、ユーザーがコントロールできる様にするべき
- 情報へのアクセス要求はユーザにとって、明らかにアプリがその情報が必要だとわかるときに行うべき
- アクセス要求時にシステムから表示されるダイアログには、短く明快になぜアプリはその情報が必要なのか書くべき
この中でどれも重要なのですが、2番目は「アプリがその情報にアクセスできなければ動作できないのであれば、ちゃんと要求を出さなければいけない」ということも意味しています。
具体的には、例えばあるカメラアプリが起動時にカメラを起動させるとして、起動前にパーミッション要求を行ったとします。
一度ユーザが「許可しない」を選択したとして、その後アプリを再起動させました。
そのときアプリは一度パーミッション取得できなかったため、再び要求を出さずそのまま状態だったとします。これはアプリがその情報が必要なときに要求を行っていないとみなされます。アプリがその情報にアクセスできなければ動作できないのであれば、再度要求を出すことが求められます。
テンプレートプロジェクトのポイント
以上の点を踏まえてテンプレートプロジェクトでは次の機能を実装しています。
アプリは初回起動時に自分がCamera Permissionを持っているか確認する
⇨let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)
初回起動時の判定に基づいて、アプリがForegroundになったタイミングで
- Camera Permissionを持っていなければ要求を出す⇨
requestCameraPermission()
- Camera Permissionをユーザに断られていたら、アプリの動作に必要な旨のダイアログを表示し、設定アプリへ誘導する⇨
alertCameraAccessNeeded()
そのために、UIApplication.willEnterForegroundNotificationを監視する。
⇨NotificationCenter.default.addObserver(self, selector: #selector(checkPermission), name: UIApplication.willEnterForegroundNotification, object: nil)
- ユーザが理解し易い様にダイアログのメッセージは国際化する
⇨ InfoPlist.strings、Localizable.stringsスクリーンショット
- 投稿日:2019-02-11T01:32:09+09:00
LLDBでアドレスからオブジェクトの内容を確認する
例えば、Xcodeの
Debugging View Hierarchies
を使ってデバッグ中に取得したアドレスから、オブジェクトのより詳細な内容を確認したいときなどがあるかと思います。
そこで、SwiftのunsafeBitCast(_:to:)
を使えば簡単にアドレスからオブジェクトの内容を確認することができます。LLDBでは以下のようになるかと思います。
(lldb) expr -l Swift -- import UIKit (lldb) expr -l Swift -- let $label = unsafeBitCast(0x7fac81778d00, to: UILabel.self) (lldb) expr -l Swift -- print($label.font) Optional(<UICTFont: 0x7fac81779410> font-family: ".SFUIDisplay-Bold"; font-weight: bold; font-style: normal; font-size: 22.00pt)
- 投稿日:2019-02-11T00:58:07+09:00
ChildViewControllerのSafeAreaが正しくない場合がある
特定の条件下においてChildViewControllerのSafeAreaが正しい値に設定されない場合があり、レイアウトが崩れることがあったので記録しておきます。
発生した際の前提条件
環境
- Xcode10.1
- iPhoneXシュミレータ(iOS12.1)
Viewヒエラルキー
事象
上記Viewヒエラルキーにおいて、UICollectionViewCell内のChildViewControllerにSafeAreaが正しくない値が設定されることがありました。
初回に画面内に表示されるセルのChildViewControllerにおいては正しい値が設定されており、画面外のセルのChildViewControllerには正しくない以下の値が設定されていました。
▿ UIEdgeInsets
- top : 0.0
- left : 0.0
- bottom : 0.0
- right : 0.0
頻度は毎回必ず。
親のUICollectionViewCellのSafeAreaまでは正しい値。ワークアラウンド
親のViewから正しいSafeAreaを受け渡し、愚直に各Viewのレイアウトに反映させる方法をとりました。
SafeArea関連のAPIを調べてみましたがsetterが存在するのは-[UIViewController additionalSafeAreaInsets]
だけのようでSafeAreaを上書きする用途だと少し扱いづらいですね。何か他に良い方法があれば教えて欲しいです。