2017年6月15日木曜日

凛としてSwift (血闘編ノ参)

凛としてSwift (血闘編ノ弐)」からの続きです。最終回です。


redundant_discardable_let

関数の返り値を読み捨てるのに、letを使うのは冗長だよ、です。
返り値を読み捨てていることの是非はともかく、letいらないの?知りませんでした。
let _ = daoFooDB.updateFooSortKey(foos)
_ = daoFooDB.updateFooSortKey(foos) //え、アンダースコアだけでいいの?まじか。


statement_position

ステートメントの開始位置がよくない、です。
このケースでは、そのステートメントの前に空白がないよ、というわけです。opening_braceと同じような意味合いというか、対になる感じでしょうか。
        if xMax <= 15.1 {
            //…省略
        }else if xMax <= 30.1 { //←elseの前にスペースがない
            //…省略
        }else if xMax <= 60.1 { //←elseの前にスペースがない
            //…省略
        }else { //←elseの前にスペースがない
            //…省略
        }
        if xMax <= 15.1 {
            //…省略
        } else if xMax <= 30.1 { //←elseの前にスペースを入れました
            //…省略
        } else if xMax <= 60.1 { //←elseの前にスペースを入れました
            //…省略
        } else {
            //…省略
        }


empty_count

配列が空かを判断するのには、isEmptyを使いましょう、ですね。
        if practices.count > 0 { //←配列が空か?
            //…省略
        }
        if !practices.isEmpty { //isEmptyを使用する
            //…省略
        }

※2018/12/29追記
countという変数名を使うと引っかかってしまうみたいなので注意です。
    func hoge(count: Int) {
        if count == 0 { //← empty_countエラー?
            //…省略
        } else {
            //…省略
        }
    }
    
    func hoge2(work: Int) {
        let count = work
        if count == 0 { //← empty_countエラー?
            //…省略
        } else {
            //…省略
        }
    }


variable_name

変数名,定数名にアンダースコアが入っているものがエラーになりました。
まぁ、アンダースコアはワイルドカードとして単体で意味があるので、変数には使用しない方がいいかもしれません。大文字のスネークケースの方が定数っぽいのに、と思うのはジジイだからでしょうか。
let SQL_PRACTICE_SELECT = "SELECT id, sort_key, name FROM practice WHERE id = ?;"
let kSqlPracticeSelect  = "SELECT id, sort_key, name FROM practice WHERE id = ?;"


force_cast

強制キャストは、失敗すると実行エラーになるので禁止ね、です。

そこでどうするかですが、そのキャストの失敗の重大性をどう見るかで変わってきそうです。起こりうる失敗として処理を続けるようなケースと、もう後続の処理を行う必要のないような重大なものです。

まず、後続の処理をハンドリングするために、キャストの成功時と失敗時の振り分けをif文で行う方法です。
ダウンキャストに失敗したとき、as!演算子だと実行時エラーになりますが、as?演算子を使ったときはnilを返します。これを利用してif let 文と組み合わせて、ダウンキャスト失敗時の処理をelseに落とします。
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MenuTableViewCell", for: indexPath)
        self.updateCell(cell as! MenuTableViewCell, indexPath: indexPath) //←強制キャスト
     
        return cell
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "MenuTableViewCell", for: indexPath)
        if let c = cell as? MenuTableViewCell {
            self.updateCell(c, indexPath: indexPath) //セルの表示項目の設定
            return c
        } //←この例ではelseがありませんが、もし失敗時の処理が必要ならここに。
     
        return cell
    }
これならキャストの成功/失敗で何かしらの処理を行って、後続の処理に流すことができますね。また、その値が複数の型のうちのどれかである可能性があるような場合など、if文でどの型かを判別して、それぞれ処理するのにも便利です。

※)2017/06/19追記
いくつかの型を判別してそれぞれ処理する時には、switch文でパターンマッチングを使った方が綺麗ですね。
    func fooBar(hoge: Any) {
        switch hoge {
        case let f as Foo:
            print("name is \(f.name)")
        case let f as Bar:
            print("name is \(f.name)")
        default:
            print("something wrong?")
        }
    }

