N予備校iOSアプリへ SwiftUI を導入するまでの道のりについて

はじめに

N予備校 iOS アプリ 開発チームでは、長い間 UIKit & Storyboard & RxSwift & MVVM で開発してきました。
以前から「Storyboard やめたい!」「SwiftUI 書きたい!」「Xcode Previews を使って効率よく開発したい!」といった思いが強く、SwiftUI を導入したい気持ちがありました。
ですが、昨年まではアプリのサポートOSが iOS 13.0+ だったこともあり、SwiftUI で苦しむことを危惧していました。

2022年3月末、ついに iOS 14.0 未満のサポートを終了しました。
2022年2月頃から2022年10月現在までに、SwiftUI 導入を実現する為にどのようなことを検討・実践してきたのかをご紹介します。

導入に向けて検討したこと

流れ

  1. UIKit 及び Storyboard をどのように置き換えるのか
  2. 書き慣れた MVVM パターンを維持できるのか
  3. 既存のプロジェクト構成でもスムーズに導入できるのか
  4. アプリのサポートOSが iOS14.0+ だが、 SwiftUI 特有の苦しみは問題なさそうか
  5. 本当に開発効率は向上できるのか

1. UIKit 及び Storyboard をどのように置き換えるのか

「全てを SwiftUI で作り直す」という目標は立てませんでした。
主な理由は、画面遷移を実現する NavigationView 及び NavigationLink のバグが多く扱い辛い点です。
画面遷移に関しては既存実装を活かすことにしたので、 今後も ViewController を使います。

実装方針は以下のようにしました。

  • 既存の ViewController で実装されている View の実装は削除
    • IBOutlet で接続しているプロパティは全て削除
    • Storyboard を削除
  • UIKit の世界に SwiftUI を持ってくるため、UIHostingController を使う

2. 書き慣れた MVVM パターンを維持できるのかどうか

TCA の採用も少し検討しましたが、現在採用しているアーキテクチャとは全くの別物です。
最終的に、クックパッドさんの記事 を参考にさせていただきました。
シンプルな画面であれば、ViewController に実装されている ViewModel と Rx のコードをほぼそのまま残し、ViewModel で取得したデータのバインド先を差し替える程度で済みそうでした。

SwiftUI 以外の学習コストを極力増やさない方向で考えていたので、引き続き MVVM パターンで実装する方針としました。

採用した実装のイメージ

// SwiftUI View
struct SettingView: View {
    final class DataSource: ObservableObject, ReactiveCompatible {
        @Published var hasError: Bool
        init(...) { ... }
    }

    @ObservedObject var dataSource: DataSource

    var body: some View {
        if hasError {
            errorView
        } else {
            contentView
        }
    }
}

// ViewController
final class SettingViewController: UIViewController {
    ...
    override func viewDidLoad() {
      ...
      let hostingViewController = UIHostingViewController(rootView: SettingView(...))
      ...
      viewModel.outputs.hasError
          .drive(dataSource.rx.hasError)
          .disposed(by: disposeBag)
   }
}

3. 既存のプロジェクト構成でもスムーズに導入できるのか

アプリのサポートOS が iOS 14.0+ なので、最初から SwiftUI 2 が使用できる状態でした。
上述の 1.2. の方針によって部分的な SwiftUI 移行が実現できる為、初期導入コストもほとんど払わずに済みました。

4. アプリのサポートOSが iOS14.0+ だが、 SwiftUI 特有の苦しみは問題なさそうか

検討開始当時は、アプリのサポートOSは iOS13.0+ でした。
「あって当然」と考えている機能が用意されていなかったりしたので、逆に開発効率は下がると判断し、SwiftUI の導入を渋っていました。
しかし、 2021年9月の時点で、2022年3月下旬に iOS14 未満のサポート終了することが決定しています。
N予備校フォーラムでの告知

この事から、前向きに SwiftUI の導入の検討を始めました。
iOS13.0+ ほどではないにしても、 iOS14.0+ でも多少は苦しむだろうと覚悟していました。
(alert や actionSheet が iOS15.0+ で非推奨になったり、etc)

5. 本当に開発効率は向上できるのか

