ポジティブ丸メガネ

3年目エンジニアです。

RxSwiftでUITextFieldにコードから値を代入してもイベントが流れない

環境

業務でiOSアプリ開発ではRxSwiftとMVVMを導入しています。開発をはじめて1ヶ月半というところです。

  • Xcode 8.3.1
  • Swift 3
  • RxSwift 3.5.0

TL;DR

  • textField.text = "hoge"してもtextFieldのストリームにイベントが流れてこない
  • コードから代入したら、textField.sendActions(for: .valueChange)してやれば流れる

何をしようとして何が起こった

後輩くんが、日付とメッセージを入力する画面をつくっていました(本当はもっとたくさん入力する)。日付毎に入力するタイプのものなので、ユーザーが選択した日付に対応するメッセージがRealmに存在していた場合は、メッセージ入力フォームに予め値を入れておいてあげる必要がありました。また、日付とメッセージが入力されていた場合、保存ボタンを有効にするようにしていました。

組んでいたストリームの流れは、

  1. VC:dateField → VM:date
  2. VM:dateの値でRealmのデータを検索して、
    • 存在すればVM:messageに代入
    • しなければなにもしない
  3. VM:messageTemp → VC:messageField
  4. VM:date & message が入力されていればVM:isSaveButtonEnabledをtrueに
  5. VM:isSaveButtonEnabled → VC:saveButton.isEnabled

というシンプルなものでした。レビューした感じだと、日付に対応するデータが存在しない場合はうまく動いていましたし、存在する場合でもmessageFieldにRealmから取ってきたデータがバインドされていました。しかし、存在する場合にsaveButtonが有効になっていませんでした。

messageFieldに値は入力されているのに、ボタンは有効になっていない。messageFieldをタップすれば有効になる。そんな状態でした。

原因と解決策

ひとまずあらゆるストリームに.debug()をつけて原因を調べてみました。すると、上記の2.までは流れているが3.にイベントが流れてきていませんでした。

何が原因なのかググってみると、下記issueを発見。1年以上前のissue。。。

github.com

さてさて、読んでみるとこんなことが書いてありました。

this does not work because it’s not a control event. And as far as I remember its not possible to observe text property via KVO. The only “hack” that you can make is to tell manually to UIControl that value has changed via sendActionsForControlEvents then it will notify your observers

なるほど、コードから代入するのはControl Eventじゃないからうまく動いていないようです。よってそのEventを自分でハックすればできるよって感じですね。

なので早速3.をbind(to:)ではなくsubscribe()で下記のように書くようにしました。

vm.message.subscribe(onNext: { [weak self] message in 
    self?.messageField.text = message
    self?.messageField.sendActions(for: .valueChanged)
})....

すると想定したように保存ボタンが有効になるようになりました。

この状況を簡単に再現したものを作ってみたのでよかったら見てみてください。TwoWayBinding*.swiftです。

github.com

しかし、先程のissueを読み進めていくとこんなことが書いてありました。

Well, I thing that values in the UITextField and UITextView are always reflection of some values of Model or ViewModel layer. So I don’t understand why programmatic changes of text property should “send” events.

いやー本当にそのとおりですね。ちゃんと設計すればこの問題は起こらなかったですし、それに気づけなかった僕はまだまだダメです。

おわり

新規iOSアプリ開発にモブプログラミングを取り入れてみた。

経緯

新規iOSアプリの開発を下記4人のメンバーでやることになりました。

  • 5年目エンジニア:K先輩
    • iOSアプリ開発経験無し。Androidは多少あり。
    • バックエンドの経験豊富。ここ1年はWebフロントにどっぷり浸かる。C#JavaScriptラブ。
  • 2年目エンジニア:私
    • プライベートでiOSアプリの開発経験あり(読んだ。NewsGAINなど)。
    • バックエンドの開発もぼちぼちやりつつ、Webフロントもぼちぼちやっている感じ。JSラブ。
  • 1年目エンジニア:Iくん
    • Obj-Cでの開発経験あり。Androidも多少あり。
    • Web大好きマン。JSラブ。
  • 1年目エンジニア:Mくん
    • アプリ開発やWebフロントの経験がほとんどなし。
    • バックエンド開発経験が豊富。Rubyラブ。

まとめると、SwiftでのiOSアプリ開発経験があるのが私だけというチームで、進めていくことになりました。

