はじめに (対象読者・この記事でわかること)

この記事は、SwiftでiOSアプリ開発を行っている方、特にSDK開発に携わっている方を対象としています。この記事を読むことで、SwiftでSDK開発を行う際に、呼び出し側アプリケーションから引数を受け取らずにViewを表示させるための画面制御の実装方法、具体的にはプロトコルとクロージャを活用した柔軟な画面遷移の実装方法がわかります。SDK開発における共通UIコンポーネントの提供方法として、実践的な知識を習得できます。

前提知識

この記事を読み進める上で、以下の知識があるとスムーズです。 - Swiftの基本的な文法 - iOSアプリ開発の基本的な知識 - StoryboardやSwiftUIでのUI作成の経験

Swift SDK開発における画面制御の必要性

SwiftでSDKを開発する際、呼び出し側アプリケーションから引数を受け取らずにViewを表示させたいという要件はよくあります。これは、SDK内部で状態管理を行い、呼び出し側の実装をシンプルに保つためです。特に、ユーザー認証や設定画面など、SDKが提供する共通のUIコンポーネントを表示する場合に有効なアプローチです。本記事では、プロトコルとクロージャを活用して、このような要件を実現する具体的な方法を解説します。

引数なしでViewを表示させる実装方法

ステップ1:プロトコルの定義

まずは、画面表示に関する処理を定義するプロトコルを作成します。このプロトコルは、SDKの利用者が実装する必要があります。

Swift
import UIKit protocol ScreenPresenter: AnyObject { func presentViewController(_ viewController: UIViewController, animated: Bool, completion: (() -> Void)?) }

ステップ2:画面表示用のクラスの作成

次に、Viewを表示するためのクラスを作成します。このクラスは、プロトコルに準拠したインスタンスを受け取り、画面表示の処理を委譲します。

Swift
class ViewControllerPresenter { private weak var presenter: ScreenPresenter? init(presenter: ScreenPresenter) { self.presenter = presenter } func showViewController() { // 引数なしでViewを表示 let viewController = UIViewController() viewController.view.backgroundColor = .white viewController.title = "SDK提供画面" // presenterを介してViewを表示 presenter?.presentViewController(viewController, animated: true, completion: nil) } }

ステップ3:クロージャを使った柔軟な画面制御

さらに柔軟な画面制御のために、クロージャを活用します。これにより、呼び出し側で画面表示時の処理をカスタマイズできます。

Swift
class FlexibleViewControllerPresenter { private weak var presenter: ScreenPresenter? private var configureViewController: ((UIViewController) -> Void)? init(presenter: ScreenPresenter) { self.presenter = presenter } func setConfigurationHandler(_ handler: @escaping (UIViewController) -> Void) { self.configureViewController = handler } func showViewController() { let viewController = UIViewController() viewController.view.backgroundColor = .white // クロージャを使ってViewをカスタマイズ configureViewController?(viewController) // presenterを介してViewを表示 presenter?.presentViewController(viewController, animated: true, completion: nil) } }

ステップ4:利用側での実装

SDKを利用する側では、以下のように実装します。

Swift
class ViewController: UIViewController, ScreenPresenter { func viewDidLoad() { super.viewDidLoad() // 画面表示用のインスタンスを作成 let presenter = ViewControllerPresenter(presenter: self) // ボタンを作成 let button = UIButton(type: .system) button.setTitle("SDK画面を表示", for: .normal) button.addTarget(self, action: #selector(showSdkView), for: .touchUpInside) // ボタンをViewに追加 view.addSubview(button) button.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ button.centerXAnchor.constraint(equalTo: view.centerXAnchor), button.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) } @objc func showSdkView() { let presenter = ViewControllerPresenter(presenter: self) presenter.showViewController() } // ScreenPresenterプロトコルの実装 func presentViewController(_ viewController: UIViewController, animated: Bool, completion: (() -> Void)?) { present(viewController, animated: animated, completion: completion) } }

ステップ5:SwiftUIでの実装

SwiftUIを使用している場合の実装例です。

Swift
import SwiftUI struct SwiftUIViewRepresentable: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UIViewController { return UIViewController() } func updateUIViewController(_ uiViewController: UIViewController, context: Context) { uiViewController.view.backgroundColor = .white } } struct ContentView: View { @State private var showSdkView = false var body: some View { VStack { Button("SDK画面を表示") { showSdkView = true } .sheet(isPresented: $showSdkView) { SwiftUIViewRepresentable() } } } }

ハマった点やエラー解決

問題点1:循環参照によるメモリリーク

ViewControllerPresenterがpresenterを保持しているため、循環参照が発生する可能性があります。

解決策

weakキーワードを使用して、参照を弱参照にします。

Swift
class ViewControllerPresenter { private weak var presenter: ScreenPresenter? // weakキーワードを追加 // ... }

問題点2:画面表示時のアニメーションが正しく動作しない

一部のiOSバージョンで、アニメーションが正しく動作しない問題が発生しました。

解決策

アニメーションを明示的に指定し、必要に応じて遷移方法を変更します。

Swift
func showViewController() { let viewController = UIViewController() // ... // アニメーションを明示的に指定 let transition = CATransition() transition.duration = 0.3 transition.type = .fade viewController.view.layer.add(transition, forKey: kCATransition) presenter?.presentViewController(viewController, animated: false, completion: nil) }

まとめ

本記事では、Swift SDK開発において、呼び出し元から引数を受け取らずにViewを表示させるための画面制御の実装方法を解説しました。プロトコルとクロージャを活用することで、柔軟かつ再利用性の高い画面表示処理を実装することができます。このアプローチにより、SDKの利用側での実装を簡潔に保ちつつ、SDK内部での状態管理を容易に行うことができます。今後は、より複雑な画面遷移や状態管理の方法についても記事にする予定です。

参考資料