swiftのクロージャを使って設定情報を書きたい
他言語では設定をクロージャを使って書いたりするので、その流れでやってみた。
(ちなみにswiftは一昨日から触り始めて、知識はほぼwikiから)
やりたいこと
いま、たくさんのページにUITextFeildをデザインに従っていっぱい埋め込むという、タンポポのせみたいな仕事をしている。
storyboardを使わずに。
どういう仕事かというと、ページ全部で100ページあるとすると、10ページずつ、大まかにUITextFieldの設定が違う。
そして、1ページの中にあるいくつかのUITextFieldの設定はさらに細かく違う。
こうなると、UITextFieldを生成するのに、さすがに毎回以下のように書くわけにはいかない。
let textField = UITextField()
textField.borderStyle = UITextBorderStyle.RoundedRect // 枠
textField.font = UIFont.systemFontOfSize(15) // フォントサイズ変える
textField.textColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0) // 色を変える
textField.placeholder = "ここに書き込んでください"
textField.textAlignment = NSTextAlignment.Center // 中央寄せする
// 次は文字が入るぴったりのサイズに変えて、次は...etc
くくり出す
普通に考えて、共有できる部分をくくり出してみる。
100ページのうち、10ページずつで固定されるものは、(ここでは例として)borderStyleとtextAlignmentとする。
buildTextField()というメソッドを作って、そのへんのクラスに放り込んでおいた(適当)。
class UIBuilder {
class func buildTextField() -> UITextField {
let textField = UITextField()
textField.borderStyle = UITextBorderStyle.RoundedRect // 枠
textField.textAlignment = NSTextAlignment.Center // 中央寄せする
return textField
}
}
呼び出す。
let textField = UIBuilder.buildTextField()
textField.font = UIFont.systemFontOfSize(15) // フォントサイズ変える
textField.textColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0) // 色を変える
textField.placeholder = "ここに書き込んでください"
これだけでも、タンポポのせがちょっと楽になる。
UITextFieldの生成と一部の設定が隠蔽されているので、知らない人が読んだ時に若干戸惑いが発生する可能性があるけど、背に腹はかえられない。
カオス
現実のタンポポのせは辛いもの。
最初の例は問題を簡単にするために、TextFieldが1個しかないことになっているけど、当然1ページの中に複数ある。
そうなると、ソース上で1つ1つのtextFieldを判別できる変数名にする必要がある。
…あるよね?
さすがに、textField1とかtextField2とかいう変数にすると、後で見た時に辛いと思ってくれるよね?
とりあえず辛いということにして、意味のある名前をつける必要がある。
「えーと、この画像の上にある、この四角の中の、このラインの上で、隣に丸がある…」みたいな名前をつける。
そう、textFieldOnHogeInPiyoUnderFugaWith...みたいな名前をつけるんだ…!
このへんでstoryboardに帰りたくなったけど、我慢した。
さて、素直に書いてみましょう。
let textFieldOnHogeInPiyoUnderFuga = UIBuilder.buildTextField()
textFieldOnHogeInPiyoUnderFuga.textColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0) // 色を変える
textFieldOnHogeInPiyoUnderFuga.placeholder = "ここに書き込んでよね"
// ほか3行ほど設定
let textFieldOnHogeraInPiyoUnderFuga = UIBuilder.buildTextField()
textFieldOnHogeraInPiyoUnderFuga.font = UIFont.systemFontOfSize(15) // フォントサイズ変える
textFieldOnHogeraInPiyoUnderFuga.placeholder = "ここに書き込むべき"
// ほか4行ほど設定
// あと5個だよ!
いや…わかる…わかるよ。
所謂クソコードってやつね。わかる。あれね。
1行が長すぎるし、タイプミス(Xcodeだから補完ミスか)してても正直わからん。
クロージャによる設定
名前はしょうがないとして、できるならその変数の使用回数を減らしたい。あと、もうちょっと明示的にまとまりを示したい。
ので、
class UIBuilder {
class func buildTextField(settingFunc: (UITextField)->Void) -> UITextField {
let textField = UITextField()
textField.borderStyle = UITextBorderStyle.RoundedRect // 枠
textField.textAlignment = NSTextAlignment.Center // 中央寄せする
settingFunc(textField)
return textField
}
}
とクロージャを受けるようにしておいて、
let textFieldOnHogeInPiyoUnderFuga = UIBuilder.buildTextField() { f in
f.textColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 1.0) // 色を変える
f.placeholder = "ここに書き込んでよね"
// ほか3行ほど設定
}
let textFieldOnHogeraInPiyoUnderFuga = UIBuilder.buildTextField() { f in
f.font = UIFont.systemFontOfSize(15) // フォントサイズ変える
f.placeholder = "ここに書き込むべき"
// ほか4行ほど設定
}
とする。
これでタンポポが綺麗にのせられるようになった。
速くなったり、すごく事故が減るとか、そういうメリットがある書き方ではないんだけど、見やすい。
見やすいのはメンテしやすい。
なお、現実のコードでは、第一引数のクロージャは省略可能にしないと空のクロージャを登録する場面がでてくる。
この例だと必要性がないので書いてない。
余談
例えばRubyでは、同様にクロージャを使って設定情報を入れるというのは良く見る。
foo = Foo.build() { |setting|
setting.hoge = true
setting.fuga = false
}
けれど、こうするより
foo = Foo.build( {
hoge: true,
fuga: false
})
と書いた方が、後者の方が自由度が少なくてハマることが多いと個人的には思う(ここまで書いといてなんだが)。
前者のようにクロージャで書くと、中に何でも書けるので、クロージャ内部で書いた方が良いことと、外で書いた方が良いことが曖昧になる。
上の例だと、意図としてはbuild()するのに必要な補足情報をクロージャで入れる、という感じで書いたのだろうけど、build()した後にゴニョゴニョするようなコードを入れてしまう事がある。
TODO: この部分をswiftで書き直す。