[iOS, Swift] FLEXというDebugに便利なツール

# はじめに
iOS開発で、Debug用のツールに「[FLEX](https://github.com/FLEXTool/FLEX)」という便利なものがあります。実務で使ってみて非常に便利だったことと、FLEXについて説明する記事があまりなかったので、本記事を書こうと思いました。

# FLEX (Flipboard Explorer) とは
FLEXは、iOS開発で用いられるアプリ内Debugツールです。2022/12月時点で、Githubのリポジトリのスター数が13.3kもつく非常に有名なライブラリです。

Githubのリンク： https://github.com/FLEXTool/FLEX

以下のように、iOSシミュレータ上でFLEXを起動すると、ツールバーが表示され、様々なことを行うことができます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397136/b5bd5346-1a4b-2781-76b2-da04972c7836.gif" width="300">

# FLEXのデフォルト機能
* ビューの修正（プロパティの変更・メソッド呼び出しなど）
* ネットワークの通信履歴の確認
* Heap上のオブジェクトの確認
* アドレスからオブジェクトの探索
* iOSシミュレータでショートカットキーの利用（例えば、`↑↓`キーで上下にスクロール）。カスタマイズキーも登録可能。
* ファイルの閲覧
* データベースのテーブル閲覧（SQLite又はRealm）
* 様々な圧力レベルの3Dタッチ

などが挙げられます。詳しくは[README](https://github.com/FLEXTool/FLEX#readme)を参照してみてください。

個人的に、「ネットワークの通信履歴」を確認できる機能は非常に便利なので、よく利用しています。APIのリクエスト、レスポンスの確認や、リクエストに対するCurlコマンドのコピーもできます。

# FLEXで登録した任意の処理を呼び出す
上記の機能に加えて、FLEXは、Debug用に任意の処理を登録することができ、FLEXのMenuからそれらを簡単に実行することができます。

例として、今回はFLEXを用いて「画面遷移」と「APIを叩く」処理を呼び出してみます。

## セットアップ
### FLEXのインストール
FLEXはSPM, CocoaPods, Carthageなどを用いてインストール可能です。今回はSPMでインストールしていきます。

以下、手順です。

1. PROJECTでプラスボタンを押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397136/7a899d88-15b6-cb53-2c05-c1bcc06480a3.png)

2. 検索バーにリポジトリのリンク（）を貼り、Dependency Ruleを適宜設定して、[Add Package]ボタンを押す
![スクリーンショット 2022-12-06 10.50.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397136/f9c90add-8856-d401-6f18-0df224889586.png)

3. FLEXにチェックをつけて、[Add Package]ボタンを押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397136/e3757e8a-02e9-678f-3ffc-39d7d886feda.png)

4. Packagesの項目と左のサイドバーにFLEXが追加さればOKです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397136/a959111a-12cc-3290-d9ca-de2557e6aab7.png)


### FLEXをDebugモードでのみ有効化
FLEXはDebug用のツールなので、Debugモードの時のみ入れておけば良く、Releaseモードに入れる必要はありません。

そこで、TARGETの`Build Settings > Build Options > Excluded Source File Names`の`Release`の項目に、`FLEX*`を追加します。これにより、接頭辞がFLEXとつくファイルがReleaseモードで除外されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397136/098463a8-773c-fd60-5100-ec30fbca2f36.png)

## 実装
最終的なソースコード： https://github.com/Toshiyana/FlexExample/tree/main

今回は、DebugToolというクラスを作成し、setupFlexという関数の中にFLEXで呼び出したい処理を追加していきます。

```swift
#if DEBUG
import FLEX
import SwiftUI

final class DebugTool {
    func setupFlex() {
        // 今回は、ここにFLEXで呼び出したい処理を追加していく
    }
}
#endif
```

setupFlex関数に記述した処理を呼び出せるように、Appのイニシャライザを以下のように記述します。

```swift
import SwiftUI

@main
struct FlexExampleApp: App {
    init() {
        #if DEBUG
        let debugTool = DebugTool()
        debugTool.setupFlex()
        #endif
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
```

