Understanding SwiftUI: Modifiers
Tags: swift, ios, swiftui
Published: 2020-10-11 21:35:14


**Views** are lightweight structures that generally have only the body property and intrinsically necessary properties, such as alignment for **VStack/HStack/ZStack** and action for **Button**. They are not very customised by themselves and that is when **View Modifiers** comes into play.

An important thing to undestand is that when we apply a modifier to a View we are not directly modifying it. There is no properties to be really modified . Instead, when a modifier is applied a **ModifiedContent** is returned, which wrappers the View that we applied the modifier.

```swift
let view = Rectangle().frame(width: 100, height: 100)
type(of: view) // ModifiedContent<Rectangle, _FrameLayout>
```

**ModifiedContent** is another very simple struct that holds a **Content** and **Modifier** to be applied in runtime.

```swift
struct ModifiedContent<Content, Modifier> {

  var content: Content
  var modifier: Modifier

}
```

Note that **Modified Content** doesn't implement **View** protocol in its declaration. Instead, **ModifiedContent** implements different protocols based on the protocols implemented by the **Content** and **Modifier** generic properties. This approach keeps **ModifiedContent** itself as simple as possible while allow its to be extensible.

**ModifiedContent** implements **View**, for example, when **Content** and **Modifier** implement **View** and **ViewModifier** protocols, respectively.

```swift
extension ModifiedContent: View 
where Content: View, Modifier: ViewModifier 
{ ... }
```

>**ModifiedContent** for **Scene** and **Widget** are also implemented in the same way, but using **SceneModifier** and **WidgetModifier**.

**ViewModifer** is a protocol which only requirement is a **body** function.

```swift
public protocol ViewModifier {
  associatedtype Body : SwiftUI.View
  func body(content: Self.Content) -> Self.Body
}
```

> Just like **Views**, most built-in **ViewModifiers** actually return **Never** from **body** function because SwiftUI uses only its properties.

You can implement yourself this protocol to create custom modifiers.

```swift
struct CustomModifier: ViewModifier {
func body(content: Content) -> some View {
        content
            .padding()
            .background(Color.gray)
            .clipShape(RoundedRectangle(cornerRadius: 10))
    }
}
```
 
All SwiftUI modifiers are internally backed by a **modifier** function.

```swift
extension View {
  public func modifier<T>(_ modifier: T) -> SwiftUI.ModifiedContent<Self, T> {
        return .init(content: self, modifier: modifier)
    }
}
```

This function is the one that you should use to apply a custom modifier.

```swift
Rectangle().modifier(CustomModifier())
```

Regardless the built-in modifiers, it's worth to mention that although the following pieces of code are equivalent you won't be able to use the last two because **_FrameLayout** has an **internal** initialiser. 

```swift

Rectangle().frame(width: 100, height: 100)

Rectangle().modifier(_FrameLayout(width: 100, height: 100))

ModifedContent(content: Rectangle(), modifier: _FrameLayout(width: 100, height: 100))

```

>There are some **ViewModifiers** that for some reason have a **public** initializer, **_PaddingLayout** for example, but they are not mean to be used directly and there is no guarantee that they will remain accessible in future versions of SwifUI.

## Functional Approach

**Modifiers** and **Stacks** are similar in the sense that they both wrap some content. Stacks wrap their content using a closure so you may wonder why not use this same approach for modifiers.

```swift
FrameLayout(width: 100, height: 100) {
    Rectangle()
}
```

I personally think that this approach makes it a little easier in some cases to understand what is going on when we apply a modifier. The problem is that as soon as you start applying multiple modifiers it quickly becomes a Pyramid of Doom. It does not escalate well.

```swift
CornerRadius(5) {
    Background(Color.red) {
        Padding(10) {
            FrameLayout(width: 100, height: 100) {
                Rectangle()
            }
        }
    }
}
```

The functional approach chosen is much more scalable and cleaner and once you get used it becomes clear that it's the right choice.

```swift
Rectangle()
    .frame(width: 100, height: 100)
    .padding(10)
    .background(Color.red)
    .cornerRadius(5)
```

## Conclusion

**Modifiers** are as essential as **Views** in SwiftUI and allows us to customize **Views** as much as necessary while keeping a clean code, thanks to its wisely chosen functional approach.