Dot Freelancer
Dot Freelancer

Reputation: 4253

How to define a Protocol with generic function and some View as return type

I want to create a protocol with one function for the following

@ViewBuilder
func navigate<T: View>(content: () -> T) -> some View {
    switch self{
    case .list:
        NavigationLink(destination: Text("Test")){ content() }
    default:
        EmptyView()
    }
}

This is my try and it doesn't work

protocol Test {
    associatedtype Result: View

    func navigate<T: View>(content: () -> T) -> Result
}

Upvotes: 7

Views: 2735

Answers (2)

Rob Napier
Rob Napier

Reputation: 299623

This is explicitly disallowed by the Associated Type Inference section of the Opaque Result Types proposal. From the proposal:

Associated type inference can only infer an opaque result type for a non-generic requirement, because the opaque type is parameterized by the function's own generic arguments. For instance, in:

protocol P {
 associatedtype A: P
 func foo<T: P>(x: T) -> A
}

struct Foo: P {
 func foo<T: P>(x: T) -> some P {
   return x
 }
}

there is no single underlying type to infer A to, because the return type of foo is allowed to change with T.

To make this more specific to your question, in order to conform to Test, there must be exactly one type that can be assigned to Result. However, your return type is generic, so it depends on the what is passed. The actual (non-opaque) return type of navigate is:

_ConditionalContent<NavigationLink<T, Text>, EmptyView>

But T is a type parameter and changes depending on how navigate is called. So there is no one type that can be assigned to Result.

You'll need something that can return a single, non-parameterized type. For the example you've given, that's probably AnyView, which is annoying.

That said, what you've written here doesn't really feel like a protocol. It looks a lot like just a function. I'd think a lot about how many different ways navigate could be written. If everyone would implement it the same way, that's just a function. (If you give another example of a conforming type, it might help to design a better approach.)

Upvotes: 6

SeaSpell
SeaSpell

Reputation: 758

You could do this where you call it on the destination.

extension View {
     func navigate< Destination: View, Label: View>(content: Label) -> some View   {
          NavigationLink(destination: Destination) { content.    }
     }
}

The problem is you can't find out the caller inside the method because you're extending a protocol.

Upvotes: 0

Related Questions