去年のWWDC2019で発表されたSwiftUI。 待望の Swift製のUIライブラリでしたが、実際のところはUIHostingController上に新しいSwiftUIのViewをのせる方式で、過去互換性を守っていました。
そうするしかないだろうとは思ってたんですけど、中途半端さにすっきりしない気分の方も多かったと思います。
iOS14では、いよいよ、アプリのすべてのUIをSwiftUIで作ることができるようになりました。 UIViewControllerにもStoryboardにもAutoLayoutにもAppDelegateにも、window.makeKeyAndVisibleにもさよならです。
今までの古いコードを全部捨てて新しく書き直せるのかと思うと、ちょっとドキドキしますね。
(この記事の内容はXcode 12.0 beta 2 (12A6163b) で調査した結果ですが、実際に14.0が正式にリリースされる際には変更になる可能性が高いです。最新のXcodeで確認してくださいね。)
Pure Swift UI の前提条件
Pure Swift UIを使うには、target version を14.0にする必要があります。
iOS13以下のバージョンに対応する必要がある場合には使えないので、現実のプロジェクトに今すぐ適用するのは難しいかもしれません。
ただ、Pure SwiftUIにするとどう変わるのかを確認しておくと、今からアプリの将来設計を考えておく助けになるかと思います。
Pure Swift UI にするには
新規でPure Swift UI のプロジェクトを作る場合には、Xcode12を使ってください。
新規プロジェクトの設定ダイアログの中にLife Cycleという項目があります。
ここでSwiftUI Appを選ぶと Pure SwiftUIのアプリになり、UIKit App Delegateを選ぶと今までの App Delegate を使った構成になります。
アプリの起動時の処理
今までは、AppDelegate(UIApplicationDelegate)のdidFinishLaunchingWithOptionsで起動時の処理を行なっていましたね。
PureSwiftUIでは、App Protocolを継承する構造体(!)がその役目を担います。
現状では、その構造体のinit()で起動時処理をやるしかなさそうです。
試してみたところ、@Environmenで取得できる値が、init()で取得した値とSceneやViewで取得した値とでと異なっていることもありましたので、若干注意が必要です。
AppleのApp Life Cycle の公式ドキュメントには、SwiftUI Appについて記述がないので、このあたりの処理は今後変更される可能性もありそうです。 https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle
@main struct MyPureSwiftApp: App { @SceneBuilder var body: some Scene { WindowGroup { ContentView() } init(){ print(" initializing...") } }
アプリがバックグラウンドになった時の処理
バックグラウンド/フォアグラウンドの処理は、iOS13で大きく変更が入っていますよね。(以前の構成のままでも特に問題はないので、新構成に対応した人は多くはないと思いますが。)
iOS12までは、AppDelegateでの対応、iOS13からはSceneDelegateで対応することになっていました。
Pure SwiftUIではView、App、Sceneのどこでも処理できるようになります。
Viewで処理する場合はこうなります。
struct SmallView: View { @Environment(\.scenePhase) private var scenePhase var body: some View { Text("Hello World") .onChange(of: scenePhase) { phase in print("user scene changed to..\(phase)") } } }
App で処理する場合はこうなります。
@main struct MyApp: App { @Environment(\.scenePhase) private var scenePhase var body: some Scene { WindowGroup { MyRootView() } .onChange(of: scenePhase) { phase in if phase == .background { print("changed to background!") } } } }
ScenePhaseはactive、inactive、backgroundの3値をとるenumです。
openURL
アプリ間連携や、その他細々したところでよく使うopenURLですが、こちらは今のところViewでしかハンドリングできないようです。
struct SmallView: View { var body: some View { Text("Hello") .onOpenURL{ url in print(" called with url.. \(url)") } } }
アプリ起動画面
今までは、LaunchScreen Storyboardを使っていましたね。(古くはDefault.pngという固定イメージファイルを使っていましたが……。)
Pure Swift UIアプリでは、info.plistにファイル名を書くことでその画像を起動画面にすることができます。
<key>UILaunchScreen</key>
<dict>
<key>UIImageName</key>
<string>startScreen</string>
</dict>
viewDidAppear系
今までViewControllerで使っていたviewDidAppear/viewDidDisapperはViewのonAppear、 onDisapperになります。
struct SmallView: View { var body: some View { Text("Hello World") .onAppear { print("use view appeared!") } .onDisappear { print("user view disappeared!") } } }
まとめ
正直なところ、Pure SwiftUIは、公式ドキュメントの整備もまだまだなので、これからまだいろいろと変更がありそうです。
本番のコードにいれるのはまだ先かもしれませんが、プライベートプロジェクトなどで是非試してみましょう。