次に、後続の処理をキャンセルしたい場合には、guard-let文でするりと抜けるのがスマートです。letで定義した定数が、guard文以降でも値を保持したまま使用可能という、まさにこのためにあるような便利機能です。
guard文の中にelseの処理は書けますが、thenの処理は書けません。また、elseの処理の中にはそのブロック内の後続処理に流れないようなreturn(もしくはbreakthrowなど)が必須です。if文のように分岐するというより、elseに入ったらもうブロック内の後続には流さない、以降の処理は条件が真であることを保証する、まさに「ガード」ですね。
    @IBAction func sliderVolumeValueChanged(_ sender: AnyObject) {
        guard let slider = sender as? UISlider else {
            //メッセージを出すとかね。
            return //←これは必須で、関数sliderVolumeValueChangedを抜けています
        }

        volume = slider.value //←guard-let文で定義されたsliderが使用できる!
        //…省略
    }
ちなみに、これをif letで書くと、ネストも深くなりますし、なんだか冗長です。
    @IBAction func sliderVolumeValueChanged(_ sender: AnyObject) {
        if let slider = sender as? UISlider {
            volume = slider.value
            //…省略
        } else {
            //メッセージを出すとかね。
        }
    }
ただ、このguard文の書式ですが言語的にはどうなんでしょうか。ここだけ定数のローカルスコープの例外っていうのは、なにやらモヤモヤします。規則性って大切です。例外ってのはできるだけ少ない方が人に優しいと思います。elseも、条件が偽である処理であることを示すためにあるのでしょうが、thenが書けないならelseも必要なさそうです。別の何かスマートな書式を定義しても良かったんじゃないかとは思います。言語を設計しているエラい人はそのあたりどう考えているんでしょうね?

それはさておき、ここで例に出したのはInterface Builderから生成されたアクションです。強制キャストに失敗することはありえなさそうです。
どうせキャストに失敗しないんだし、いっそのこと必要な型で受け取ってしまえば、とパラメータの型を変更してもちゃんと動きます。Lintのワーニングも出ません。でも、これは単に暗黙のうちに強制キャストをしているだけですし、むしろ強制キャストを隠してしまっているという点で、本末転倒で悪手じゃないかと思いますがどうでしょう?
    @IBAction func sliderVolumeValueChanged(_ sender: UISlider) { //← AnyObjectをUISliderに変更
        volume = sender.value //音量
        //…省略
    }
これをするくらいなら、// swiftlint:disableを使って、明示的にLintの例外とする方が健全だと思います。
    @IBAction func sliderVolumeValueChanged(_ sender: AnyObject) { //← AnyObjectをUISliderに変更
        // swiftlint:disable force_cast
        let slider = sender as! UISlider
        volume = sender.value //音量
        //…省略
    }
まぁ、問題なく動けば良いといえば、いいんですけどね。:P



force_try

エラーをthrowする文は、ちゃんとdo-catchでエラーをハンドリングしなさい、です。
    func playHoge(player: AVAudioPlayerNode, sourceFile: AVAudioFile, engine: AVAudioEngine) {
        player.scheduleFile(sourceFile, at: nil, completionHandler: nil)
        try! engine.start() //←エラーを投げる可能性あり
        player.play()
    }
    func playHoge(player: AVAudioPlayerNode, sourceFile: AVAudioFile, engine: AVAudioEngine) {
        player.scheduleFile(sourceFile, at: nil, completionHandler: nil)
        do {
            try engine.start()
            player.play()
        } catch {
            print("something wrong? \(error)")
        }
    }
まぁ、そうですよね。ただ、上記の例は別としても、try!を一律に制限するのもなぁ、という気もしないでもないです。でも、明示的にエラーを無視をしたいときには// swiftlint:disableを使って、ガツンと明示しなさいということなのでしょう。



unused_optional_binding

使わないならletでバインドする必要ないよね?、です。
    if let _ = h as? Hoge {
        //hはHoge型だよ
    } else {
       //hはHoge型じゃないよ
    }
    if h as? Hoge != nil {
        //hはHoge型だよ
    } else {
        //hはHoge型じゃないよ
    }
確かにそうなんですが、使わないことを明示するためのアンダースコアがあるんだから使ってもいいじゃんとは思います。個人的な好みだと上の方なんですが、Lintがそう言うならいたしかないです。

ただ上記の例のように、単に型を判断したいだけならtype(of: T)を使った方がいいのは間違いないです。
    if type(of: h) != Hoge.self {
        //hはHoge型だよ
    } else {
        //hはHoge型じゃないよ
    }


♪♪♪


