Optimize your SwiftUI code: Two ways to avoid duplication
Tags: ios, swift, bestpractices, cleancode
Published: 2024-06-10 06:42:03

---
title: Optimize your SwiftUI code: Two ways to avoid duplication
published: true
date: 2023-06-24 14:19:20 UTC
tags: ios,swift,bestpractices,cleancode
canonical_url: https://mindsers.blog/post/optimize-your-swiftui-code-two-ways-to-avoid-duplication/
---

 ![Optimize your SwiftUI code: Two ways to avoid duplication](https://images.unsplash.com/photo-1614741118887-7a4ee193a5fa?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wxMTc3M3wwfDF8c2VhcmNofDF8fHN3aWZ0fGVufDB8fHx8MTY4NzYxMjA1OXww&ixlib=rb-4.0.3&q=80&w=2000)

**Code duplication** is a real problem in applications. Having a lot of **duplicate code** means “complicated maintenance” but also a catastrophic experience.

I have two methods to avoid this problem in my [Swift applications](https://steply-app.com). I'll tell you all about them in this article.

## Custom components for greater SwiftUI code modularity
As you would with other frameworks, SwiftUI allows you to create views in a declarative/descriptive way.

The views are composed of other views and the pattern can be repeated endlessly on several levels.

```swift
struct SignInScreen: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            Text("Connect")
            Spacer()
        }
    }
}
```

In the example above, the `SignInScreen` view is composed of a `VStack` view, a Text view and a Spacer view. Same operation therefore as a web framework or the global component will be composed of several smaller components.

Since views are just assemblies of views, we too can create our own views to bring together repeating elements.

```swift
struct SignInScreen: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            TextField("Email address", text: $email)

			DividerCustom()
            
            Button("Connect") {
                // action
            }
        }
    }
}

struct SignUpScreen: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            TextField("Email address", text: $email)

			DividerCustom()
            
            Button("Sign Up") {
                // action
            }
        }
    }
}

struct DividerCustom: View {
    var body: some View {
        HStack(alignment: .center) {
            VStack {
                Divider()
                    .frame(height: 1.0)
                    .background(.green)
            }
            Text("or")
            VStack {
                Divider()
                    .frame(height: 1.0)
                    .background(.green)
            }
        }
    }
}
```


The `DividerCustom` view combines three other views and thus forms a view that is called in the `SignInScreen` and `SignUpScreen` view.

Modifying the `DividerCustom` code will update the element in both components where it is used. This prevents the code from being duplicated several times in the application.

## Optimization with modifiers and `ViewModifier`
Sometimes, what is duplicated is not a set of components but a set of modifications on a single view.

```swift
struct Buttons: View {
    var body: some View {
        Button {
            // action
        } label: {
            Label("Connect with Google", image: "GoogleLogo")
                .frame(maxWidth: .infinity)
        }
        .padding()
        .frame(minWidth: 300, alignment: .center)
        .background(backgroundColor)
        .foregroundColor(foregroundColor)
        .cornerRadius(30)
        .font(.Theme.regular(size: 16))

        Button {
            // action
        } label: {
            Label("Connect with Facebook", image: "FacebookLogo")
                .frame(maxWidth: .infinity)
        }
        .padding()
        .frame(minWidth: 300, alignment: .center)
        .background(backgroundColor)
        .foregroundColor(foregroundColor)
        .cornerRadius(30)
        .font(.Theme.regular(size: 16))

        Button {
            // action
        } label: {
            Label("Connect with GitHub", image: "GitHubLogo")
                .frame(maxWidth: .infinity)
        }
        .padding()
        .frame(minWidth: 300, alignment: .center)
        .background(backgroundColor)
        .foregroundColor(foregroundColor)
        .cornerRadius(30)
        .font(.Theme.regular(size: 16))
    }
}
```

In the example above, we want to display three login buttons for different services.

We don't want to change the behavior of a classic button or even associate it with other views so that they work together. What we want here is to modify the design of the button so that all the buttons look the same.

We will end up with 6 lines of modifications of a button which are duplicated each time a new button is created.

If I ultimately want to change the button's maximum width, I'll have to apply the change manually for each button!

One solution to avoid code duplication (and simplify application maintenance) in this situation is `ViewModifiers`. This is a very simple way to apply modifiers to multiple views:

```swift
struct BasicButton: ViewModifier {
    let backgroundColor: Color
    let foregroundColor: Color
    
    func body(content: Content) -> some View {
        content
            .padding()
            .frame(minWidth: 300, alignment: .center)
            .background(backgroundColor)
            .foregroundColor(foregroundColor)
            .cornerRadius(30)
            .font(.Theme.regular(size: 16))
    }
}
```


A `ViewModifier` is a struct with a body function. It is this function that will apply the styles to our views.
To use it, we use the generic modifier `modifier()` as follows:

```swift
Button {
  // action
} label: {
  Label("Connect with GitHub", image: "GitHubLogo")
    .frame(maxWidth: .infinity)
}
.modifier(BasicButton(backgroundColor: .black, foregroundColor: .white))
```


And that's it!

`BasicButton` will apply the styles to our Button view with no more effort.

To make the code a little more elegant, here is a little trick:
And we call this function from our Button view like classic modifiers

```swift
Button {
  // action
} label: {
  Label("Connect with GitHub", image: "GitHubLogo")
    .frame(maxWidth: .infinity)
}
.withSteplyDefaultStyle()
```


We first add a function to the `View` class that will call the modifier.

```swift
extension View {
    func withSteplyDefaultStyle(foregroundColor: Color = .white, backgroundColor: Color = .blue) -> some View {
        modifier(BasicButton(backgroundColor: backgroundColor, foregroundColor: foregroundColor))
    }
}
```

Note also the use of optional parameters, so you don't have to mention them if they do not change.

You can also limit the use of this modifier to the `Button` view by extending the `Button` class rather than View, as we have done here.

The downside is that you will only be able to call the modifier directly on the view. It cannot be chained after other modifiers because this one returns an object of type View and not `Button`.

## Conclusion

As already discussed, avoiding code duplication is not only a good practice to make your code look “pretty”. Rather, it's one of the easiest ways to optimize your code to reduce development time and application maintenance time.
Do not hesitate to use the `ViewModifier` or the creation of custom components to optimize your [SwiftUI](https://mindsers.blog/topic/swift/) code.