1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

iOSでMVVMをやってる時に、カオスになったRxSwfitのバインディング処理をスッキリしたかった【骨組み編】

Posted at
4年半ぶりに復帰!
生成AIやら、SwiftUIやら、
世界が変わってししまいましたが、
なんとかiOSエンジニアとして生き残っています。

iOS開発でMVVMをやってる時に、

  • ViewControllerやViewModel内のバインディングがぐちゃぐちゃになった
  • どのプロパティがどこにバインドされてるかわからん
  • Outputのはずなのに、他のOutputにInputとしてそのまま使われてる

など、バインド処理がぐちゃぐちゃで鬱になったので、役割ごとに切り分けることで、すっきりさせる設計を色々試しました。

で、どのように分けたか

  • Input
    • Viewからの入力
      • ボタンのタップ
      • テキストの入力
      • ライフサイクル
  • Output
    • ViewModelからViewへ通知
      • テキストの更新
      • 色の更新
      • 画像の更新
  • Navigation
    • ViewModelからControllerへ通知
      • 次の画面へ進むトリガー
      • 前の画面へ進むトリガー
      • エラーダイアログ表示トリガー

骨組み

全コードを記載すると、全体が見渡しにくいので、骨組みだけ記載します。

ViewModel


/// ViewModel
final class SampleViewModel {
    /// 入力
    struct Input {
        // 画面から受け取る入力を定義
    }
    
    /// 出力
    struct Output {
        // 画面へ出力する内容を定義
    }
    
    /// 画面遷移
    struct Navigation {
        // 画面遷移を定義
    }
    
    init() {}

    /// Inputをバインディング
    func bindViewModel(input: Input) -> (Output, Navigation) {
        // inputとModelを利用して、OutputとNavigationを紐づける
        let output = Output()
        let navigation = Navigation()
        return (output, navigation)
    }
}

ViewController

/// ViewController
final class SampleViewController<ViewModel: SampleViewModel>: UIViewController {

    /// ViewModel
    private let viewModel: ViewModel
    /// Input
    private var input = ViewModel.Input

    init(_ viewModel: ViewModel) {
        self.viewModel = viewModel
        self.input = .init()
        super.init(nibName: nil, bundle: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        bindViewModel()
    }
    
    /// ViewModelとバインド
    private func bindViewModel() {
        bindIntput(input)
        let (output, navigation) = viewModel.bind(input)
        bindOutput(output)
        bindNavigaion(navigation)
    }
    
    /// 画面の操作をバインド
    private func bindInput(_ input: ViewModel.Input) {
    }
    
    /// ViewModelの出力をバインド
    private func bindOutput(_ output: ViewModel.Output) {
    }
    
    /// 画面遷移をバインド
    private func bindNavigation(_ navigation: ViewModel.Navigation) {
    }
}

今後

  • 実際の実装レベルのサンプルを公開

    • UseCaseとRepositryも含める
  • 画面の中に画面がある、カスタムViewの入れ子状態の時、View間でやりとりするとぐちゃぐちゃになるので、Model層で効率的に連携する方法を模索

  • 生成AIでサクッと機能を追加するテンプレ呪文を考えたい

参考

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?