本日が開発2週目ですが、1週目は下記のようなことをしてました。

  • 私が、アプリのアーキテクチャやコーディング方法を調査し、K先輩とぼちぼち相談しながら検討。
    • 結局、全体のバランスと、技術的チャレンジ具合を鑑みて、 RxSwift × MVVM に決定。
  • RxSwiftとMVVMの感じを掴むために、私がアプリの一部機能を開発。
  • 1年生達は、業務での開発を経験してもらうために、必要なAPIの一部を開発。
    • その後は私が書いたRxやMVVMのSwiftコードを読んでコピペしてちょっと変えてみたりしてた

今週からは、本格的に4人で開発を進めていこうとしていました。しかし経験がないメンバーが3名もいるため、なかなかうまくは行かないかもしれないなあ〜とぼんやりと考えていました。

そんな折、通勤中に日課であるNewsGAINで情報収集をしていたところ、下記記事を発見しました。Microsoftの牛尾さんの記事です。 simplearchitect.hatenablog.com

この記事の中に、Mob Programmingという進め方が書かれていました。

チーム全員が同時に、同じ場所で同じコンピュータを使って同じことをする。1台のコンピュータとプロジェクタ2台を用意して、全員で、1台のコンピュータをシェアして、メンバーがドライバーと呼ばれるキーボードを入力する役とそれ以外の人がどういうコードを書くかを議論するスタイル。<

これを読んだ私は、 めっちゃやりたい! とテンションがあがり、チームに訴えかけ、今日いきなりやることにしました。その際に、次のようなメリットを期待しました。

  • RxSwift × MVVMの雰囲気や書き方を共有できる
  • Xcodeでハマる諸々やTipsなどを共有できる

準備

なんだかんだ下記を準備するのが大変でした。

  • モブプログラミングができる部屋
  • プロジェクタを投影する場所と座る位置のバランス
  • 投影時の解像度や文字の大きさの統一

特に、1つ目がたいへんで、なかなか長時間抑えられる部屋がありませんでした。なので、今回は別途開発研修を行っている大きめの部屋の後方部分を借りて実施することにしました。

進め方のルール

下記ルールを決めて進めることにしました。

  • ドライバー役の人のPCを使って、ドライバーのみがコードを書く
  • ポモドーロテクニック(25分やって、5分休憩する)を利用して、25分交代にする
  • 発言が否定のみにならないようにする

開発したもの

「日付や数値を入力して、その入力された内容を保存し、画面遷移する」という簡単なものです。

実際にやってみて…

よかったことはこんな感じです。

  • Rxやアーキテクチャの思想が共有できた
    • 1年生がコードを書いているときに、RxやMVVMの考え方を、いまやりたいことベースで詳しく説明できた
    • なおかつ、コードを書くのは1年生だったので、次回書くときも書きやすくなった(はず)
  • Xcodeでハマる部分の解決方法をすぐに共有できた
  • 全く書いたことがない人でもコードが書けた
  • とっても楽しかった

ポモドーロテクニックを使いつつ、25分を目安にしたことがなんとなく良かった気がします。ただ、25分で切るとキリが悪い場合が多かったので、その際はキリの良いところまでやって、Pushして交代するようにしました。

逆に、改善したいことは下記でした。

  • いま指摘している部分が、コードのどの部分なのかが伝わりにくい
    • この行の前の部分が…とか、その括弧のあとで改行して…とかが伝わりにくい
    • これが原因でかなりイライラしてしまった(個人的反省)
    • 32型とかのテレビに投影して、その前に集まってやるほうがよさそうな気がします
      • プロジェクタだと影が入ってしまうので、投影画面に近づけない
  • 一旦簡単に作ってしまえばよさそうな部分も議論してしまい、しかもそれが発散してしまう
    • ドライバー役の人が結局コードを書くので、ドライバー役の人がファシリテーションもしてしまえば解決しそう
  • 議論が不要な、作業的に書かないといけない部分で、ドライバ以外の人たちが何すればいいかわからない
    • この先に必要そうな議論をしたりしてましたが、結局ドライバー役の人はその議論に参加できないのでよくなさそう
    • 先に必要になりそうなことを調査するだけとか、テスト書いとくとかはありかも?

実際にやってみて、このスタイルはプロジェクトの立ち上げ時や、未経験メンバーがいる/入ってきた場合によさそうです。チームの最低レベルを大きく上げることができそうです。また、立ち上げ時によくある、環境設定でドハマリする等も、みんなでやれば何とかなる感があるのでとってもよさそうです。

まとめ

今後、うちのチームでは、毎週月曜日に3時間ほどやってみようという話になりました。毎日やっているとしんどそうですが、週に1回やっておけば、うまく知識レベルを併せつつ進めることができそうな予感がしています。

おわり。