Swift の extension ってどこに書きますか? 例えば String
の extension を書く場合.
StringExtensions.swift
みたいなファイルにまとめて書くStringHoge.swift
、StringHuga.swift
みたいに意味のある単位で分けて書く- 使うところで
private extension
に書く
などなど色々あると思います。 実際はある一つの方法だけを選ぶのではなく状況に合わせて書く事が多いと思います。
僕はどんな事を考えながら extension を書く場所を決めているかを紹介したいと思います。
個人的な基本的な考えでは framework が用意している型に対して広いスコープの extension を作るのは結構メンテナンス性が悪いと思うので気をつけて実装するようにしています。
めちゃくちゃ汎用的でコードベースのどこからでも使うもの
- 前提としてこういう機能は最初から
String
にある場合が多いので書くことは少ないです - このパターンの場合は
StringExtensions.swift
のようなグローバルっぽい名前のファイルに作ります
/// StringExtensions.swift extension String { var firstOrEmpty: String { return first.map { String($0) } ?? "" } }
ある特定の機能のまとまりがあり、コードベースのいろいろな場所で使う場合
例えば何らかの Hoge があってこれの RxSwift の extension だけまとめたい場合とかは、それと分かるような名前をつけます。
命名規則はコードベース内で統一されていればいいと思うので、 Hoge+Reactive.swift
でも HogeReactiveExtensions.swift
でもなんでもいいと思います。
// HogeReactiveExtensions.swift extension Reactive where Base: Hoge { // 幾つかのメソッド.. }
UI の表示に使うもの (UI の仕様を実装) & テスト不要なもの
例えば3文字ごとにスペースを入れた文字列を表示したいという(謎の)仕様がある場合。
この場合はその UI の要素(View or ViewController) の中に private extension
として書くことが多いです。
スコープ的にテストコードから見えなくなるのでテストが不要なぐらい簡単な場合にはこの方法を取ります。
この方法の利点はこの extension が、この View からしかアクセスできないので安全です。例えば View の仕様が変わっても安心して修正できます。 また、他の箇所から使うことを想定していないので、プロパティ・メソッド名が短くて済みます。命名の負荷が減るのはいいこと。
// HogeHogeView.swift private extension String { var insertSpacedString: String { return "処理" } }
例としてこれが private
じゃない場合に何が起こるかを説明します。
private じゃない場合、このメソッドは複数の箇所(View_A, View_B) から使うことができます。 このとき View_B の仕様だけ変わった場合に View_A でも使ってることを知らずに extension のコードを修正してしまうと View_A に予期せぬ変更が発生してしまいます。
「テストしとけば大丈夫でしょ」という意見もあると思いますが、現実問題で網羅するのは大変なので間違いが発生しにくいコードを書いたほうが有益だと考えています。
他の箇所からも使いたくなったら?
まず、他の View からも使いたくなったら、 View そのものが再利用できないか考えましょう。
別の View から使う必要が出たらそれと分かるような名前の extension のファイルを作ります 例: StringDisplayHogeExtension.swift
。
でもこうすることは実際のところあんまりなくて通常は Entity に対して extension を生やすことが多いと思います. 例えば User
という Entity があったら User+UIExtension.swift
みたいにするなど.
もちろん Extension を作らない解決方法もありです。例えば HogeFormatter.swift
みたいなものを作るなど.
UI の表示に使うもの (UI の仕様を実装) & テストが必要なもの
この場合は ViewModel
や Presenter
などの View と Model をつなげるレイヤーの private extension
として実装します。
この場合も extension 自体のテストはできませんが、 ViewModel
や Presenter
のテストができるのでそこからテストします。
// HogeViewModel.swift class HogeViewModel { } private extension String { var insertSpacedString: String { return "処理" } }
ユーザーからの入力をバリデーションする場合もこの書き方をすることが多いです。
議論
「UI の表示に使うもの」の2パターンについて「なんで extension にするの? 」、「View や ViewModel のメソッドとして書いたらいいのでは?」と思う人もいるかも知れないです。
僕的にはそういう方法はなんかイケてないのでしません。extension のメソッドが View や ViewModel の property などに依存していない場合は extension として切り出しておいたほうが依存関係がスッキリしてあとからの修正に耐えられるコードになります。
class HogeViewModel { // イケてない func insertSpacedString(_ str:String) -> String { return "処理" } }
残念ながら
弊社ではこの書き方は普通なのですが、世界は広いのでこの書き方をするとレビューなどで 「class の中に書いてください」とレビューで指摘されることがあって泣く泣く治す場合もあります。
良くないと思うもの
StringExtensions.swift
みたいなグローバルっぽい名前のファイルに全部書いてある
まとめ
ほんの一部ですが extension をどこに書くのかの方針を書きました。
前提として、なんの extension にするのか?ある機能は Date の extension? String の extension ? のどれに生えているべき? という話もあるなと思ったのですが、今回はそれには触れませんでした。
コードを書く際の参考になれば幸いです。