新規プロジェクト作成せずに、既存プロジェクトをUIKitからSwiftUIに移行するまでにやったこと

## 概要

既存UIKitプロジェクトをSwiftUIに移行する機会がありました。

UIKitからSwiftUIへの移行の仕方で調べると、新規でプロジェクトを作成することでの移行のやり方、つまりSwiftUIをUIフレームワークに指定してプロジェクトを新規作成するやり方しかヒットしなかったので、
既存のUIKitプロジェクトを移行することはできないのかと勝手に思い込んでました。

でも、SwiftUIで作成されたプロジェクトを見ると、別に新規でプロジェクト作成しなければいけなそうな理由はなかったので（例えば新規作成したときにしか作成されない必須ファイルがあるとか）、試してみたところ普通にできました・・・

既存のUIKitを使ったプロジェクトをSwiftUIに移行するということ自体、頻繁に発生しないかもしれないですが、今後の自分や誰かのために残しておきます。

ちなみになぜ自分はこれをやりたかったのかというと、訳あってGitの履歴を途切れさせずに、UIKitからSwiftUIへ移行したかったからです。

## 環境

- Xcode13.0

## やってみた

何か正式な手順を踏んでやったというよりかは、UIKitとSwiftUIのプロジェクトを見比べて、SwiftUI側のプロジェクトにあるファイルや設定をUIKitを使ったプロジェクトの方に移していっただけではあります。
なので、順番はあまり意識していません。

実際に移行作業をした時は試行錯誤しながらだったので、今回記事を書くに当たってもう一度やってみることにしました。

ちなみにここで書くのは、**SwiftUIのライフサイクルプロジェクトへの移行**です。

### 1. エントリポイント周りの修正

周りくどいと思いますが、まず`AppDelegate.swift`にある`@main`をコメントアウトしてみました。
`AppDelegate.swift`にある`@main`はエントリポイントを示しているため、これがないと動かないはずですよね。
やってみたところ、想定通りビルドエラーが出ました。
`Entry point (_main) undefined. for architecture x86_64`とのこと。

![スクリーンショット 2021-12-29 17.31.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401966/6185ebbc-c75c-2412-5cb0-a203e8462ed8.png)

では早速、エントリポイントを決めるため、別のSwiftUIファイルを作成します。
デフォルトだと、こんな感じの中身になるので、

```swift
import SwiftUI

struct UIFrameworkMigrationTestApp: View {
    var body: some View {
        Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
    }
}

struct UIFrameworkMigrationTestApp_Previews: PreviewProvider {
    static var previews: some View {
        UIFrameworkMigrationTestApp()
    }
}
```

それをこっち↓に差し替えます。
ライフサイクルをSwiftUIに設定して新規プロジェクト作成したのであれば、勝手に作成されるファイルですが、今回は移行元プロジェクトはUIKitで作成していたので、このファイルは自分で作成します。

```swift
import SwiftUI

@main  // アプリのエントリ
struct TestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView() // 最初に表示される View
        }
    }
}
```

あとは`ContentView()`を作成して適当に作りたいViewを実装します。

```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("SwiftUIに移行したよ")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

これでビルドをしてみたところ、ビルドエラーはなくなりました。
が、ContentViewで実装した「SwiftUIに移行したよ」は表示されておらず、最初に作ったときのstoryboardの画面が表示されています。

どうやらアプリ起動時の画面がまだstoryboardのままのようです。

### 2. Info.plistを変更する

いじったのは4つです。

やったことしては、**storyboardは使わないようになるので、それ周りの設定を削除したことと
`Enable Multiple Windows`をいじったこと**です。


- `Launch screen interface file base name`を削除

スプラッシュ画面が表示されなくなり、スプラッシュ画面以降の最初の画面が表示されます。でもまだSwiftUIで実装したViewは表示されません。storyboardの方が表示されています。

- `Main storyboard file base name`を削除
- `Storyboard Name`（Application Scene Manifest > Scene Configuration > Application Session Role > item 0 の中）を削除

ビルドしてみると、ビルドエラーは出ませんが、画面が真っ黒になります。

- `Enable Multiple Windows`を`YES`に変更

理由はわからなかったのですが、これを一度YESに書き換えてビルドすると、SwiftUIで実装したViewがちゃんと表示されるようになります。
ただ、これをまた`NO`に戻しても、問題なく表示されてはいましたので、NOに戻しました。

なぜ一旦YESにしないといけないのでしょう・・・わからないのですが、真っ黒なまま表示されなくて悩んでいたところ、[こちら](https://daisuke-t-jp.hatenablog.com/entry/2020/10/24/change-to-ios14-swiftui-lifecycle)の記事に以下の記述があったので、やってみたところ、表示されるようになりました。

> UIApplicationSupportsMultipleScenes がもともと false で、ライフサイクル変更と共に true にして上書きインストールすると、上記の現象は起きなかった。


最終的にこうなりました。↓
![スクリーンショット 2021-12-29 18.02.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401966/96a68087-fb3d-d3ec-2248-6afa6f935f89.png)

## 参考

- [既存の SwiftUI アプリのライフサイクルを iOS 14 の SwiftUI App に変更してみる](https://daisuke-t-jp.hatenablog.com/entry/2020/10/24/change-to-ios14-swiftui-lifecycle)
- [[SwiftUI] appDelegate を使ってイベント対応する方法](https://software.small-desk.com/development/2021/07/28/swiftui-howto-use-appdelegate/)

## 終わりに

意外と、移行作業でやったことは多くなかったみたいです。
あとは、storyboardで実装していたところをSwiftUIで書き直したりといったことですね。

AppDelegate.swiftやSceneDelegate.swiftを使用しないのであれば、削除してしまっても良いと思います。実際私が移行作業をした時は、特に必要なかったので削除しました。

ただし、現状ではAppDelegateがないと実装できないこともあるようなので、場合によって必要有無は変わりそうです。
参考に載せたリンクにもあるように、SwiftUIライフサイクルでプロジェクトを作成していてもAppDelegateが必要になって後から追加する例もあります。

最後になりますが、上記に記載した作業ログとして、Gitのコミットに履歴を残しました。

https://github.com/kamimi01/UIFrameworkMigrationTest/commit/1dc6803d28b701ac2ac6937d0eb093ee3b8c67cc#diff-709085659eb2d464d6ab82fd0bab4dafbb883c9f32e90667c49301af86cda12a

`Enable Multiple Windows`をいじる必要があるのは何故か分かったら、更新しようと思います。

ViewのコードがついにHuman Readableになって幸せになれそうです。

未来の私の役に立てばいいな。