Xcodeテンプレートを作ってみた

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ボタンを押下します。

名前を指定して、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

  • このエントリーをはてなブックマークに追加
にっきー
にっきー niki

最近、仕事に復帰し、子育てと仕事の両立に奮闘しているママエンジニアです。趣味はダイビングで、海に潜って魚や珊瑚の写真を撮るのが好きです。