Help us understand the problem. What is going on with this article?

SwiftUI と UIKit 混合環境で開発を行うときの tips 集

はじめに

AkkeyTV という動画視聴アプリはこの記事執筆段階で UIKit と SwiftUI を用いて開発されています。
iOS13 未満は UIKit で構築された UI をロードし、 iOS13 以降では SwiftUI で構築された UI をロードするため、分岐処理が必要になります。また、1画面のために2パターンの UI 設計をする必要があるため、開発効率的にはよくありません。

ただ、 SwiftUI で設計する能力は今後必要になるため、勉強も兼ねて UIKit と SwiftUI の混合環境を選んでいます。
この混合環境を実現する上で学んだことをここにまとめたいと思います。

SwiftUI.framework の weak link

Carthage などでライブラリを追加した場合、 Xcode でパスの設定も行うと思います。Apple が提供する framework に関してはこれを行わなくても import して呼び出すことができます。これが Auto Link です。
これが iOS13 未満の端末でアプリを起動した際にも動いてしまうため、 SwiftUI が実装されているアプリはクラッシュしてしまいます。

解決方法ですが、明示的に SwiftUI.frameworkweak link として設定してあげることで対応可能です。
Build Phases > Link Binary With Libraries > SwiftUI.framework [Optional]

UIHostingController

SwiftUI で設計されたものを UIKit 側から触るときは UIHostingController を使用します。
SwiftUI で設計された SwiftUIView() というものを定義している場合、以下のように画面遷移を記述することができます。

guard #available(iOS 13.0.0, *) else { return }
let vc = UIHostingController(rootView: SwiftUIView())
self.present(vc, animated: true)

また、「すでに表示されてたら画面遷移処理を行わない」といった処理を行うときも UIHostingController を用いて対応可能です。

guard #available(iOS 13.0.0, *) else { return }
let vc = UIHostingController(rootView: SwiftUIView())
let frontVC = ...

if !(frontVC is UIHostingController<SwiftUIView>) {
    self.present(vc, animated: true)
}

*Representable

UIKit で設計されたものを SwiftUI 側から触るときは *Representable に準拠させてあげます。具体的には、 UIViewControllerUIViewControllerRepresentable に、 UIViewUIViewRepresentable に準拠させてあげます。
UIKit で設計された AkkeyView() というものを定義している場合、以下のようにプレビューさせることができます。

import UIKit
#if canImport(SwiftUI) && DEBUG
import SwiftUI

@available(iOS 13, *)
struct AkkeyViewPreviews: PreviewProvider {
    static var previews: some View {
        Group {
            AkkeyView()
                .previewLayout(.fixed(width: 414, height: 100))
                .previewDevice(PreviewDevice(rawValue: "iPhone XS Max"))
        }
    }

    static var platform: PreviewPlatform? = .iOS
}

@available(iOS 13, *)
extension AkkeyView: UIViewRepresentable {
    typealias UIViewType = AkkeyView

    func makeUIView(context: Context) -> AkkeyView {
        return .init()
    }

    func updateUIView(_ uiView: AkkeyView, context: Context) {
        // Make parameter change for preview
    }
}
#endif

さいごに

SwiftUI 導入を手助けする方法をいくつか考えていますので、よかったら参考にしてみてください。

AkkeyLab/AutoPreviewable
UIKit で設計された画面をプレビューするクラスを自動生成させる

AkkeyLab/XcodePreviewsTemplate
UIKit のクラスファイルを作成するときにプレビューするクラスを自動生成させる

AkkeyLab/StoryboardPreviewsBySwiftUI
実際のプロダクトに XcodePreviews を導入してみた

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした