KINTO Tech Blog
iOS

【iOS】UIKit+CombineからSwiftUIを使った独自アーキテクチャへ

Cover Image for 【iOS】UIKit+CombineからSwiftUIを使った独自アーキテクチャへ

この記事は KINTOテクノロジーズアドベントカレンダー2024 の8日目の記事です🎅🎄

はじめに

こんにちは。モバイルアプリ開発グループの中本です。
普段は、大阪にいながら東京のメンバーと協力して、KINTO UnlimitedアプリのiOS開発をしています。

本記事では、KINTO Unlimitedアプリ(iOS)のアーキテクチャ改善の過程について詳しく説明します。
このアプリのアーキテクチャは、1st → 2nd → 3rdと段階的に進化し、最終的には独自のアーキテクチャへと移行しました。
それぞれの段階における設計や課題について、以下でお話しします。

1st Generation Architecture

  • VIPERアーキテクチャを採用
    • すべての画面をUIKit + xib/storyboardで実装
    • Combineを使用してViewを更新
    • ファーストリリースに向けて納期が短かったこともあり、社内で実績のあったアーキテクチャを採用

1stの設計

  • ViewController

    • ViewModelにイベントを通知
    • ViewModelからのイベントに基づいてアウトプットを購読
    • 購読結果に応じてViewを更新し、Routerを呼び出して画面遷移を実施
  • ViewModel

    • Combineを使用してリアクティブに状態を変化
      • イベントPublisherを変換し、Viewの状態をPublisher経由でアウトプット
  • Interactor

    • APIや内部DBにリクエストを実施
  • Router

    • 他の画面への遷移処理を実施
  • UIView

    • コード/xib/storyboardを使用してレイアウト

1stの課題

  • UIKitを使用したレイアウトは開発コストが高く、特にxib/storyboardを使用した場合は変更が容易ではない
    → SwiftUIへ移行したい!

2nd Generation Architecture

  • UIKitからSwiftUIへ移行
    • UIKitによるレイアウトをSwiftUIに置き換え、開発効率を改善
      • UIHostingControllerを使用してSwiftUIのViewをViewControllerに注入
    • 画面遷移は従来通りUIKitで実施
      • 当時、SwiftUIの画面遷移APIは不安定だったためUIKitのまま
    • SwiftUIへの移行に専念する
      • 一度にたくさん変更すると、機能仕様のデグレが懸念されるため

2ndの設計

  • ViewController

    • HostingControllerInjectableプロトコルを実装し、SwiftUI Viewを追加
    • ViewModelのアウトプットを購読し、ScreenModel(ObservableObject)に反映
    • ViewModelのアウトプットやScreenModelのPublisherを購読し、Routerを用いて画面遷移を実施
  • ScreenModel

    • Viewの状態を保持するObservableObject
  • ViewModel / Interactor / Router

    • 1st Generationと同様の機能

2ndの課題

  • 状態管理がViewModelとScreenModelの両方で行われるため、ロジックが分散し、開発・保守コストが増加

  • 1stからの課題

    • Combineによるリアクティブな状態変化の実装は保守性に懸念があり、コード量が多く可読性に難がある
    • 1画面につき1つのViewModelであるため、機能の多い画面ではViewModelが巨大化

→ CombineやViewModelから脱却したい!

3rd Generation Architecture

  • Combineを用いたViewModelから状態を集中管理するViewStoreを中心としたアーキテクチャへ移行
    • イベントの結果をAnyPublisherを経ずに直接ObservableObjectに反映できる仕組みを実現
    • Combineを使用せず、async/awaitを用いてリアクティブな状態変更を実現
    • 状態管理ロジックを機能ごとに分割可能

3rdの設計

  • ViewStore

    • State

      • Viewの状態を保持するObservableObjectで、SwiftUIのViewで使用
    • Action

      • 従来のViewModelのtransformメソッドにおけるINPUTに相当する機能を提供するenum
    • ActionHandler

      • Actionを引数にとり、Stateを更新するハンドラー
      • async/awaitを使用して実装
  • ViewController

    • routerSubjectを購読し、Routerを用いて画面遷移を実施
  • Interactor / Router

    • 2nd Generationと同様

ActionHandlerの分割

機能の多い画面では、ActionHandlerとStateを分割することで、コードの可読性や保守性を向上させることができる

  • StateのactionPublisherを他のStateにバインドすることで、あるViewから他のViewにアクションを送ることが可能

おわりに

今回の取り組みは、機能開発と並行して1年以上かけて進めてきました。
現在では、ほぼすべてのソースコードが3rd Generation Architectureに置き換わっています。
その結果、コードの可読性や保守性が向上し、今後の開発が非常にやりやすくなったと感じています。
引き続き、さらなる改善を重ねていければと思います!

Facebook

関連記事 | Related Posts

We are hiring!

【iOSエンジニア】モバイルアプリ開発G/大阪

モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。

【iOSエンジニア】モバイルアプリ開発G/東京

モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。