[発散メモ] SwiftUIではMVVM系とRedux系のどちらが良さそうか
💭

[発散メモ] SwiftUIではMVVM系とRedux系のどちらが良さそうか

[発散しながらのメモ]

ここ数日、SwiftUIについて猛烈に調査と思考をめぐらせてみていた。 SwiftUIの中でもUIデザインの実装ではなく、データ連携部分の設計(いわゆるアーキテクチャ)面にフォーカスしていた。

SwiftUIはReactやVueに非常に似ている。個人的な印象ではReactが8割 Vueが2割というイメージ(それぞれ本当に触りしか知らないのであくまで感触)

Reactのrenderがbodyに相当し、VueのbinderがBindingに相当するところからこういうイメージを持った。

まぁ、こうなってくると当然ReduxやVuexのようなUnidirectionalなdata-flowなアーキテクチャの採用を考えたくなるわけで色々考えた。

VuexインスパイアでStoreを作りReducerを定義するようなライブラリ VergeNeue の開発もしてみた。

この辺りのコミットで閲覧できるVergeNeueがその様子

最初はいい感じかなーと思っていたが、その時は全部扱うデータがValueTypeとして扱える場合のみだった。

そこで、例えばCoreDataやRealm または FirebaseのDatabase(Firestore / Realtime database) を活用している場合はどうなるのか?を考えてみた。

すると、どうにもスッと落ちていかない。

というのも、Redux系アーキテクチャは原則ValueTypeのみを扱い、かつ、パフォーマンスを保つためにデータはノーマライズを行い格納すべきで、ここに問題を感じる。

ノーマライズについて

仮にCoreData(DB)を持つ場合、

  • APIからデータを取得 (JSON) JSONはノーマライズされていない
  • CoreDataに格納 (皆、自然とノーマライズしている)
  • ノーマライズされていないように見えるNSManagedObjectが取れる
  • ReduxのStateにノーマライズを行い格納する
  • ViewはReduxのStateを使って表示していく

という感じになるのかな。という感じで、なんか周りくどさがすごいな。と

また、別の課題もあり

CoreDataのようなDB/ORMは独自の通知システムを持っており、レコードが更新されればその通知を発行する。

Firebaseも同じような仕組みがある

この変更を誰かが監視し、ReduxのStateに随時変更を適用していかなければならない。

面倒だけど、やればいい話?でもなくて、NSManagedObject → ValueTypeの変換のコストは無視できない。(永続化に使用しているストアにもよるが)

CoreDataを例にすれば、faultという仕組みがあり、これがデータをメモリにロードすることを制御している。

なので、今の僕の結論からいえば、やるべきではない と思う。

さて、どうしたものか。というところ。

DBをReduxStateのルートとして考えてしまえば? というアイデアもあったが、なんか違う。 Firebaseの時にはどうするんだ?という感じで転用が効かない気もするし。

また、データの取得管理まで含めて行ってくれるSDKを用いてアプリを開発する際にも同様の問題が起きる気がする。

例えば写真管理アプリとか? (Photos.frameworkを使う)

もしくは、NSManagedObjectのようなDAOをStateに含める? View側で実体を取り出し、NSManagedObjectを監視する。とか まぁまぁ悪くないアイデアかなとは思っていて、というのも SwiftUIがNSManagedObjectをObservableObjectとしてサポートしているから。

が、依然として、ノーマライズは避けられそうなものの、CoreDataからReactStateへの同期はまだ必要。

ではRedux以外の方法を考える。

MVVM / Flux

MVVMのVMとFluxが違うものかというと、結構絶妙

FluxのStoreを画面(View)に特化させてそれぞれに作っていけばVMみたいな感じになり、2-wayか1-wayのbindingの違いぐらい?かな。

2-way 1-wayの見分けもなかなか難しいところではある。

なので、自分の中では、画面やドメインなどで分割した領域に使うModel(Store)というイメージでReduxの考え方と差別化をする。 (そもそもReduxという言葉の選び方が合っているかは際どい。言いたいことは One state tree)

その前に、

自分の考えでは、アプリの中身をFrontendとBackendにまず真っ二つに切り分け、それをどう繋ぐのか。という考え方を大切にしている。

大規模なアプリを保守性高く開発していくにあたってメリットは大きい。

こうしておくことで、UIKitベースからSwiftUIベースのフロントにすり替えることも比較的簡単になる。

image

また、Backendレイヤーはあまり特定の技術に依存はしたくないのが本音。

とはいえ、現実問題とのバランスをみた結果RxSwiftを全面活用していたりする。

この理由から、BackendLayerからReduxのルートが生えていてほしくない。 BackendLayerは極力プレーンな形で状態を公開しているだけに留め、どう扱うかはフロントに任せる。

という思想からスタートしているのが、今回の悩みの根元だとは思う。 ここを変えれば別の悩みに切り替わると思う。

で、VM/Flux系ならどうなるのかというと、

記事は古いけど、こういう感じ

VMのStateがVMをもつ

画面ごとにVMを作っていたこれまでのスタイルと特に変わりはない。 VMのStateに入れてしまうことで、SwiftUIの描画に巻き込まれて生成されまくることを防げる。

どういうことかというと、

VM → VMのチェーンでそんなに問題はないけど、VM間のなにかの同期が必要になったら、とか

VMチェーンが画面のツリーと一致していたときに、画面のツリーが深いとVMのinitが大量になるからlazyにしないとね。みたいな話になりそうなのが気がかり。

さてー どうしたものか。

そもそも色々考えすぎなんだろうなとは思う。

追記