Swiftのコンパイルはストレスが溜まるので

SwiftでiOSアプリを作成しているとコンパイル速度が遅くてストレス溜まるなーというタイミングが多々あるのでコンパイル速度というかストレスを減らそうという対応を幾つか記載。

やったこと

プロジェクトのコンパイル時間計測オプションを設定する

Xcodeプロジェクトの Bundle Settings にある Swift Compiler - Custom Flags-Xfrontend -debug-time-function-bodies を設定する

BuildTimeAnalyzer を入れる

BuildTimeAnalyzer プラグインをインストールする

現行バージョンではBuildTimeAnalyzerの更新中にビルドし直すとクラッシュするけど重いメソッドが見られるので便利。

InterfaceBuilderの自動コンパイルを止める

StoryboardかXibファイルを開いてInterfaceBuilderが表示されているときに Automatically Refresh Views のチェックを外す。

Xcode -> Editor -> Automatically Refresh Views

これは多分 @IBDesignable を使っているときくらいしか出番が無いけどスペックの低い端末で開発するときには便利。
Automatically Refresh Views のチェックを外すと Refresh All Views が有効になるので任意のタイミングで更新することができる。

コーディング

1. 型を宣言する

よく言われている対策だけど本当に効果があるので型は宣言するべき。

対応を忘れやすいのはこんな感じで引数にdictionaryを突っ込んでしまったりして。

NSError(domain: "domain", code: 0, userInfo: [ NSLocalizedDescriptionKey: "error message" ])

その他、コーディングで気をつけたほうがいいこと

細かく書く気分じゃないのでソース貼ります。メソッド名の横にコンパイル時間だけ書きます。検証用コードなので適当に書いています。

class ViewController: UIViewController {
    
    // dictionary pattern 1
    func dict1() { // 20.4ms
        let error: NSError = NSError(domain: "domain", code: 0, userInfo: [ NSLocalizedDescriptionKey: "error message" ])
        print("error: \(error)")
    }
    // dictionary pattern 2
    func dict2() { // 1.9ms
        let userInfo: [String: AnyObject] = [ NSLocalizedDescriptionKey: "error message" ]
        let error: NSError = NSError(domain: "domain", code: 0, userInfo: userInfo)
        print("error: \(error)")
    }
    // dictionary pattern 3
    func dict3() { // 1.7
        let domain: String = "domain"
        let code: Int = 0
        let userInfo: [String: AnyObject] = [ NSLocalizedDescriptionKey: "error message" ]
        let error: NSError = NSError(domain: domain, code: code, userInfo: userInfo)
        print("error: \(error)")
    }
    
    // ceil pattern 1
    func height1() { // 21.6ms
        let window: UIWindow = UIApplication.sharedApplication().windows.first!
        let width: CGFloat = self.view.frame.size.width
        let margin: CGFloat = (width-window.frame.size.width)
        let height: CGFloat = ceil((width - margin) / 16.0 * 9.0)
        print("height: \(height)")
    }
    // ceil pattern 2
    func height2() { // 2.7ms
        let window: UIWindow = UIApplication.sharedApplication().windows.first!
        let width: CGFloat = self.view.frame.size.width
        let margin: CGFloat = (width-window.frame.size.width)
        var height: CGFloat = width - margin
        height = height / 16.0 * 9.0
        height = ceil(height)
        print("height: \(height)")
    }
    // ceil pattern 3
    func height3() { // 3.1ms
        let window: UIWindow = UIApplication.sharedApplication().windows.first!
        let width: CGFloat = self.view.frame.size.width
        let margin: CGFloat = (width-window.frame.size.width)
        var height: CGFloat = (width - margin) / 16.0 * 9.0
        height = ceil(height)
        print("height: \(height)")
    }
    // ceil pattern 4
    func height4() { // 2.7ms
        let window: UIWindow = UIApplication.sharedApplication().windows.first!
        let width: CGFloat = self.view.frame.size.width
        let margin: CGFloat = (width-window.frame.size.width)
        var height: CGFloat = (width - margin) / 16.0
        height = ceil(height * 9.0)
        print("height: \(height)")
    }
    
    // CGRectGet pattern 1-1
    func rectGet1_1() { // 2.3ms
        let y: CGFloat = CGRectGetMaxX(self.view.subviews[0].frame)
        print("y: \(y)")
    }
    // CGRectGet pattern 1-2
    func rectGet1_2() { // 2.0ms
        let frame: CGRect = self.view.subviews[0].frame
        let y: CGFloat = CGRectGetMaxX(frame)
        print("y: \(y)")
    }
    // CGRectGet pattern 1-3
    func rectGet1_3() { // 1.8ms
        let view: UIView = self.view.subviews[0]
        let y: CGFloat = CGRectGetMaxX(view.frame)
        print("y: \(y)")
    }
    // CGRectGet pattern 2-1
    func rectGet2_1() { // 3.0ms
        let y: CGFloat = self.view.subviews[0].frame.origin.y + self.view.subviews[0].frame.size.height
        print("y: \(y)")
    }
    // CGRectGet pattern 2-2
    func rectGet2_2() { // 1.8ms
        let frame: CGRect = self.view.subviews[0].frame
        let y: CGFloat = frame.origin.y + frame.size.height
        print("y: \(y)")
    }
    // CGRectGet pattern 2-3
    func rectGet2_3() { // 1.8ms
        let view: UIView = self.view.subviews[0]
        let y: CGFloat = view.frame.origin.y + view.frame.size.height
        print("y: \(y)")
    }
    
}

ビルドにかかった時間はこんな感じでした。ソース読んでもらえれば分かりますよね。

検証機のスペック