I'm trying to figure out the navigation in the beginning of an app I'm making in the SwiftUI lifecycle. Depending on settings determined at launch, the user may start in one of three views. If the user opens the app from a push notification, they should see the video view (WrappedVideoViewController). Otherwise, if they are logged in already, they should see the Home view, or if not, the Login view. I'm using ContentView as the parent:
import SwiftUI
struct ContentView: View {
@EnvironmentObject var notifService: NotificationService
@EnvironmentObject var authState: AuthenticationState
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@ObservedObject var viewRouter: ViewRouter
init() {
self.viewRouter = ViewRouter.shared
}
var body: some View {
print("in ContentView body")
switch viewRouter.currentScreen {
case .home:
print("home")
return AnyView(Home(innerGradientColor: Color.homeActiveInner, outerGradientColor: Color.homeActiveOuter))
case .login:
print("login")
return
AnyView(Login()
.environmentObject(AuthenticationState.shared))
case .video:
print("video")
return AnyView(WrappedVideoViewController())
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(AuthenticationState.shared)
.environmentObject(NotificationService())
}
}
My ViewRouter class is an ObservableObject meant to control which screen is shown. Currently, I have this:
//
// NavigationState.swift
// BumpCall-SwiftUI
//
// Created by Ben on 1/22/21.
//
import Foundation
import SwiftUI
enum Screen {
case home
case video
case login
}
class ViewRouter: ObservableObject {
var authState: AuthenticationState
var notifService: NotificationService
static var shared = ViewRouter(authState: AuthenticationState.shared, notifService: NotificationService.shared)
init(authState: AuthenticationState, notifService: NotificationService) {
self.authState = authState
self.notifService = notifService
if notifService.dumbData != nil {
currentScreen = .video
} else if (notifService.dumbData == nil && authState.isLoggedIn()) {
currentScreen = .home
} else {
currentScreen = .login
}
}
@Published var currentScreen: Screen
}
It's tricky because the ViewRouter is dependent on two other ObservableObjects, which I'm trying to handle here with the init(). It appears that the AuthenticationState class is working fine because upon opening the app, it does send you to the home screen if you are logged in. However, opening from a notification also sends me to the home screen. NotificationService changes the ViewRouter singleton in the didReceive response: method:
class NotificationService: NSObject, ObservableObject {
@Published var dumbData: UNNotificationResponse?
static var shared = NotificationService()
override init() {
super.init()
UNUserNotificationCenter.current().delegate = self
}
}
extension NotificationService: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
print("presenting notification")
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
ViewRouter.shared.notifService.dumbData = response
print("Notification service didReceive response")
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) { }
}
But the singleton must not update for ContentView to notice. This is super convoluted and it's been confusing me for days, any help would be much appreciated.
question from:
https://stackoverflow.com/questions/65863608/swiftui-determine-initial-view 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…