以降のLintエラー、ワーニングは、// swiftlint:disableを使って明示的に回避しました。
ちなみに// swiftlint:disableに複数指定するときは空白を開けて、// swiftlint:disable cyclomatic_complexity function_body_lengthと書くようです。


function_parameter_count
関数のパラメータの数が多すぎ、です。

Objective-Cとの絡みもあり、ちょっと今は直せないと判断しました。
    @objc(calcIntonationGap:variety1:variety2:variety3:variety4:variety5:)
    static func calcIntonationGap(_ stimmung: Stimmung, variety1: StimmungVariety1, variety2: StimmungVariety2, variety3: StimmungVariety3, variety4: StimmungVariety4, variety5: StimmungVariety5) -> [Double] {

        //…省略
             
        return intonationGap
    }
    @objc(calcIntonationGap:variety1:variety2:variety3:variety4:variety5:)
    // swiftlint:disable function_parameter_count
    static func calcIntonationGap(_ stimmung: Stimmung, variety1: StimmungVariety1, variety2: StimmungVariety2, variety3: StimmungVariety3, variety4: StimmungVariety4, variety5: StimmungVariety5) -> [Double] {

        //…省略
     
        return intonationGap
    }


cyclomatic_complexity
これ、なんて訳せばいいのかよくわからなかったのでググったら、ウィキペディアの「循環的複雑度」が一番に出てきました。初めて聞いた単語(笑)。ざっくり言うと「分岐多すぎ」ですか。これも厄介で、五線譜を表示する処理ではどうしてもif文やswitch文の塊になるので避けられません。

type_body_length
構造体(enumも、かな?)の長さが、長すぎ。

function_body_length
関数の長さが、長すぎ。

file_length
ファイルの長さが、長すぎ。

この辺りは.swiftlint.ymlを修正して調整してもいいですが、少し頑張ればなんとかなりそうなので、あえて// swiftlint:disableで手が空くまでペンディングにしました。いずれにしても、// swiftlint:disableを使って回避した箇所は、定期的に棚卸しする必要があるでしょう。


これでLintのエラー、ワーニングが全て消えました。おつかれさまでした。

「帰ってきた凛としてSwift(くるくる編)」に続きます。


♪♪♪


こちらもどうぞ。
凛としてSwift
凛としてSwift (血闘編ノ壱)
凛としてSwift (血闘編ノ弐)
帰ってきた凛としてSwift(くるくる編)

2017年5月15日月曜日

凛としてSwift (血闘編ノ弐)

SwiftLintネタ「凛としてSwift (血闘編ノ壱)」からの続きです。


trailing_semicolon

いうまでもなく、文末のセミコロンですね。
気をつけていたんですが意外に多かったです。Objective-C混じりのプロジェクトなので、反射的に入力してしまっていたんですね。
self.imageFoo.alpha = 0.3; //むむ、セミコロンが!
self.imageBar.alpha = 0.3;
self.imageFoo.alpha = 0.3 //取りましたよ
self.imageBar.alpha = 0.3


vertical_whitespace

2行以上、空の行がつづく時に出ます。
意図的に2行以上空けることはないので、これもうっかりですね。気がついていませんでした。
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    } 


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return notes.count
    }


trailing_comma

配列の最後の要素の後ろにカンマをつけるか否かです。
これは何というか、コーディングの流派というか、どっちもアリですし、だからこそSwiftの文法では両方OKになっているのだと思いますが、SwiftLintのデフォルトではカンマなしのようです。ここはSwiftLintに従うことにします。
        accidentials = [
            "picker_label_none",
            "picker_label_natural",
            "picker_label_sharp",
            "picker_label_flat", //←ここのカンマ
        ]
        accidentials = [
            "picker_label_none",
            "picker_label_natural",
            "picker_label_sharp",
            "picker_label_flat" //←取りましたよ

        ]


colon

コロンの後には空白を1個入れましょう、ですね。はい、その通りですね。
let hogeRequest:HogeRequest = HogeRequest()
let hogeRequest: HogeRequest = HogeRequest()


unused_closure_parameter

クロージャの使っていないパラメータをアンダースコアで省略すべきと。ふむ。
        UIView.animate(withDuration: TimeInterval(CGFloat(0.3)),
                       animations: { () -> Void in
                        pickerView?.center = offScreenCenter
        },
                       completion: { (Bool) -> Void in
                        pickerView?.removeFromSuperview()
        })
        UIView.animate(withDuration: TimeInt
erval(CGFloat(0.3)),
                       animations: { () -> Void in
                        pickerView?.center = offScreenCenter
        },
                       completion: { (_) -> Void in //←Boolをアンダースコアに変更
                        pickerView?.removeFromSuperview()
        })


