課題
- SwiftUIが登場して、2年ぐらい経過している。2年経つが既存のプロダクトでSwiftUIで開発をするといくつかの制約に出会す。特に機能不足、不具合、サポートバージョンなどの問題もあり、UIKitベースで進めることが多い。
しかし、長期的な視点で見るとSwiftUIが開発の中心になっていくことが予想される。今回は既存のプロダクトでUIKitとSwiftUIの共存、少しずつ置き換えるための戦略について考えてみました。
SwiftUIで実装するメリット
- 日頃の開発でInterface Builderを使っている場合は、コードだけでUIを実装できる
- 宣言的に実装できるのでメンテしやすく、コードの可読性が上がる
- プレビュー機能が使える(UIKitのクラスをWrapすれば同様の機能は使える)
実装方法
- UIHostingControllerを使う
- SwiftUIのViewをUIHostingControllerに渡して、Child ViewControllerとして、登録することで表示することができる
import UIKit
import SwiftUI
final class SampleListDisplayController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
title = "SampleListView(SwiftUI)"
let hostingController = UIHostingController(rootView: SampleListView())
addChild(hostingController)
view.addSubview(hostingController.view)
hostingController.didMove(toParent: self)
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
hostingController.view.rightAnchor.constraint(equalTo: view.rightAnchor),
hostingController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
hostingController.view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
}
}
実装 + α
- Mewを使う
- Mewを使います
- Mewを使うことでchild ViewControllerの管理が楽になり、StackViewと合わせて使うとさらにレイアウトが簡単になります。
- MicroViewControllerとすることで画面の一部のパーツに切り分けることができ、部分的に置き換えることが容易になります
- UIHostingControllerを継承して、SwiftUIのViewをrootViewに渡すことでSwiftUIのViewを表示できます。そして、MicroViewControllerとして画面に追加することで表示できます。
import Foundation
import UIKit
import SwiftUI
import Mew
final class BodyViewController: UIHostingController<BodyView>, Instantiatable, Injectable {
func input(_ input: Input) {}
typealias Input = Void
typealias Environment = Void
private var swiftUIView: BodyView
var environment: Void
init(with input: Input, environment: Environment) {
self.swiftUIView = BodyView()
super.init(rootView: swiftUIView)
view.backgroundColor = .clear
}
@available(*, unavailable)
@objc required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
- サンプルコードはこちらです
まとめ
- 今後はSwiftUIのアップデートによって、使いやすくなり、主流になっていくことが予想される。
- Mewのライブラリを使うことで少しずつの置き換えが簡単になります。
- 現状はSwiftUIだけでは実装したい機能を実現するのは大変である。UIKitと組み合わせ、1つの画面の一部を置き換えていくことが現実的なやり方である
環境
Xcode 12.5
Build version 12E262
Apple Swift version 5.4 (swiftlang-1205.0.26.9 clang-1205.0.19.55)
Target: x86_64-apple-darwin20.3.0