Generambaを使ってみよう！

## はじめに
弊社では、技術領域というグループがあり、それぞれの技術に特化して何かしらの活動をしています。
私はモバイル領域に所属しており、iOSについて学習したり、実際にアプリ開発を行っています。
そのモバイル領域の一環で、チームでiOSアプリの開発をすることになりました。
お試し的なアプリですので、軽量なアーキテクチャを使おうという話になり、MVPを使って開発を進めていくことになりました。
MVPで開発していく上で、Model・View・Presenterそれぞれのswiftファイルを作成するのはめんどくさいため[Generamba](https://github.com/strongself/Generamba)というツールを使用してswiftファイルを自動生成した時の環境構築について書いていきます。

この記事はTDCソフト株式会社Advent Calendarの18日目です。

https://qiita.com/advent-calendar/2022/tdc
## Generambaについて
GithubのREADMEには、`Generambaは、Xcodeで動作するように作られたコードジェネレータです。主にVIPERモジュールを生成するために設計されていますが、他のクラス（Objective-CとSwiftの両方）の生成のために非常に簡単にカスタマイズすることができます。`と書かれており、VIPERというアーキテクチャを使用する際に使うと便利なことが分かります。

今回使用するアーキテクチャはMVPですので、MVPに使いやすいようにカスタマイズしていきます。

## Generambaのインストール
>注意｜Ruby 2.2 以降のバージョンが必要です。
```
gem install generamba
```

## セットアップ
>注意｜プロジェクトファイルがすでに作成されている前提
```
generamba setup
```
いくつか質問が聞かれるので、適当に入力してEnterを押していきます。
これが終わると、`Rambafile`という設定ファイルが作成されます。
このファイルの中に質問で答えた内容が含まれているので、たとえ間違った内容を入力したとしても`Rambafile`で直接修正することができます。
```shell:
# ヘッダーで使用される会社名
The company name which will be used in the headers: TDC SOFT Inc.
# プロジェクト名は「hoge」です。使用しますか？
The name of your project is hoge. Do you want to use it? (yes/no) yes
# プロジェクトのプレフィックス（ある場合）(Object-C用)
The project prefix (if any): 
# プロジェクトの.xcodeprojファイルへのパスは「hoge.xcodeproj」です。これを使用しますか？
The path to a .xcodeproj file of the project is 'hoge.xcodeproj'. Do you want to use it? (yes/no) yes
# MODULESを追加するための適切なターゲットを選択する（インデックスを入力する）
Select the appropriate target for adding your MODULES (type the index):
0. hoge
1. hogeTests
2. hogeTests
 0
# このプロジェクトではユニットテストを使っていますか？
Are you using unit-tests in this project? (yes/no) yes
# TESTSを追加する適切なターゲットを選択する（インデックスを入力する）
Select the appropriate target for adding your TESTS (type the index):
0. hoge
1. hogeTests
2. hogeTests
 1
# すべてのモジュールを1つのパスで追加しますか？(はい/いいえ)
Do you want to add all your modules by one path? (yes/no) yes
# Xcodeとファイルシステムの両方で、ファイルに同じパスを使用したいですか？(はい/いいえ)
Do you want to use the same paths for your files both in Xcode and the filesystem? (yes/no) yes
# 新しいモジュールを作成するためのデフォルトのパス
The default path for creating new modules: ./hoge
# テストを作成するためのデフォルトのパス
The default path for creating tests: hogeTests
# Cocoapodsをお使いですか（はい／いいえ）
Are you using Cocoapods? (yes/no) no
# Carthageを利用していますか（はい・いいえ）
Are you using Carthage? (yes/no) no
# Rambafileには、よく知られたテンプレートを追加したいですか？
Do you want to add some well known templates to the Rambafile? (yes/no) no
```

## テンプレートの作成
今回は自分でテンプレートを作成するため、以下のコマンドで独自のテンプレートを作成します。
```bash
generamba template create [TEMPLATE_NAME]
```
コマンドを叩くと、いくつかの質問をされるのでそれらを適当に入力していき、Enterを押すとデフォルトのテンプレートと設定ファイルが作成されます。
以下はhogeテンプレートを作成した時の例です
```bash
$ generamba template create hoge
// 新しいテンプレートの概要
The brief description of your new template: hoge
// テンプレートの著者
Who is the author of this template: hoge
// テンプレートのライセンス
What license will be used (e.g. MIT): MIT
// テンプレートにサードパーティの依存性があるかどうか
Will your template contain any third-party dependencies (available via Cocoapods or Carthage)? (yes/no) no
```
```:デフォルトのテンプレート
 hoge
    ├── Code
    │   └── Service
    │       ├── service.h.liquid
    │       └── service.m.liquid
    ├── Tests
    │   └── Service
    │       └── service_tests.m.liquid
    └── hoge.rambaspec // 設定ファイル
```
<details><summary>実際に作ったテンプレート</summary>

```:フォルダ構成
Templates/
└── mvp_controller
    ├── Code
    │   ├── Models
    │   │   └── model.swift.liquid
    │   ├── Presenters
    │   │   └── presenter.swift.liquid
    │   └── Views
    │       └── viewcontroller.swift.liquid
    └── mvp_controller.rambaspec
```
### Modelのテンプレート
<details><summary>開くとコードが表示されます</summary>

```liquid:model.swift.liquid
//
//  {{ module_info.file_name }}
//  {{ module_info.project_name }}
//
//  Created by {{ developer.name }} on {{ date }}.
//  Copyright © {{ year }} {{ developer.company }}. All rights reserved.
//

import Foundation

protocol {{ module_info.name }}ModelInput: AnyObject {

}

class {{ module_info.name }}Model: {{ module_info.name }}ModelInput {
    
}
```
</details>

### Presenterのテンプレート
<details><summary>開くとコードが表示されます</summary>

```liquid:presenter.swift.liquid
//
//  {{ module_info.file_name }}
//  {{ module_info.project_name }}
//
//  Created by {{ developer.name }} on {{ date }}.
//  Copyright © {{ year }} {{ developer.company }}. All rights reserved.
//

import Foundation

protocol {{ module_info.name }}PresenterInput: AnyObject {
    func viewIsReady()
}

protocol {{ module_info.name }}PresenterOutput: AnyObject {
    
}

class {{ module_info.name }}Presenter: {{ module_info.name }}PresenterInput {
    weak var view: {{ module_info.name }}PresenterOutput?
    var model: {{ module_info.name }}ModelInput

    init(view: {{ module_info.name }}PresenterOutput, model: {{ module_info.name }}ModelInput) {
        self.view = view
        self.model = model
    }
}

extension {{ module_info.name }}Presenter {
    func viewIsReady() {
        
    }    
}
```

</details>

### Viewのテンプレート
<details><summary>開くとコードが表示されます</summary>

```liquid:viewcontroller.swift.liquid
//
//  {{ module_info.file_name }}
//  {{ module_info.project_name }}
//
//  Created by {{ developer.name }} on {{ date }}.
//  Copyright © {{ year }} {{ developer.company }}. All rights reserved.
//

import UIKit

class {{ module_info.name }}ViewController: UIViewController {
    var presenter: {{ module_info.name }}PresenterInput!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        presenter.viewIsReady()
    }
}

extension {{ module_info.name }}ViewController: {{ module_info.name }}PresenterOutput {

}
```

</details>

### テンプレートの設定ファイル
```:.rambaspec
# Template information section
name: mvp_controller
summary: MVP Template
author: m1k50118
version: 1.0.0
license: MIT

# The declarations for code files
code_files:
- {name: Views/ViewController.swift, path: Code/Views/viewcontroller.swift.liquid}
- {name: Presenters/Presenter.swift, path: Code/Presenters/presenter.swift.liquid}
- {name: Models/Model.swift, path: Code/Models/model.swift.liquid}

# The declarations for test files
#test_files:
# - {name: Presenters/PresenterTests.swift, path: Tests/Presenters/presenter_tests.swift.liquid}
# - {name: Models/ModelTests.swift, path: Tests/Models/model_tests.swift.liquid}

# Template dependencies
dependencies:
```

</details>

### 作成したテンプレートを設定ファイルに反映
`Rambfile`に上記のファイルを追加します。
```:Rambfile
### Headers settings
company: TDC SOFT Inc.

### Xcode project settings
project_name: hoge
xcodeproj_path: hoge.xcodeproj
prefix: hoge

### Code generation settings section
# The main project target name
project_target: hoge

# The file path for new modules
project_file_path: ./hoge

# The Xcode group path to new modules
project_group_path: ./hoge

### Tests generation settings section
# The tests target name
test_target: hogeTests

# The file path for new tests
test_file_path: hogeTests

# The Xcode group path to new tests
test_group_path: hogeTests

### Templates
templates:
#- {name: local_template_name, local: 'absolute/file/path'}
#- {name: remote_template_name, git: 'https://github.com/igrekde/remote_template'}
#- {name: catalog_template_name}
- {name: hoge, local: './hoge'}
```

## テンプレートのインストール
```shell
$ generamba template install
```

## モジュールの作成
```shell
$ generamba gen [MODULE_NAME] [TEMPLATE_NAME]
```
`[MODULE_NAME]`に作成するモジュールの名前を入力し、`[TEMPLATE_NAME]`に上記で作成したカスタムテンプレート(hoge)を入力すると、ファイルが自動生成できます。

# 感想
Generamba以外にも、Xcodegenでプロジェクトファイルを生成したり、チームのフォルダ構成に合わせるためにMakefileでゴニョゴニョしたりと、他にも環境構築はやったのですがここで書くのは長すぎてしまうので別の機会に書くかもしれないです。
めんどくさい作業はツールでどうにかするに限りますね。

次回は@dango_yさんです。

## 参考文献
- https://github.com/strongself/Generamba