weak_delegate

delegateパターンでは、循環参照によるメモリリークを避けるために弱参照にしなさいとのこと。
weakをつければいいのですが、それだけだと、"'weak' may only be applied to class and class-bound protocol types, not 'xxxDelegate'"のエラーが出るので、プロトコルにclassキーワードを追加して、クラス専用プロトコルにします。
protocol FooViewControllerDelegate {
    func fooViewDidDissmissed()
}

class FooViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    var delegate: FooViewControllerDelegate? //←強参照
    var selectedPractice: Practice? //選択されているPractice
protocol FooViewControllerDelegate: class { //←クラス専用プロトコル
    func fooViewDidDissmissed()
}

class FooViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    weak var delegate: FooViewControllerDelegate? //←弱参照にする
    var selectedPractice: Practice? //選択されているPractice


control_statement

これもまたObjective-C的うっかりです。
            switch (noteLanguage) { //←あれ、カッコが!
            case .english:
                name = "E♭"
            case .italian:
                name = "Mi♭"
            /* 省略 */
            case .japanese:
                name = "E♭"
            }
            switch noteLanguage { //←消しました
            case .english:
                name = "E♭"
            case .italian:
                name = "Mi♭"
            /* 省略 */
            case .japanese:
                name = "E♭"
            }


redundant_optional_initialization

オプショナル型の初期値はnilなので、わざわざnilで初期化する必要はないよ、です。
class HogeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    var finishTime: Date? = nil //←nilで初期化は冗長
class HogeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    var finishTime: Date? //←初期化を消しました


syntactic_sugar

いろんなケースがあるのかもしれませんが、ここではArray<hoge>()のシンタックス・シュガーである、[hoge]を使いなさいとのこと。

どうでもいいことですけど、syntactic_sugarなので「シンタクティック・シュガー」なんじゃないかなぁと思って辞書を引いたら、syntacticの訳に「シンタックスの」がありますね。syntaxの形容詞がsyntacticらしいです。ひょっとしてシンタックス・シュガーって和製英語だったりして?

    var transposingInstrumentKeys = Array<(TransposingInstrumentKey, String)>() //Arrayですね
    var transposingInstrumentKeys: [(TransposingInstrumentKey, String)] = [] //←配列っぽくね


unused_enumerated

enumeratedで変数が使われてないから、`.indices`とか使ってね、です。

こんな感じでenumeratedで回したけど、結局変数が使われないときにワーニングが出ます。これはコンパイラによるワーニングです。
"Immutable value 'h' was never used; consider replacing with '_' or removing it"
for (i, h) in hoge.enumerated() {
    /* 省略 */
    hoge[i] = piyo
    /* 省略 */
}

じゃあ、アンダースコアにすればいいかと修正すると、Lintがunused_enumeratedを検出します。
for (i, _) in hoge.enumerated() { // ←hをアンダースコアに
    /* 省略 */
    hoge[i] = piyo
    /* 省略 */
}

なるほどそりゃそうだ、です。
for i in hoge.indices { // ←ずぼらせずに、indicesを使いなさい
    /* 省略 */
    hoge[i] = piyo
    /* 省略 */
}


implicit_getter

コンピューテッドプロパティにおいて、セッタが不要なケースではgetキーワードが省略可能です。省略可能なものは省略してスッキリさせときや、です。
    var hoge: Int {
        get {
            return fuga + 10
        }
    }

もちろん、get{}を削除します。
    var hoge: Int {
        return fuga + 10
    }


まだまだ続くよ。「凛としてSwift (血闘編ノ参)」につづく。


♪♪♪


こちらもどうぞ。
凛としてSwift
凛としてSwift (血闘編ノ壱)
凛としてSwift (血闘編ノ参)
帰ってきた凛としてSwift(くるくる編)


2017年4月15日土曜日

凛としてSwift (血闘編ノ壱)

割と凹みやすいアルマイトな性格です。

いくつかの軽い(Swift成分の低い)プロジェクトにSwiftLintを導入してみて、なんとなく把握できたので、次はSwift率の高いプロジェクトにSwiftLintを導入してみることにしました。