また、Rleaseモードで実行されずに、Debugモードでのみ実行されるよう`#if DEBUG`と`#endif`で囲んでいます。

### 例１： FLEXを用いて任意の画面に遷移
遷移先の適当なViewをSampleViewとします。

```swift
import SwiftUI

struct SampleView: View {
    var body: some View {
        Text("Sample View")
    }
}

struct SampleView_Previews: PreviewProvider {
    static var previews: some View {
        SampleView()
    }
}
```

そして、setupFlex関数に画面遷移の処理を追加します。

```swift
#if DEBUG
import FLEX
import SwiftUI

final class DebugTool {
    func setupFlex() {
        FLEXManager.shared.registerGlobalEntry(withName: "SmpleViewに画面遷移") { hostViewController in
            let controller = UIHostingController(rootView: SampleView())
            hostViewController.present(controller, animated: true)
        }
    }
}
#endif
```

これで、FLEXを用いてSampleViewの画面に遷移できるようになりました。

iOSシミュレータを起動し、キーボードの`f`キーを押してFLEXのツールバーを表示して、Menuから実行できることを確認します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397136/d5186eb6-63ec-f40e-aa71-54ddc9a0d49f.gif" width="300">

このように、FLEXに画面遷移の処理を登録して、任意の画面をすぐに確認することができます。

### 例2： FLEXを用いてAPIを叩く
今回は、GithubのAPIを叩いてみます。簡単に、APIを呼ぶための処理を以下のようにしました。

```swift
import Foundation
import Combine

enum GithubAPI {
    static func searchRepos(page: Int, perPage: Int) -> AnyPublisher<[Repository], Error> {
        let url = URL(string: "https://api.github.com/search/repositories?q=swift&sort=stars&page=\(page)&per_page=\(perPage)")!
        return URLSession.shared
            .dataTaskPublisher(for: url)
            .tryMap { try JSONDecoder().decode(GithubSearchResult.self, from: $0.data).items }
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    }
}

struct GithubSearchResult: Codable {
    let items: [Repository]
}

struct Repository: Codable, Identifiable, Equatable {
    let id: Int
    let name: String
    let description: String?
    let stargazersCount: Int

    enum CodingKeys: String, CodingKey {
        case id
        case name
        case description
        case stargazersCount = "stargazers_count"
    }
}
```
そして、setupFlex関数の例１の画面遷移の処理の下に、APIを叩く処理を追加します。

```swift
#if DEBUG
import FLEX
import SwiftUI
import Combine

final class DebugTool {
    private var subscriptions = Set<AnyCancellable>()

    func setupFlex() {
        // SmpleViewに画面遷移の処理...（省略）

        // APIを叩く処理を追加
        FLEXManager.shared.registerGlobalEntry(withName: "GithubAPIを叩く") { _ in
            GithubAPI.searchRepos(page: 1, perPage: 30)
                .sink(receiveCompletion: { completion in
                    switch completion {
                    case .finished:
                        break
                    case .failure(let error):
                        print("DEBUG: \(error.localizedDescription)")
                    }
                }) { repositories in
                    print("DEBUG: \(repositories)")
                }
                .store(in: &self.subscriptions)
        }
    }
}
#endif

```

これで、FLEXを用いてAPIを叩くことができるようになりました。

iOSシミュレータを起動し、キーボードの`f`キーを押してFLEXのツールバーを表示して、Menuから実行できることを確認します。

![api-2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397136/47d14194-196f-bdb7-9503-55fffc068bc2.gif)

一例として、画面遷移やAPIを叩く処理をFLEXに登録しましたが、任意の処理を登録できるので、その他にも色々な使い方ができると思います。例えば、ログイン、ログアウト、キャッシュのクリアなどの操作を登録しておくのも良いですね！

# まとめ
FLEXはDebugに非常に便利なツールなので、使ったことのない人はぜひ使ってみてください！

# 参考
* https://github.com/FLEXTool/FLEX