Swiftのデバッグが捗るデバッグ関数を独自定義する

# Swiftのデバッグ関数をつくる

[アプリ道場 Advent Calendar 2015](http://qiita.com/advent-calendar/2015/app-dojo)のトップバッター、[@mo_to_44](https://twitter.com/mo_to_44)です。Advent Calendarの最初の投稿なのでゆるめにデバッグ関数の定義をしてみようと思います。

## `DebugLog`関数

こちらがコードです。[コードはgistにアップ](https://gist.github.com/mnat44/f625f38d3da21e626149)しています。

```swift
func DebugLog(@autoclosure condition: () -> Bool = true, _ message: String = "", function: StaticString = __FUNCTION__, file: StaticString = __FILE__, line: UInt = __LINE__) {
    
    #if DEBUG
        if let fileName = NSURL(string: String(file))?.lastPathComponent {
            print("time: \(NSDate()), message: \(message), function: \(function), file: \(fileName), line: \(line)")
        } else {
            print("time: \(NSDate()), message: \(message), function: \(function), file: \(file), line: \(line)")
        }
        
        assert(condition, message, file: file, line: line)
    #endif
    
}
```

`#if DEBUG`の設定方法ですが、Build Settings > Swift Compiler > Other Swift Flagsに `-DDEBUG`を指定すればOKです。

<img width="465" alt="スクリーンショット 2015-11-30 0.16.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/45525/e6c6f695-ac2e-ab68-8f80-98cde63dd7c2.png">


### なにをしているのか
なにをしているのか見ていきましょう。`DebugLog`関数でやっていることは以下の通りです。

1. `DebugLog`が呼び出された時間の出力
2. `DebugLog`呼び出し元のファイル名出力
3. `DebugLog`呼び出し元の関数名出力
4. `DebugLog`呼び出し元のファイル内の行数出力
5. `assert`の実行

1については簡単ですね。`print`関数内で`NSDate()`を呼び出して現在の時間を出力しているだけです。それ以外の部分について見ていきましょう。

### 関数名、ファイル名、ファイル内の行数の出力
Swiftでは`__FILE__`, `__LINE__`, `__COLUMN__`, `__FUNCTION__`という特殊な識別子が定義されており、これを利用して関数名、ファイル名、ファイル内行数の出力を行っています [（参考）](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/LexicalStructure.html)。

| `__FILE__` | `__LINE__` | `__COLUMN__` | `__FUNCTION__` |
|:-----------:|:------------:|:------------:|:------------:|
|  ファイル名  | 行数 | カラム | 関数 |

### `assert`関数呼び出し
`condition`が`false`だった場合は`assert`が実行されるようになっています。異常な値が入っている場合に`assert`が実行されます。


### デフォルト引数を指定してログと`assert`を使い分けるようにする
`DebugLog`の引数は全て**デフォルト引数**を指定しているので、`DebugLog()`のように呼び出した場合は単にログのみが出力されます。

```swift
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        DebugLog()
    }

}
```

上記のコードを実行した場合、以下のログが出力されます。
```
time: 2015-11-29 14:55:09 +0000, message: , function: viewDidLoad(), file: ViewController.swift, line: 16
```

デバッグ時に`assert`で値のチェックもしたい場合は以下のように呼び出します。`someValue`が1未満の場合はログの出力とともに、`assert`で実行を停止します。

```swift
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let someValue = 0
        DebugLog(someValue > 0)
    }

}
```

以下のようなログが出力され、プログラムが停止します。
```
time: 2015-11-29 14:58:32 +0000, message: , function: viewDidLoad(), file: ViewController.swift, line: 18
assertion failed: : file /Users/Motoki/Desktop/Example/Example/ViewController.swift, line 18
```

第2引数には付加情報としてメッセージを入れることができます。

```swift
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let someValue = 0
        DebugLog(someValue > 0, "Unexpected value")
    }
}
```

このように指定した場合は、以下のように出力されます。

```
time: 2015-11-29 15:11:03 +0000, message: Unexpected value, function: viewDidLoad(), file: ViewController.swift, line: 16
assertion failed: Unexpected value: file /Users/Motoki/Desktop/Example/Example/ViewController.swift, line 16
```

## まとめ
必要な情報はプロジェクトによっても変わってくると思うのですが、関数名、ファイル名、行数などが分かるとデバッグ時に便利かと思います。今回は状況によっては`assert`関数を実行させるようにしました。

デバッグ関数を定義する際の参考にしてみてください。

## 参考
* [Swift Blog](https://developer.apple.com/swift/blog/?id=15)