諸々調査・検討した結果、 開発効率向上は見込める と判断できました。
ピックアップした一部の判断材料は下記の通りです。

  • 複雑怪奇な Storyboard を編集する必要がなくなった
    • 制約の張り方が無秩序で手に負えない
  • Storyboard や Xib (自動生成XML)は読みにくく、コードレビューがつらい
    • SwiftUI だと人間が書くので、読みやすかったり、コードの意図を教えてくれたり
    • 自動生成XML vs 人間が書いた Swift コード
  • プレビューが非常に便利!
    • SwiftUI コードを書いている隣でプレビュー表示してくれる

⚠️ 10分経ってもプレビュー表示されない場合は諦める(潔く諦めるのも大事。実行して確認すればOK。)
⚠️ プレビュー時に謎のエラーが出る場合は Xcode を再起動してみる(諦めないのも大事。再起動するだけで最高の体験が得られるかも。)

導入決定後から本格対応までにやったこと

流れ

  1. 画面構成がシンプルで、利用頻度の低い設定画面の SwiftUI 化
  2. チーム勉強会の時間を使って一緒に学習

1. 画面構成がシンプルで、利用頻度の低い設定画面の SwiftUI 化

SwiftUI 化の第一弾は設定画面に決定しました。 画面構成がシンプルかつ要素が少ないので、取っ掛かりとしてはちょうどよかったです。
ただ、標準の List だと罫線の描画が想定通りにいかない問題がありました。
他にも幾つか懸念点があったので、 ScrollView + LazyVStack でリストを実装する方針にしました。

標準の List で困った例

List {
    Section { 
        // 罫線の色が変更できない(iOS 15.0+ ならできる)
        Text("text1")
            .listRowSeparatorTint(Color.red)
        // 罫線の表示/非表示が変更できない(iOS 15.0+ ならできる)
        Text("text2")
            .listRowSeparator(.hidden)
    }
}

2. チーム勉強会の時間を使って一緒に学習

N予備校iOSチームでは、毎週木曜日に30分枠で勉強会を実施しています。
今年5月下旬から12営業日はほぼ連続で、SwiftUI 勉強会を実施しました。(1回あたり1時間) Apple 公式の SwiftUI チュートリアルを読み合わせながら、チュートリアル通りにコードを書いて理解していきました。

ただなぞるだけでなく、気になった箇所をぱぱっと調べて情報共有もしていました。
思いの外時間が掛かりましたが、共通の不明点がその場で解消できてよかったです。

実践

移行計画

まずは、移行対象となる画面を決める条件を以下のように設定しました。

  • 比較的シンプルであること(パーツ数が少ない、ビジネスロジックがシンプル、etc)
  • 直近の案件で改修対象となっていない

次に、移行順を決める条件を以下のように設定しました。

  • 移行が原因で万が一問題が発生しても、ユーザー影響が小さい順
  • 移行対応の着手が可能な時期

検討の結果、以下の順番で移行を進めていくことにしました。

  1. 設定画面
  2. 通知画面(運営からのお知らせ一覧)
  3. フォーラムTOP画面(フォーラムに投稿された質問の一覧)
  4. 授業TOP画面(放送前・放送中・放送後の授業の一覧)
  5. フォーラム検索画面(フォーラムに投稿された質問や回答を検索)
  6. 授業検索画面(放送前・放送中・放送後の授業を検索)

※ 他の画面についても現在検討中です。
※ 新規で画面を作る場合は、最初から SwiftUI で作ります。

成果

2022年10月現在、以下の画面が SwiftUI 移行を完了しています。

  1. 設定画面
  2. 通知画面
  3. フォーラムTOP画面

現在、他の画面も鋭意開発中です!

おわりに

実践していくなかで見えてきた新たな課題があり、都度 SwiftUI と格闘することがあります。
iOS 14 系と iOS 15 系で動作や見た目に差異があったりするので、それを吸収したコンポーネントを作ったり、入念に動作確認しています。
それでも、開発・レビュー・改修のやりやすさは格段に向上し、「SwiftUI を導入して良かった!」 と感じています。

今後も、SwiftUI を活用した開発に取り組んでいきます。

We are hiring!

株式会社ドワンゴの教育事業では、一緒に未来の当たり前の教育をつくるメンバーを募集しています。

カジュアル面談も行っています。 お気軽にご連絡ください!

カジュアル面談応募フォームはこちら

www.nnn.ed.nico

開発チームの取り組み、教育事業の今後については、他の記事や採用資料をご覧ください。

speakerdeck.com