Home > front end >  SwiftUI: why does ModifiedContent conform to `some View` function return type?
SwiftUI: why does ModifiedContent conform to `some View` function return type?

Time:01-27

// it compiles without any problems, regardless ModifiedContent is not a View
func modify(with viewModifier: some ViewModifier) -> some View {
    let content: ModifiedContent<Self, some ViewModifier> = modifier(viewModifier)
    return content
}

but if we write same in generic way, we got expected error message

func modify<V>(with viewModifier: some ViewModifier) -> V where V : View {
    let content: ModifiedContent<Self, some ViewModifier> = modifier(viewModifier)
    return content // error: Cannot convert return expression of type 
                   //       'ModifiedContent<Self, some ViewModifier>' to return type 'V'
}

CodePudding user response:

Here's your first, working declaration:

func modify(with viewModifier: some ViewModifier) -> some View

You used the some keyword twice in that function declaration. It is important to understand that some means different things in those two uses.

In the first use (viewModifier: some ViewModifier), the some keyword is “syntactic sugar” for a implicit, unnamed generic parameter. We can de-sugar the use of some there by replacing it with an explicit, named generic parameter, like this:

// Replace `some` in parameter position with an explicit generic parameter:
func modify<VM: ViewModifier>(with viewModifier: VM) -> some View

When you call that modify method, you as the caller choose a real type to replace the VM parameter. So you can call modify many times, with a different type to replace VM each time. The only constraint is the replacement type must always conform to the ViewModifier protocol.

The second use of some in your first function declaration (in -> some View) has a different meaning. It means the function will return a value whose type conforms to View, but the specific type is chosen by the body of the function. You as the function caller do not choose the type.

To summarize the differences:

  • The some keyword in parameter position

    • represents an implicit, unnamed generic type parameter;
    • can be de-sugared to an explicit, named generic parameter without changing the function body;
    • is replaced by a type chosen by the caller of the function.
  • The some keyword in return type position

    • represents a type known to conform to a protocol;
    • is a type chosen by the body of the function.

Now let's look at your second, broken function declaration:

func modify<V>(with viewModifier: some ViewModifier) -> V where V : View

This is like your first function declaration, except that you have replaced the some in return type position with the generic parameter V.

The problem is that these don't mean the same things. As I explained above, a return type of some View means the function body chooses the actual type, but a generic parameter is always chosen by the caller of the function.

That's why you get the error when your function tries to return content. There's no guarantee that content has type V. Your function knows so little about type V (you only know it conforms to View) that there's no way for the function to even create a value of type V (because the View protocol doesn't have any init requirements).

You also have this comment on your first, working function declaration:

// it compiles without any problems, regardless ModifiedContent is not a View

I assume you declared the modify(withViewModifier:) method in an extension View. That means Self conforms to View, so ModifiedContent<Self, some ViewModifier> conforms to View.

CodePudding user response:

ModifiedContent is a generic View so you can return it as opaque result type some View. when returning -> V where V : View ModifiedContent has to match any V type where it is called from, which is not possible.

see as well: https://forums.swift.org/t/how-to-use-a-generic-type-as-the-functions-return-type/37666/2

  • Related