予想通り、数え切れないワーニングとエラーが吐かれますね。ため息が出ますね。当然これをちくちくと直していかなければならないのですが、先頭からひとつひとつやっていると、最後にたどり着く前にメンタルがやられそうです。

そこでXcodeの問題ナビゲーターからいったん目をそらして、個々の対処法について考えてみます。
  1. 素直に修正する。
  2. 特定の箇所だけLintの例外にする。
  3. ymlファイルを編集して、Lintのルールを緩める。
  4. 放置する。
  5. SwiftLintの導入を断念する。
まず5.は避けたいですね。せっかくなんだし。何より、現行のソースがLint適用済みになれば、その後はソースを修正する都度Lintが指摘してくれるわけですから、辛いのは最初だけです。

次に4.は「ダメ、絶対!」ですね。これから先ソースを修正していくのに、修正しなければならないエラーやワーニングが、Lintのワーニングに埋もれてしまってはバグの温床になります。

そうなると1.2.3になりますが、できるだけ1.で頑張って、ちゃんとした理由がある時にかぎって2.、それでもしょうがないときは3.ということにします。

そうしよう、そうしよう。

で、そらしていた目をワーニングの山に向けます。先頭から順にやっていくのも芸がないですよね。それに全て修正し終わってからgitのコミットをしていたのでは粒度が大きくなるので、うっかりエンバグしていたときに、追いかけるのが大変になりそうです。そこで少しだけ工夫をします。

ymlファイルを編集して、エラー/ワーニングの元になっているルールを一時的に全てdisabledにします。そしてエラー/ワーニングがなくなった状態から、disabledにしたルールをひとつずつ有効にして、同じワーニング毎に潰していきます。これなら修正対象は全て同じ種類になるので、作業が楽になります。上記1,2,3の判断もしやすくなりますし、さらにコミットの粒度も下がります。

ymlファイルを抜粋すると、こんな感じですか。

これで、エラー/ワーニングが出力されなくなったので、機械的に修正できる簡単なものから手をつけていきます。

opening_braceを、disabled_rulesから削除すると、こういったところが指摘されます。

えー、Paul Hegarty先生だってこう書いてたぢゃん。 Objective-Cの流れでこうなんじゃないのー、って愚痴りながら修正します。


この他にもこういうところにワーニングが出ますね。

If文の条件にカッコが必要ないことなど、スッキリした文法になっている分、各語句の前後や、{ } の前後の空白はきちんと入れましょうということですね。


凛としてSwift (血闘編ノ弐)」につづく。


こんな風に手を入れているチューナーアプリです。よろしくね。


♪♪♪


こちらもどうぞ。
凛としてSwift
凛としてSwift (血闘編ノ弐)
凛としてSwift (血闘編ノ参)
帰ってきた凛としてSwift(くるくる編)


2017年3月15日水曜日

凛としてSwift

Swiftize”という謎単語を思いつきました。

僕の作っているiOS用アプリは大部分がObjective-Cで書かれています。もちろん新たに追加する部分はSwiftで書いているので、全体の2, 3割がSwiftで書かれているでしょうか。それでSwiftのバージョンが上がるたびに派手にエラー・警告が吐かれるという罰ゲームを受けています。

仕様変更しすぎでは。。。

そんなことからObjective-Cで書かれた処理をあえてSwiftで書き直すのは、まだ時期尚早なのでは、と手をつけていませんでした。でも、ひとつのプロジェクトでSwiftObjective-Cが混ざった状態というのは、著しく生産性が落ちるのです。頭がパッパと切り替わらないのです。おっさんなのです。

今から思えば、中途半端に混ぜるようなことはしないで、一気に書き換えるようにすれば良かったなぁ。

でもSwiftもバージョン3になりましたしね、ここは一念発起して少しづつ"Swiftize"していこうと決めました。

そこで、どうせなら綺麗なソースを書きたいので、SwiftLintを導入することにしました。

SwiftLint本体Home Brewでインストールしました。
$ brew install swiftlint
==> Downloading https://homebrew.bintray.com/bottles/swiftlint-0.16.1.sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring swiftlint-0.16.1.sierra.bottle.tar.gz
🍺  /usr/local/Cellar/swiftlint/0.16.1: 37 files, 13.6M
$ swiftlint version
0.16.1
$ 

次に、Xcodeのプロジェクトを開いて、TARGETS > Build Phases で、Run Script Phaseを追加します。

1. TARGETSを選択します。


2. Build Phasesで”+”をクリックします。


