Swift Advent Calendar 2015 で勉強してみる(1日目)
Swift プログラミング
Swift Advent Calendar 2015 が暖かい感じに盛り上がっていて好きだったのと、話題自体も広かったり深かったりで、眺めていたらこれをきっかけにして Swift を勉強したくなってきました。
そこで、開催されている4つの Swift Advent Calendar の内容を拝見して、そこからいろいろ浮かんだことを綴ってみようと思います。
- Swift Advent Calendar 2015
- Swift その2 Advent Calendar 2015
- Swift その3 Advent Calendar 2015
- Swift(一人) Advent Calendar 2015
Swift Advent Calendar 2015
Swift Advent Calendar 2015 の 1 日目は mono0926さん の Embedded Framework使いこなし術 というお話でした。
Swift では モジュール (Framework) が意味を意味を持つので、それを活用する上で Embedded Framework はとても大事な機能ですよね。
利点
具体的には、モジュールを使うことで次のような特徴が得られるのかなと思います。
- モジュール名が名前空間として機能し、型名などのシンボルに適切な名前を付けやすくなる。
- internal 指定のシンボルはモジュール内から自由にアクセスできるので、モジュール内で自在に機能を組み立てやすくなる。
そんな Embedded Framework でしたけど、たしかに何か工夫を迫られた時にネットで調べてみてもなかなか目的の情報にたどり着けない印象だったので、今回の記事の記事の趣旨はとってもいいなと感じました。
上記で記した自分が感じる利点の他にも Advent Calendar の記事内 ではもっと実践的な利点がいくつも記されていて、とても勉強になりました。
ビルド時間短縮
そして Advent Calendar の記事内 で記されていたビルド時間短縮の話も興味が湧きました。
自分は規模の大きい開発に関わらないせいか、これまでを通して Swift のビルド速度に不満を感じたことは全くないのですけど、人によっては相当深刻な話みたいですよね。実際 Swift 1 の頃にですけど、検証としてやってみた時にはちょっと長いタプルを使うだけで、それまで数秒で終わっていたビルドが30分以上に跳ね上がった経験があるので、当事者としては深刻なことは確実そうです。
改善は図られている様子
今の Swift はビルド周りの速度改善が図られていて、ソースコードを変更した時に、リビルドが必要になる部分をできるだけ少なくできるようにと改良が進められている印象です。
その最適化を意味するオプションのひとつがきっと、記事内で紹介されていた Whole module optimization
なのでしょう。今は Optimization Lebel
の一環として設定できるようなので、基本的には特に意識しなくても良い感じに働いていてくれそうです。
名前空間
個人的にはこれがモジュールの最大の利点かなと思ってます。もっとも、モジュールに限らないで言語仕様にあっても使い勝手が良かったようにも思いますけど。
言語仕様で名前空間を実現する方法としては 構造体やクラスにさらに入れ子にして定義する という方法もありますけど、自分は『名前空間』と聞くともっと広い、ざっくりとした空間を思い描いて、なんとなく違和感を覚えます。もっとこう、空虚なところに名前をつけたい感じです。
Objective-C の名前空間
さて、この記事を拝見していて思ったのですけど、記載されている通り、モジュールの名前空間は Swift では効果がありますけど、それを Objective-C にブリッジしたときには無視されてしまい、名前が衝突するかもしれない危険性が出てきます。
Objective-C では名前の衝突をできるだけ避けるために、クラス名の先頭2文字や3文字に自分で決めたプレフィックス文字列をつけて、例えば ESString
とか ESNotification
みたいに名前をつけるのが、名前空間の代わりだったと認識してます。
これが Swift では、名前空間のおかげで String
とか Notification
といった名前を普通に使って、それを必要であれば名前空間で区別して ESModule.String
とか ESModule.Notification
みたいに区別できたのですが、そんな名前の Swift クラスを Objective-C にブリッジすると、Objective-C では名前空間なしの String
とか Notification
といった名前でアクセスしないといけなくなり、他のモジュールと衝突する可能性が出てきてしまいます。
@objc
そんな時に役に立つはずなのが @objc
です。
このキーワードは Swift で定義したシンボルを Objective-C でも使えるように指示するものですけど、これに引数を指定すると Objective-C クラス名を指定できます。
@objc(ESNotification) protocol Notification {
}
このようにすることで、上記のプロトコルなら Swift では Notification
という名前で扱えるのは変わりませんけど、これを Objective-C に Bridge して使う時には ESNotification
という名前で使うことができるはずです。
先ほどから「はず」という言葉を使っているのは、自分の勘違いなのかもわかりませんけど Swift 1 が出て間もなくの頃はそういう風に動いてくれていた気がしたのが、ある時から効かなくなって単なる『内部名』だけが変更される様子に見えたのが、つい先日に自作モジュールを作った時に ダメ元で使ってみたら ちゃんとその名前で Objective-C では利用できるようになったというのがあって、いまいち自信が持てないためです。
多分きっと大丈夫なはず…?
本来の目的と違うとか、ダメだったらまた対策を考えないといけないですけど、これで問題なさそうならば Objective-C に限っては従前通りのプレフィックスによる名前空間 を実現できるので、良い感じに名前が付けられそうです。モジュールによる名前空間のお話とは何も関係ない話ですけれど。
強制されるということ
今回の記事を拝見していて印象に残ったのは、話の中で何度も 強制されるから便利に使ってる
みたいな主張が現れるところでした。
余談になるのですけど、自由が尊重される昨今、強制されることがこんなに見通しを良くすることなんて Swift を学んで自分もつくづく実感しました。
Swift って、どんな型でも扱える id
みたいな型よりも、扱えるものが制限される Int
の方が、ずっと扱いやすいんですよね。自由に nil
をいれられる NSString*
みたいな型よりも、絶対に nil
を持てない String
型と nil
になるかもしれない String?
型とを区別する方が扱いやすい。
そんなところに気がついた時、Swift の面白さが引き立ち初めて感動したのを覚えています。
制限すると広がる世界
今回のモジュールの話もきっと同じで、世界からひとつ名前空間で切り出された別の世界から、その小さな檻の中で物事を組み立てていくことで、見通しが効いて考える範囲が狭まる分、全体を把握する負担も減るし、コードを書く負担も減るように感じます。
自分の印象的には、ちょうど ひとつの関数内のコードはできるだけ短い方がいい というののプロジェクト版みたいな感じですね。
そしてこれはプロトコル拡張についても言えて、クラスや構造体だけだとそこに必要な機能がすべて詰められてしまい、全体を把握したり必要な機能を見つけたりするのが負担になってきますけど、プロトコルとプロトコル拡張を使うと 必要な機能がどれで、それを使ってどう組み立てる だけに集中できて、見通しの良いコードになってくれる気がします。
CocoaPods ライブラリインストール
それと Advent Calendar の記事内 に出てきた CocoaPods ライブラリインストールのところが少しだけ気になりました。
この辺りにまったく疎くてわからないんですけど、自分はメインターゲットへの pod 指定はせずに、必要な各ターゲットだけで pod を指定することで上手く使えている気がします。
use_frameworks!
def pods
pod 'Swim', '~> 1.4.1-beta2'
pod 'ESThread','~> 0.1.1'
end
target :ESNotification_OSX do
platform :osx, '10.9'
pods
end
target :ESNotification_iOS do
platform :ios, '8.0'
pods
end
逆にメインターゲットに pod を指定してしまうと、例えば OS X と iOS のターゲットが混在している時に上手く行かなくなることがあってそうしたような気がします。
どうするのが、最も素敵なやり方になるのでしょうね。
Swift その2 Advent Calendar 2015
Swift その2 Advent Calendar 2015 の 1 日目は susieyyさん の SwiftでReduxをさわってみる というお話でした。
Redux … ?
そもそも Redux とは何かを自分は知らないのですけど、記事内の引用によると Redux は JavaScript アプリケーションのための予測可能な状態コンテナ ということになるようです。
抽象的でイマイチ全容をつかめないのですけど、とりあえず自分の認識としては、Swift で状態といえば var
で、それは時事刻々と変わるものなので、それをいかに制御するかが Swift にとっては関心どころ。それを極力排除するために let
が活用されているけれど、どうしても状態に頼る必要が出てくる場面がある訳で…。
それを制御するために、いつ変化するかもわからない予測不能な状態を、予測可能にするためのもの?みたいな仮説を立てて、読ませて頂くことにしました。
Redux 3原則
そして早速 Redux 3 原則 が掲げられ、早くも全貌がおぼろげに掴めるようになっていました。
詳しくは実際の記事を拝見してもらうとして、興味深いのが State is read-only というところでした。状態は読出し専用として、変更はアクションを以って行う、という感じになるみたい。
つまり、状態を public var
にはせず private set
にして、それを更新する窓口をメソッド等で設置して、予期しないタイミングや操作での更新を防ぐみたいな感じでしょうか。所定のリクエストを投げて状態を更新するみたいな捉え方でも、もしかすると良いのかもしれません。
Reducer についてはこの段階ではまだよくわかりませんが、なんとなく役目は想像できる気がします。
データフローの図
そして、ここまで読んで データフローの図 が出てきて、なんとなく全容をイメージできてきました。
きっと今までのイメージ通りで、状態を包括的に管理する読み取り専用の仕組みがあって、それに所定のメッセージを送信することで状態更新を行うということになるように見えます。
きっと大事なのは Redux はそれを構築する抽象化レイヤーみたいな感じで、そのため、シングルステートとして纏めあげたり、Reducer という状態更新のための仕組みが必要になる、のかもしれません。
reduxSwift
こういった機構を実現するためのライブラリとして Aleksion/reduxSwift というのがあるみたいです。
記事内でそれについての環境構築とコードの書き方が記載されていて、それまでの説明とコードを読むとなんとなくですけど世界観が見えてきました。
挙げられていた簡単なサンプルだと、準備と扱う手間が目立つようにも感じられましたけど、大きな状態を複合的に管理するにはもしかすると助かる機構なのかもしれません。
reduxSwift に対する印象
気になった点は Action の書式が汎用的で意味を捉えにくそうなのと、Reducer は組んでしまえば問題なさそうですけど、状態を取得する手続きが若干煩雑そうで、JavaScript で期待できていた利点が Swift でどれくらい得られるかどうか気になりました。
reduxSwift の仕組みも趣旨も詳しくは把握できてないのですけど、もしかすると Action は値付き列挙型で最低限の情報を渡して Reducer はそれを switch で処理する感じにしてみても面白いかもしれません。
NSNotificationCenter … ?
そして、記事内にあった Kohei Asai さんのスライド Introduction to Redux を拝見していたら もしかして NSNotificationCenter で行けるんじゃないか? みたいな事を思ってみたり。
Action が NSNotification で、State は Observer で、Reducer は Observer で addObserver したセレクタと置き換えてみても、スライドの内容がなんか普通に読めそうな気がしました。
コードの試作と所感
そうした時に、先日作った EZ-NET/ESNotification を使って実際に照らし合わせてみると、もしかして記事内のソースコードを ESNotification を使って書き換えてみても意味が通るものかどうか。
そう思って始めてみたとき、reduxSwift にはもうひとつ、Store の変更が Subscriber へ通知される機能があることに今、気が付きました。これこそ NSNotificationCenter にそっくりですね。それを踏まえてコードを書いてみることにしました。
import ESNotification
struct IncrementAction : Notification {
var step = 1
}
struct CountStateChangedNotification : Notification {
var count: Int
}
struct AppStore {
var appState = AppState(count: 0)
struct AppState : NotificationObservable {
private(set) var notificationHandlers = NotificationHandlers()
let count: Int {
didSet {
CountStateChangedNotification(self.count).post()
}
}
init(count: Int) {
self.count = count
self.observeNotification(IncrementAction.self) { [unowned self] notification in
self.count += notification.step
}
}
}
}
let store = AppStore()
class ViewController : UIViewController, NotificationObservable {
private(set) var notificationHandlers = NotificationHandlers()
@IBOutlet weak var countLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.observeNotification(CountStateChangedNotification.self) { [unowned self] notification in
print(notification)
self.countLabel.text = "\(notification.count)"
}
}
@IBAction func incrementAction(sender: AnyObject) {
IncrementAction().post()
}
}
確かに、これだとちょっと何をやっているかがコードから読み取りづらいかもしれませんね。型のキャストが要らなくなったのは良いと思うんですけど、先ほどの reduxSwift みたいに『状態』に着目して組まれてないので、比べてコードが読みにくい印象がします。
あと、今は自分の把握している限りの reduxSwift の真似でしたけど、もっと広い概念が見えたら、実装がかなり複雑化しそうな予感もします。
通知センターを使った場合の全体の印象として、特に Store へメッセージを送信するところまで通知センターを使うには、少しばかり相互の距離が遠いような気もしてきます。ここは Store のメソッドに働きかける感じくらいでちょうど良いような気がしました。
状態から Subscriber へ通知するのもなんとなく距離感を感じますけど、ここはきっと Redux のコンセプト的なところじゃないかと想像するので、この距離感で適切なのかもしれません。
感想
全体を通して、知らなかった Redux というアプローチの雰囲気が流れ良く分かる記事で面白かったです。
Read-only State という着目の仕方が面白いなと感じました。あとはコードが、現状の JavaScript を見据えたもの?だと Swift では少し複雑な印象がするので、型安全と通知センターの考えをベースに、状態制御に着目した汎用的な枠組みを考えてみるのも、面白かったりするかもしれませんね。
Swift(一人) Advent Calendar 2015
Swift(一人) Advent Calendar 2015 は全日を shimesabaさん が埋め尽くすという素晴らしさですけれど、第1日目は mapとflatMapという便利メソッドを理解する というお話でした。
map と flatMap
Swift の map
と flatMap
といえば、よくお世話になる便利機能ですけれど、確かに感覚を掴むまでは自分もイマイチ上手く操れないところでした。
特に flatMap の説明が充実していて、その特徴とも言える2重の配列をどう捌いてくれるかとか、オプショナルに適用した時の動きとか、その両方を扱う場合とか、そんなところが詳しい理由や考察つきで説明されてて、具体的なところを捉えやすくて良かったです。
それの具体的な動きと、実際に使ってみるとこうなるというのが、整理して説明されていて、復習するにも良い感じでした。
こういう基本のところの動きって、捉えておくと後がずっと楽になるので、map
や flatMap
の勝手を上手く掴めていない人はこの Advent Calendar をじっくり読んで Playground で遊んでみると、きっと良いことありそうです。
そして自在に操れるようになると nil
チェックを flatMap
でサクッと解消したり、map
をいくつも連ねて一気に計算したり、いろいろと楽しいことができたりするみたいです。