HealthKitアプリSwiftコードの紹介(5) – SwiftUIによるビューの実装

Apple

MyAppクラス:WindowGroupによるメインビューの定義

ここからはSwiftUIによるビューのコードを説明していきます。SwiftUIは、Appleが提供するモダンなUIフレームワークであり、Swift言語でiOS、macOS、watchOS、tvOSアプリケーションを構築するための手段です。SwiftUIでは、状態駆動型のUIを構築し、シンプルで直感的な構文を使用してアプリケーションのビューを定義します。

このコードは、SwiftUIのアプリケーションを定義しています。MyAppApp プロトコルを準拠しており、アプリケーションのエントリーポイントとなります。

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()
    }
        
}

具体的には、次のような機能を提供しています:

  1. createModule()メソッド:
  • createModule()メソッドは、MainNaviViewのインスタンスを生成して返します。このメソッドは、アプリケーション内でのナビゲーションの起点となるビューを作成します。このビューは、他のビューへのナビゲーションの出発点として機能します。
  1. show...View()メソッド:
  • showSplashScreen()showTermView()showLoginView()showMainView()showHomeView()メソッドは、それぞれ異なるビューを表示するためのメソッドです。これらのメソッドは、それぞれのルータークラス(SplashScreenRouterTermOfUseRouterLoginRouterMainRouterHomeRouter)に定義されたcreateModule()メソッドを呼び出して対応するビューのインスタンスを生成し、それを返します。

このようにして、MainNaviRouterクラスは、アプリケーション内での異なる画面間のナビゲーションを管理するためのインターフェースを提供します。各メソッドは、対応する画面への遷移やビューの生成を行い、アプリケーションの画面遷移フローを制御します。

MainNaviViewクラス:NavigationStackViewを用いた動的なビューの切り替えの実現

NavigationStackViewは、SwiftUIでカスタムのナビゲーションスタックを実装するためのコンテナビューの一種です。通常、SwiftUIの標準的なナビゲーションにはNavigationViewが使用されますが、NavigationStackViewはカスタムナビゲーションの実装に使用されます。

NavigationStackViewを使用することで、以下のような機能を実装することができます:

  1. カスタムナビゲーションの制御: NavigationStackViewを使用すると、アプリケーション内でカスタムのナビゲーションスタックを作成し、制御することができます。これにより、標準的なナビゲーションスタックに依存せず、独自のナビゲーションロジックを実装することができます。
  2. 柔軟なビューの切り替え: NavigationStackView内で条件に応じて異なるビューを表示することができます。これにより、ユーザーのアクションやアプリケーションの状態に応じて、動的にビューを切り替えることが可能です。
  3. アニメーションの制御: 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()
    }
}

ここでは、コードのさらなる詳細について説明します。

  1. struct MainNaviView: View:
  • MainNaviViewView プロトコルを準拠しており、UIの一部を定義します。
  • @ObservedObject プロパティラッパーは、外部からの変更を監視し、ビューが自動的に再描画されるようにします。viewModel プロパティは MainNaviViewModel のインスタンスを監視します。
  • body プロパティは、ビューの実際のレンダリングを定義します。この場合、NavigationStackView を使用してナビゲーションスタックを構築し、viewModel.currentScreen の値に基づいて表示する画面を切り替えます。
  1. NavigationStackView:
  • これは、カスタムのナビゲーションスタックを表すコンテナビューです。SwiftUIのデフォルトのナビゲーションスタックとは異なり、カスタムナビゲーションを実装するために使用されます。ここで、SwiftUIのデフォルトのナビゲーションスタックとは、NavigationView コンテナビューを使用して構築されるナビゲーションスタックのことです。NavigationView は、iOSアプリケーションでよく見られるナビゲーションパターンを実装するための基本的なビューです。
  1. switch viewModel.currentScreen:
  • viewModel.currentScreen の値に基づいて、表示する画面を切り替える switch ステートメントです。.splash.term.main の各ケースに応じて、対応するビューメソッドを呼び出します。
  1. 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の機能と構造についての詳細な説明です:

  1. @Published var currentScreen: RootScreenEnum:
  • currentScreenは、現在表示されている画面を示すプロパティです。@Publishedプロパティラッパーを使用しており、このプロパティの変更は自動的にオブザーバーに通知されます。
  1. プロパティ:
  • router: MainNaviRouterProtocolを遵守するルータープロトコルのインスタンス。画面遷移の制御に使用されます。
  • appState: AppStateProtocolを遵守するアプリケーションの状態を管理するためのプロトコルのインスタンス。
  • network: NetworkPublisherProtocolを遵守するネットワーク関連のイベントを管理するためのプロトコルのインスタンス。
  1. initメソッド:
  • 初期化メソッドで、appStatenetworkを引数として受け取ります。これらのプロパティを設定し、親クラスの初期化メソッドを呼び出します。
  1. setRouterメソッド:
  • ルーターを設定するためのメソッド。ルーターは外部から注入されるため、このメソッドを使用して設定します。
  1. bindingOutputメソッド:
  • 出力のバインディングを設定するメソッド。appState.didUpdateRootScreenイベントを監視し、currentScreenプロパティに反映させます。また、network.unAuthenticatePublisherからのイベントを監視し、ユーザーが未認証になった場合にログアウトします。
  1. 画面表示メソッド:
  • 各画面表示メソッドは、対応するルーターを使用してビューを表示します。たとえば、showSplashScreen()メソッドは、スプラッシュスクリーンビューを表示するためにルーターを使用します。

MainNaviViewModelは、ビューの状態とナビゲーションを管理するための重要な役割を果たします。ビューとビジネスロジックの間のインタラクションを処理し、アプリケーションの機能を実現するための中心的なコンポーネントです。

関連記事

カテゴリー

アーカイブ