基于身份验证状态的 SwiftUI 私有路由

SwiftUI private route based on Authentication Status

我需要在 swiftUI 上实现一种机制,就像反应私有和 public 路由一样。基本上我有几十个视图,其中一些视图需要根据用户登录状态进行身份验证。到目前为止,我已尝试将当前屏幕保存在环境对象中,如下所示 class

    enum Routes {
    case screenA,
         screenB,
         screenC,
         screenD,
         screenE,
         screenF,
         screenG,
         loginScreen
    
    var isAuthRequired: Bool {
            if case . screenA = self {
                return true
            } else if case . screenD = self {
                return true
            } else {
                return false
            }
        }
}

class AuthenticatedRoute: ObservableObject {
    
    @Published var currentRoute: Routes
    
    init(){
        self.currentRoute = . screenA
    }
}

在我的主屏幕上,每次当前屏幕更改时,我都会检查用户登录和当前页面是否需要身份验证。

struct MainView: View {
    @StateObject var authenticatedRoute = AuthenticatedRoute()
    @EnvironmentObject var userAuth: UserAuth
    
    var body: some View {
        mainView()
            .environmentObject(authenticatedRoute)
    }
    
    
    @ViewBuilder
    func mainView() -> some View {
        if (self.authenticatedRoute.currentRoute.isAuthRequired && !userAuth.isLoggedIn) {
            LoginView()
        }
        else {
            DefaultTabView()
        }
    }
}

这是我如何不断更改此环境变量的示例。我改变了视图的环境对象onAppear事件方法。

struct ScreenA: View {
    @EnvironmentObject var authenticatedRoute: AuthenticatedRoute
    
    var body: some View {
        NavigationView {
            someContent()
        }.onAppear {
            authenticatedRoute.currentRoute = .screenA
        }
    }
}

虽然出于某种原因这种方法适用于大多数情况,但当屏幕处于选项卡导航时它会表现得很奇怪。此外,我对这个解决方案感到不舒服,我需要在每个页面上手动更改屏幕名称,并在主视图中检查身份验证状态。我认为如果我可以在每次页面更改之前编写一种拦截器并检查所需的目的地是否需要身份验证以及用户是否经过身份验证但我找不到管理它的方法会更好。我是 iOS 开发的新手,有过 React Native 的经验,但我认为这应该不难实现,因为这是大多数应用程序的要求。

所以基本上我需要在 swiftUI 中实现私有和 public 路由器或拦截每个页面更改,所以我不应该手动修改每个页面上的环境变量,也不应该检查函数内 MainView 中的条件。

我可以提出另一种不需要 class AuthenticatedRoute 的方法,希望这就是您要找的。过程是:

  1. 在 class UserAuth 中,创建一个静态 shared 实例,它可以在您的代码中的任何地方调用,并确保您始终使用同一个实例.

  2. 创建一个扩展View的修饰符,读取UserAuth.shared中的状态,并根据是否需要认证显示必要的视图(LoginView()如果用户未通过身份验证。

  3. 在需要用户进行身份验证的任何视图的最外层容器(VStackNavigationView 等)使用修饰符。

下面的例子展示了它是如何工作的,如果你想 运行 它:

1.静态UserAuth实例

class UserAuth: ObservableObject {
    static let shared = UserAuth()     // This is to assure that you refer to the same instance all over the code
    
    @Published private(set) var isLoggedIn = false    // Use the variable you already have
    
    func logInOrOff() {     // Implement each func as needed
        isLoggedIn.toggle()
    }
}

2。创建 View 修饰符

extension View {
    
    @ViewBuilder
    func requiresAuthentication() -> some View {
        if UserAuth.shared.isLoggedIn {  // "shared" is the same instance used by the views
            self
        } else {
            LoginView()
        }
    }
}

3。在需要认证的视图底部应用修饰符

struct Example: View {
    
    @StateObject private var userAuth = UserAuth.shared    //  Or @EnvironmentObject, as you wish
    
    var body: some View {
        VStack {
            Text(userAuth.isLoggedIn ? "Now we're good" : "You must log in")
                .padding()
            
            Button {
                userAuth.logInOrOff()
            } label: {
                Text("Logoff")
            }
        }
        .requiresAuthentication()    // This is what makes your view safe
    }
}

struct LoginView: View {
    var body: some View {
        VStack {
            Text("Authentication is required")
                .padding()
            
            Button {
                UserAuth.shared.logInOrOff()
            } label: {
                Text("Log in")
            }
        }
    }
}