Arkazzum
(Arkazzum)
1
I'm writing a generic navigator for SwiftUI views
protocol FlowNavigator {
associatedtype FlowRoute
func navigate<T: View>(_ route: FlowRoute, content: () -> T) -> AnyView
}
to use it with an enum representing current app flow
enum AppFlow {
case access, createAccount, login, loginAndSecurity
func getNavigator<T: FlowNavigator>() -> T? {
switch self {
case .access:
return nil
case .createAccount:
return CreateAccountNavigator() as? T
case .login:
return nil
case .loginAndSecurity:
return LoginAndSecurityNavigator() as? T
}
}
}
with FlowNavigator implementation like this
class LoginAndSecurityNavigator: FlowNavigator {
func navigate<T: View>(_ route: LoginAndSecurityRoute, content: () -> T) -> AnyView {
switch route {
case .changePassword:
return NavigationLink(
destination: Text("CHANGE PASSWORD")) {
content()
}.toAnyView()
case .linkedSocials:
return NavigationLink(
destination: LinkedSocialsScreen()) {
content()
}.toAnyView()
}
}
}
where LoginAndSecurityRoute is a basic enum
enum LoginAndSecurityRoute {
case changePassword
case linkedSocials
}
(CreateAccountNavigator is basically the same, it differs only for navigationLinks)
Xcode doesn't complain with this code but when I try to use it with
let flow = AppState.AppFlow.loginAndSecurity
let flowNavigator: FlowNavigator? = flow.getNavigator()
compiler says
Protocol 'FlowNavigator' as a type cannot conform to the protocol
itself
I'm pretty sure solution is trivial but I cannot find a way to make it work...
ksluder
(Kyle Sluder)
2
@hborla gave an explanation of this error and explained how it motivates the adoption of any P syntax: SE-0335: Introduce existential `any` - #112 by hborla
The root of your problem is the associated type. The type of the value on the left hand side has no constraint on the associated type, so any code that uses it has no idea what the type of the first argument of navigate(:,content:) is.
2 Likes
jjrscott
(John Scott)
3
filip-sakel shared this in AnyCodable Efficacy. To paraphrase: the existential box is opened on protocol methods, so by extending/trampolining off the protocol we can get access to the real type again. Genius!
Here's a worked example.
protocol Stuffable { }
struct Stuffer: Stuffable { }
func withStuff<T>(_ stuff: T) where T: Stuffable {
print("Hello \(T.self)")
}
extension Stuffable {
func trampoline() { withStuff(self) }
}
let value: Stuffable = Stuffer()
withStuff(value) ❌
value.trampoline() ✅
5 Likes