Xcodeテンプレートを作ってみた
iOSエンジニアの二木です。
前回、EAPiOSアプリのEAP MVPアーキテクチャの取り組みを紹介しました。
この取り組みで、これまで全てViewControllerに書いていた処理(表示ロジックや表示データの取得など)をPresenter側に切り出したことにより効果が出たのは良かったのですが、Presenterファイルが1つ増えてしまいViewControllerとPresenterの連結部分も書くのが面倒になってきて、どうしようか悩んでいました。。。
そこで、なにか良い方法はないかなと探していたところXcodeのテンプレート作れば楽になると思い、EAPのViewControllerとPresenterのテンプレートと作成してみました。
Xcodeテンプレート作成方法
1.既存テンプレートからコピー
最初からテンプレートを作成すると大変なので、既存のテンプレートをコピーします。
(既存テンプレートからコピーして作成すると変更箇所だけ直せばよいので楽かと思います。)
既存のXcodeテンプレートは以下の場所にあります。
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates
Swift File.xctemplateをまるっとコピーし、フォルダの名称を変更します。(xctemplate前の名称を変更)
ViewController用とPresenter用に2つコピーし、以下のようにそれぞれ名称を変更しました。
- ViewController.xctemplate(ViewController用のテンプレート)
- Presenter.xctemplate(Presenter用のテンプレート)
2.ソースファイルの変更
ソースファイルを変更していきます。xctemplateフォルダ内に___FILEBASENAME___.swift
というファイルがあるので、ファイルをダブルクリックして、Xcodeでファイルを直接変更していきます。
変更後のファイルはこんな感じです。
●ViewController.xctemplat/___FILEBASENAME___.swift
//___FILEHEADER___ import UIKit import EAPCore import EAPAppBase protocol ___VARIABLE_NAME___ViewInterface: class { func setup() } class ___VARIABLE_NAME___ViewController: UIViewController { typealias PresenterInterface = ___VARIABLE_NAME___PresenterInterface private lazy var presenter: PresenterInterface = ___VARIABLE_NAME___Presenter(interface: self) override func viewDidLoad() { super.viewDidLoad() presenter.viewDidLoad() } } // MARK: - ViewInterface Extension extension ___VARIABLE_NAME___ViewController: ___VARIABLE_NAME___ViewInterface { func setup() { //TODO: 初期化処理を記述する } }
●Presenter.xctemplate.xctemplat/___FILEBASENAME___.swift
//___FILEHEADER___ import EAPCore import EAPAppBase protocol ___VARIABLE_NAME___PresenterInterface: class { func viewDidLoad() } class ___VARIABLE_NAME___Presenter { typealias ViewInterface = ___VARIABLE_NAME___ViewInterface private unowned let viewInterface: ViewInterface init(interface: ViewInterface) { viewInterface = interface } } // MARK: - PresenterInterface Extension extension ___VARIABLE_NAME___Presenter: ___VARIABLE_NAME___PresenterInterface { func viewDidLoad() { viewInterface.setup() } }
上記のテンプレートに___VARIABLE_NAME___
というのが出てきますが、これはユーザーに入力してもらった値を反映するための変数になります。テンプレートを利用してViewControllerやPresenterのswiftファイルを作ったときに互いにクラスの命名を揃えるようにしているため、この変数を利用しています。
3.変数の利用
コピーしてきた既存テンプレートSwift File.xctemplate
には変数を利用できる設定がされていないので、ViewController.xctemplatとPresenter.xctemplatで利用できるようにフォルダ内にあるTemplateInfo.plistにOptionsを追加し、各項目を設定します。
上記で設定した内容は以下の通りです。
①VARIABLEに続くIdentifierを指定します。(___VARIABLE_NAME___
の場合はNAME
)
②入力値を求めるのでYESを指定します。
③入力値を求める画面で表示するテキストです。(下記画像③を参照)
④入力値を求める画面で表示する説明です。(下記画像④を参照)
⑤入力値は文字列なのでtextを指定します。
⑥デフォルトの値です。適切なものを設定してください。
4.エンジニア間での共有
作成したテンプレートは、他のエンジニアXcodeでも利用できるようにしたいので、iOSのプロジェクト内にテンプレートファイルを置きます。
EAPではこんな感じの階層構造になっています。
eap_ios |_Share |_Templates |_copy_templates.sh |_Templates |_File Templates |_EAP |_Presenter.xctemplate |_ViewController.xctemplate
Xcodeでビルド時にテンプレートを他エンジニアのローカル環境にコピーしたいため、Build PhaseからRun Scriptを作成します。
実行するスクリプトcopy_templates.shの内容は以下の通りです。
#!/bin/bash current_path=$(cd $(dirname $0); pwd) if [ ! -d ~/Library/Developer/Xcode/Templates ] then mkdir ~/Library/Developer/Xcode/Templates fi if [ ! -d ~/Library/Developer/Xcode/Templates/File\ Templates ] then mkdir ~/Library/Developer/Xcode/Templates/File\ Templates fi ln -fs ${current_path}/Templates/File\ Templates/EAP ~/Library/Developer/Xcode/Templates/File\ Templates
Xcodeでビルドすると、~/Library/Developer/Xcode/Templates
配下にテンプレートファイルがコピーされているのがわかります。
いよいよテンプレートを使ってみる
ViewControllerを作ってみます。いつも通りにcmd+Nでファイルを新規作成します。
Otherの下に「EAP」ができているので、そこからViewControllerを選択してNextボタンを押下します。
あとは、ViewControllerのファイル名を指定すればOKです。
Presenterも同じように、cmd+Nでファイルを新規作成していきます。
これで、ViewControllerとPresenterファイルが出来上がりです。
- SampleViewController.swift
protocol SampleViewInterface: class { func setup() } class SampleViewController: UIViewController { typealias PresenterInterface = SamplePresenterInterface private lazy var presenter: PresenterInterface = SamplePresenter(interface: self) override func viewDidLoad() { super.viewDidLoad() presenter.viewDidLoad() } } // MARK: - ViewInterface Extension extension SampleViewController: SampleViewInterface { func setup() { //TODO: 初期化処理を記述する } }
- SamplePresenter.swift
protocol SamplePresenterInterface: class { func viewDidLoad() } class SamplePresenter { typealias ViewInterface = SampleViewInterface private unowned let viewInterface: ViewInterface init(interface: ViewInterface) { viewInterface = interface } } // MARK: - PresenterInterface Extension extension SamplePresenter: SamplePresenterInterface { func viewDidLoad() { viewInterface.setup() } }
TAG
最近、仕事に復帰し、子育てと仕事の両立に奮闘しているママエンジニアです。趣味はダイビングで、海に潜って魚や珊瑚の写真を撮るのが好きです。
TAG
- Android
- AWS
- Bitrise
- CodePipeline
- Firebase
- HTML
- iOS
- IoT
- JavaScript
- KPI
- Linux
- Mac
- Memcached
- MGRe
- MGReのゆるガチエンジニアブログ
- MySQL
- PHP
- PICK UP
- PR
- Python
- Ruby
- Ruby on Rails
- SEO
- Swift
- TIPS
- UI/UX
- VirtualBox
- Wantedly
- Windows
- アクセス解析
- イベントレポート
- エンジニアブログ
- ガジェット
- カスタマーサクセス
- サーバ技術
- サービス
- セキュリティ
- セミナー・展示会
- テクノロジー
- デザイン
- プレスリリース
- マーケティング施策
- マネジメント
- ラボ
- リーンスタートアップ
- 企画
- 会社紹介
- 会社紹介資料
- 勉強会
- 実績紹介
- 拡張性
- 採用
- 日常
- 書籍紹介
- 歓迎会
- 社内イベント
- 社員インタビュー
- 社長ブログ
- 視察
- 開発環境