7

As far as I know, the definition of the ViewModifier protocol looks like this:

protocol ViewModifier {

    // content view type passed to body()
    typealias Content

    // type of view returned by body()
    associatedtype Body : View

    // only requirement
   func body(content: Self.Content) -> Self.Body

}

My question is:

Why Self.Content is a typealias while Self.Body is an associatedtype ? What's the difference?

mfaani
  • 33,269
  • 19
  • 164
  • 293
lochiwei
  • 1,240
  • 9
  • 16
  • 1
    `Body` will be inferred *from your* return, so it is associatedtype, `Content` will provided *to you*, it is known by ViewModifer (because generated by it), but hidden for you, so is typealias. – Asperi Sep 10 '20 at 13:25

2 Answers2

5

Why Self.Content is a typealias while Self.Body is an associatedtype ? What's the difference?

Because Content is a typealias, the author of the ViewModifier protocol gets to pick the type being aliased when she writes the protocol. (You can't see the type being aliased because that type is _ViewModifier_Content<Self>. When an identifier in the SDK starts with _, Apple omits the identifier from documentation and generated interfaces.)

Because Body is an associatedtype, you get to pick the type that it aliases, when you write a type that conforms to the ViewModifier protocol. You can make Body be any type you want, subject to two conditions:

  • You must pick a type that conforms to View (because the ViewModifier protocol constrains Body to conform to View).

  • You must be able to create or obtain an instance of whatever type you pick, because you have to return an instance of it from the body method. (Or you could crash or hang to avoid returning at all, but that's usually not what you want…)

So, when you implement a type conforming to ViewModifier, you cannot influence what Content means. It always means _ViewModifier_Content<Self>. But you can choose what Body means, by choosing the return type of the body method.

Here's I'll force Body to mean EmptyView:

struct EmptyModifier: ViewModifier {
    func body(content: Content) -> EmptyView {
        EmptyView()
    }
}

And here I'll force Body to mean Color:

struct RedModifier: ViewModifier {
    func body(content: Content) -> Color {
        Color.red
    }
}

Usually we use some View as the type, which means that Swift deduces the exact type for us and keeps it a secret:

struct FrameModifier: ViewModifier {
    var color: Color
    var width: CGFloat

    func body(content: Content) -> some View {
        return content
            .padding(width)
            .border(color, width: width)
    }
}

Here, all we know about the Body type is that it conforms to View. Swift tries pretty hard to keep us from finding out the real type at compile time.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • The protocol fully defines a `typealias`, but only constrains the definition of an `associatedtype`. Each conformance then fully defines the `associatedtype` in its own way. This is the difference between a `typealias` and an `associatedtype`. My `EmptyModifier`, `RedModifier`, and `FrameModifier` examples are three different conformances. Each conformance defines `Body` differently. If `Body` were a `typealias`, it would have been fully defined by `ViewModifier` and my three conformances could not give it three different definitions. – rob mayoff Sep 11 '20 at 17:17
0
  • typealias only changes the name of the type. Nothing more.

  • associatedtype is a way to include generics in the protocol implementation.


public protocol ViewModifier {

    /// The type of view representing the body of `Self`.
    associatedtype Body : View

    /// Returns the current body of `self`. `content` is a proxy for
    /// the view that will have the modifier represented by `Self`
    /// applied to it.
    func body(content: Self.Content) -> Self.Body

    /// The content view type passed to `body()`.
    typealias Content
}

In the above example Body is a concrete type conforming to View and is inferred by the body(content:) return type.

Content is just another name for a type passed as the content parameter.


This answer by rob mayoff explained the ViewModifier in a more detailed way:

So this tells us that, when we write our own ViewModifier, our body method will receive some sort of View (the specific type is defined by the framework and we can just call it Content), and return some sort of View (we get to pick the specific return type).

This means you don't know what the Content is, you just operate on it - it's called Content for your convenience, so you don't have to deal with _ViewModifier_Content<Self>.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • _associatedtype is a way to include generics in the protocol implementation_ It's merely a way to include another type class/protocol. e.g. you can also use a `UIView` which isn't a protocol... – mfaani Sep 10 '20 at 14:00