MyAppクラス:WindowGroup
によるメインビューの定義
ここからはSwiftUIによるビューのコードを説明していきます。SwiftUIは、Appleが提供するモダンなUIフレームワークであり、Swift言語でiOS、macOS、watchOS、tvOSアプリケーションを構築するための手段です。SwiftUIでは、状態駆動型のUIを構築し、シンプルで直感的な構文を使用してアプリケーションのビューを定義します。
このコードは、SwiftUIのアプリケーションを定義しています。MyApp
は App
プロトコルを準拠しており、アプリケーションのエントリーポイントとなります。
import SwiftUI
import Swinject
@main
struct MyApp: App {
@AppStorage("isDarkMode") var isDarkMode: Bool = true
// inject into SwiftUI life-cycle via adaptor !!!
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
MainNaviRouter.createModule()
.preferredColorScheme(isDarkMode ? .dark:.light)
}
.onChange(of: scenePhase) { phase in
switch phase {
case .active:
// App is active
break
case .inactive:
// App is inactive
break
case .background:
if let backgroundTaskService = Container.shared.resolve(BackgroundTaskServiceProtocol.self) {
backgroundTaskService.scheduleBackgroundTask()
backgroundTaskService.scheduleBackgroundRenewTask()
}
@unknown default:
break
}
}
}
}
var body: some Scene
プロパティは、アプリケーションのシーン(Scene)を定義しています。SwiftUIでは、シーンはアプリケーション内での特定の状態を表します。一般的に、アプリケーションのメインウィンドウや画面が1つのシーンに対応します。
この body
プロパティ内では、WindowGroup
が定義されています。WindowGroup
は、アプリケーションのメインウィンドウを定義し、その中に表示されるコンテンツを指定します。具体的には、MainNaviRouter.createModule()
を使用して、MainNaviRouter
クラスで定義されたビューを表示しています。
WindowGroup {
MainNaviRouter.createModule()
.preferredColorScheme(isDarkMode ? .dark:.light)
}
この部分では、MainNaviRouter.createModule()
が呼び出され、MainNaviRouter
クラス内で定義されたビューが生成されます。そして、preferredColorScheme
メソッドを使用して、表示されるビューのカラースキーム(明るいモードまたはダークモード)を設定しています。isDarkMode
プロパティの値によって、カラースキームが切り替わります。
このようにして、WindowGroup
内でアプリケーションのメインビューを指定し、そのビューを表示することができます。そして、onChange(of: scenePhase)
を使用して、アプリケーションのシーンの状態が変化した際に特定のアクションを実行することができます。
MainNaviRouterクラス:ビュー生成と画面遷移の制御
提供されたコードは、MainNaviRouter
というルータークラスの定義を示しています。このクラスは、SwiftUIアプリケーション内でのナビゲーションと画面遷移を管理するためのメソッドを提供します。
import Swinject
class MainNaviRouter: MainNaviRouterProtocol {
static func createModule() -> MainNaviView {
let view = Container.shared.resolve(MainNaviView.self)!
return view
}
func showSplashScreen() -> SplashScreenView {
return SplashScreenRouter.createModule()
}
func showTermView() -> TermOfUseView {
return TermOfUseRouter.createModule()
}
func showLoginView() -> LoginView {
return LoginRouter.createModule()
}
func showMainView() -> MainView {
return MainRouter.createModule()
}
func showHomeView() -> HomeView {
return HomeRouter.createModule()
}
}
具体的には、次のような機能を提供しています:
createModule()
メソッド:
createModule()
メソッドは、MainNaviView
のインスタンスを生成して返します。このメソッドは、アプリケーション内でのナビゲーションの起点となるビューを作成します。このビューは、他のビューへのナビゲーションの出発点として機能します。
- 各
show...View()
メソッド:
showSplashScreen()
、showTermView()
、showLoginView()
、showMainView()
、showHomeView()
メソッドは、それぞれ異なるビューを表示するためのメソッドです。これらのメソッドは、それぞれのルータークラス(SplashScreenRouter
、TermOfUseRouter
、LoginRouter
、MainRouter
、HomeRouter
)に定義されたcreateModule()
メソッドを呼び出して対応するビューのインスタンスを生成し、それを返します。
このようにして、MainNaviRouter
クラスは、アプリケーション内での異なる画面間のナビゲーションを管理するためのインターフェースを提供します。各メソッドは、対応する画面への遷移やビューの生成を行い、アプリケーションの画面遷移フローを制御します。
MainNaviViewクラス:NavigationStackViewを用いた動的なビューの切り替えの実現
NavigationStackView
は、SwiftUIでカスタムのナビゲーションスタックを実装するためのコンテナビューの一種です。通常、SwiftUIの標準的なナビゲーションにはNavigationView
が使用されますが、NavigationStackView
はカスタムナビゲーションの実装に使用されます。
NavigationStackView
を使用することで、以下のような機能を実装することができます:
- カスタムナビゲーションの制御:
NavigationStackView
を使用すると、アプリケーション内でカスタムのナビゲーションスタックを作成し、制御することができます。これにより、標準的なナビゲーションスタックに依存せず、独自のナビゲーションロジックを実装することができます。 - 柔軟なビューの切り替え:
NavigationStackView
内で条件に応じて異なるビューを表示することができます。これにより、ユーザーのアクションやアプリケーションの状態に応じて、動的にビューを切り替えることが可能です。 - アニメーションの制御:
NavigationStackView
を使用してナビゲーションを実装する場合、ビュー間の遷移アニメーションをカスタマイズすることができます。これにより、アプリケーションのユーザーエクスペリエンスを向上させることができます。
NavigationStackView
は、SwiftUIにおいて特定のナビゲーションの要件を満たすために、NavigationView
を拡張または代替するために使用されることがあります。例えば、複雑なナビゲーションロジックやアニメーションを実装する場合などに役立ちます。
import SwiftUI
struct MainNaviView: View {
@ObservedObject private(set) var viewModel: MainNaviViewModel
var body: some View {
NavigationStackView {
switch viewModel.currentScreen {
case .splash:
self.viewModel.showSplashScreen()
case .term:
self.viewModel.showTermView()
case .main:
self.viewModel.showHomeView()
}
}
}
}
struct MainNaviView_Previews: PreviewProvider {
static var previews: some View {
MainNaviRouter.createModule()
}
}
ここでは、コードのさらなる詳細について説明します。
struct MainNaviView: View
:
MainNaviView
はView
プロトコルを準拠しており、UIの一部を定義します。@ObservedObject
プロパティラッパーは、外部からの変更を監視し、ビューが自動的に再描画されるようにします。viewModel
プロパティはMainNaviViewModel
のインスタンスを監視します。body
プロパティは、ビューの実際のレンダリングを定義します。この場合、NavigationStackView
を使用してナビゲーションスタックを構築し、viewModel.currentScreen
の値に基づいて表示する画面を切り替えます。
NavigationStackView
:
- これは、カスタムのナビゲーションスタックを表すコンテナビューです。SwiftUIのデフォルトのナビゲーションスタックとは異なり、カスタムナビゲーションを実装するために使用されます。ここで、SwiftUIのデフォルトのナビゲーションスタックとは、
NavigationView
コンテナビューを使用して構築されるナビゲーションスタックのことです。NavigationView
は、iOSアプリケーションでよく見られるナビゲーションパターンを実装するための基本的なビューです。
switch viewModel.currentScreen
:
viewModel.currentScreen
の値に基づいて、表示する画面を切り替えるswitch
ステートメントです。.splash
、.term
、.main
の各ケースに応じて、対応するビューメソッドを呼び出します。
MainNaviView_Previews
:
- SwiftUIにおけるプレビューを提供するための構造体です。
MainNaviRouter.createModule()
を使用して、MainNaviView
のプレビューを生成します。SwiftUIにおけるプレビューは、XcodeのプレビューエリアでSwiftUIビューの外観や動作をリアルタイムに表示する機能です。プレビューを使用すると、コードを書きながらUIの変更を即座に確認できます。これにより、ビューのデザインやレイアウトを素早く調整し、開発プロセスを効率化できます。
このコードは、SwiftUIを使用してナビゲーションを管理し、MainNaviViewModel
によって提供される状態に基づいてビューを動的に切り替える方法を示しています。SwiftUIは、データ駆動のUIを構築するための強力なツールであり、状態管理、レイアウト、アニメーションなどの機能を提供します。
MainNaviViewModel
クラス:状態の管理
次のコードは、MainNaviViewModel
というViewModelの実装を示しています。このViewModelは、SwiftUIアプリケーション内でのナビゲーションとビューの状態を管理し、ビューとビジネスロジックの間のコミュニケーションを担当します。
import SwiftUI
import Combine
class MainNaviViewModel: BaseViewModel, MainNaviViewModelProtocol, ObservableObject {
@Published var currentScreen: RootScreenEnum = .splash
// MARK: - Properties
private var router: MainNaviRouterProtocol!
private var appState: AppStateProtocol
private var network: NetworkPublisherProtocol
// MARK: - Init
init(appState: AppStateProtocol,
network: NetworkPublisherProtocol) {
self.appState = appState
self.network = network
super.init()
}
func setRouter(router: MainNaviRouterProtocol) {
self.router = router
}
override func bindingOutput() {
super.bindingOutput()
appState.didUpdateRootScreen
.assign(to: \.currentScreen, on: self)
.store(in: &tasks)
network.unAuthenticatePublisher.sink { [weak self] in
self?.appState.logoutSuccessful()
}.store(in: &tasks)
}
func showSplashScreen() -> SplashScreenView {
return self.router.showSplashScreen()
}
func showTermView() -> TermOfUseView {
return self.router.showTermView()
}
func showLoginView() -> LoginView {
return self.router.showLoginView()
}
func showMainView() -> MainView {
return self.router.showMainView()
}
func showHomeView() -> HomeView {
return self.router.showHomeView()
}
}
以下は、このMainNaviViewModel
の機能と構造についての詳細な説明です:
@Published var currentScreen: RootScreenEnum
:
currentScreen
は、現在表示されている画面を示すプロパティです。@Published
プロパティラッパーを使用しており、このプロパティの変更は自動的にオブザーバーに通知されます。
- プロパティ:
router
:MainNaviRouterProtocol
を遵守するルータープロトコルのインスタンス。画面遷移の制御に使用されます。appState
:AppStateProtocol
を遵守するアプリケーションの状態を管理するためのプロトコルのインスタンス。network
:NetworkPublisherProtocol
を遵守するネットワーク関連のイベントを管理するためのプロトコルのインスタンス。
init
メソッド:
- 初期化メソッドで、
appState
とnetwork
を引数として受け取ります。これらのプロパティを設定し、親クラスの初期化メソッドを呼び出します。
setRouter
メソッド:
- ルーターを設定するためのメソッド。ルーターは外部から注入されるため、このメソッドを使用して設定します。
bindingOutput
メソッド:
- 出力のバインディングを設定するメソッド。
appState.didUpdateRootScreen
イベントを監視し、currentScreen
プロパティに反映させます。また、network.unAuthenticatePublisher
からのイベントを監視し、ユーザーが未認証になった場合にログアウトします。
- 画面表示メソッド:
- 各画面表示メソッドは、対応するルーターを使用してビューを表示します。たとえば、
showSplashScreen()
メソッドは、スプラッシュスクリーンビューを表示するためにルーターを使用します。
MainNaviViewModel
は、ビューの状態とナビゲーションを管理するための重要な役割を果たします。ビューとビジネスロジックの間のインタラクションを処理し、アプリケーションの機能を実現するための中心的なコンポーネントです。