3. New Run Script Phaseを選びます。


4. Run Scriptが追加されるので、開きます。



5. Swift LintのREADME.mdにある、シェルスクリプトをコピペします。
if which swiftlint >/dev/null; then
  swiftlint
else
  echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"
fi

6. プロジェクトのディレクトリに、.swiftlint.ymlを追加してLintのルールをカスタマイズします。
$ cd HelloSwiftLint/
$ ls -la
total 8
drwxr-xr-x  7   foobar  staff   238  2  3 04:02 .
drwxr-xr-x  8   foobar  staff   272  2 17 00:09 ..
-rw-r--r--  1   foobar  staff  1465  2  3 04:02 .swiftlint.yml     ←これを追加します
drwxr-xr-x  7   foobar  staff   238  2  2 03:03 HelloSwiftLint
drwxr-xr-x  5   foobar  staff   170  2  2 02:42 HelloSwiftLint.xcodeproj
drwxr-xr-x  4   foobar  staff   136  2  2 02:49 HelloSwiftLintTests
drwxr-xr-x  4   foobar  staff   136  2  2 02:49 HelloSwiftLintUITests
$
.swiftlint.ymlの中身は、やはりREADME.mdをお手本にして修正を加えます。
最低限設定するべき箇所は、included:とexcluded:です。
included:にはチェックするソースへのPATHを指定します。
これはプロジェクトの直下にある、プロジェクトと同名のディレクトリを指定すればいいと思います。(これを指定しないとプロジェクトの下全部が対象になるのかな? そんな感じがするんだけど)
excluded:には逆にチェックしたくないファイルへのPATHを指定します。
CocoaPodsを使用しているならPodsとか。あと、include:で指定した下に、何か外部のライブラリを置いたような時、それを除外するよう指定できます。

ルールのカスタマイズとは言っても、特別なことはしていません。ただ、デフォルトではかなり厳しいチェックが行われますので、ルールを少し緩めています。

disabled_rules:には、チェックしない項目を指定します。
ここには1行の長さをチェックしないように追加しました。ご存知のように、AppleのAPIはのけぞる長さですからね。

type_body_length:は1個の型の定義(クラスとかね)の行数、file_length:は1ファイルの行数です。
これは適当に大きめに変更します。ただ、ソースにルールを合わせたのでは、本末転倒になりますからほどほどに。

type_name:は型名(クラスとか)の長さ、variable_name:は変数名の長さです。
型名はともかく、変数名は1文字から使いたいのでmin_length:は指定なしにしてあります。


あ、 gitを使っているなら、.gitignoreに.swiftlint.ymlを追加しておくのもいいですよ。
(省略)
#Swift Lint
.swiftlint.yml
( 省略)


6. .swiftlint.ymlができたら、 プロジェクトを開いてビルドします。
警告が、だばぁ〜

7. ワーニングが山ほど出ますので、涙をこらえながら、ひとつひとつ修正していきます。

ちょっと気になったのは、警告対象の箇所を修正しても、警告を示す黄色の表示が消えないことがあったことです。開いているのとは別のファイルに警告が残っているときに、その警告が開いているファイルに表示されるということが、しばしば起こるようです。

修正したのに警告表示が消えないなぁ、というような時は一旦そのファイルを閉じて、Xcodeの左の問題ナビゲーターに表示されているワーニングメッセージをダブルクリックして、その警告が指しているSwiftファイルを開きなおすと、うまく問題の箇所に黄色の表示が現れると思います。

※2017/05/16追記
この不具合はSwiftLintをバージョン0.18.1に更新したら起きなくなりました。どうやら修正されたようです。


正直、Lintが指摘する書き方に対して、「えー、そこはこう書くだろぉ〜、普通ぅ〜」というようなことは、ままありますが、そんなもん慣れです。その書き方に慣れてさえしまえば、ちょっとした書き違い(間違いじゃなくて)がなくなって、可読性がよくなります。こういうことの積み重ねがバグの少ないプログラムに繋がっていくんだろうな、って思ってます。

、、、たぶんね(白目)。

しかしSwift、もう少しコンパイルが早くなりませんかね?「凛としてSwift (血闘編ノ壱)」に続きます。


♪♪♪


こちらもどうぞ。
凛としてSwift (血闘編ノ壱)
凛としてSwift (血闘編ノ弐)
凛としてSwift (血闘編ノ参)
帰ってきた凛としてSwift(くるくる編)