Optimisez votre code SwiftUI : Deux méthodes pour éviter la duplication

Cet article est libre d'accès pour tous grâce aux membres du Mindsers Club qui soutiennent notre blog indépendant. Si vous appréciez le contenu que je propose, je vous invite à rejoindre le club dès aujourd'hui.

La duplication de code est un vrai problème dans les applications. Avoir beaucoup de code dupliqué signifie “maintenance compliquée” mais aussi une expérience catastrophique.

J’ai deux méthodes pour éviter ce problème dans mes applications SwiftUI. Je vous les présente dans cet article.

Composants custom pour une meilleure modularité du code SwiftUI

Comme on le ferait avec d’autres frameworks, SwiftUI permet de créer des vues de façon déclarative/descriptive.

Les vues sont composées d’autres vues et on peut répéter le schema à l’infini sur plusieurs niveaux.

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

Dans l’exemple ci-dessus, la vue SignInScreen est composée d’un vue VStack, d’une vue Text et une vue Spacer. Même fonctionnement donc qu’un framework web ou le composant global va être composé de plusieurs composants plus petits.

Puisque les vues ne sont que des assemblages de vue, nous pouvons nous aussi créer nos propres vues pour rassembler des éléments qui se répètent.

struct SignInScreen: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            TextField("Adresse e-mail", text: $email)

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

struct SignUpScreen: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            TextField("Adresse e-mail", text: $email)

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

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

La vue DividerCustom regroupe trois autres vues et forme ainsi une vue qui est rappelée dans la vue SignInScreen et SignUpScreen.

Modifier le code de DividerCustom va mettre à jour l’élément dans les deux composants où il est utilisé. Cela évite donc que le code soit dupliqué plusieurs fois dans l’application.

Optimisation avec les modifiers et ViewModifier

Des fois, ce que l’on duplique ce n’est pas un ensemble de vues mais un ensemble de modifications sur une seule vue.

struct Buttons: View {
    var body: some View {
        Button {
            // action
        } label: {
            Label("Connexion avec 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("Connexion avec 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("Connexion avec GitHub", image: "GitHubLogo")
                .frame(maxWidth: .infinity)
        }
        .padding()
        .frame(minWidth: 300, alignment: .center)
        .background(backgroundColor)
        .foregroundColor(foregroundColor)
        .cornerRadius(30)
        .font(.Theme.regular(size: 16))
    }
}

Dans l'exemple ci-dessus, on veut afficher trois boutons de connexion pour différents services.

On ne veut pas changer le comportement d'un bouton classique ou même l'associer à d'autres vues pour qu'ils fonctionnent ensemble. Ce que l'on veut ici, c'est modifier le design du bouton et que tous les boutons se ressemblent.

On se retrouve donc avec 6 lignes de modification d'un bouton qui sont dupliquées à chaque fois qu'un nouveau bouton est créé.

Si je souhaite finalement modifier la largeur maximale du bouton, je vais devoir appliquer la modification manuellement pour chaque bouton.

Une solution pour éviter la duplication de code (et simplifier la maintenance de l'application) dans cette situation, ce sont les ViewModifier. Il s'agit d'une méthode pour appliquer des modifiers à plusieurs vues très simplement :

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))
    }
}

Un ViewModifier est un struct avec une fonction body. C’est cette fonction qui va appliquer les styles à nos vue.

Pour l’utiliser on utilise le modifier générique modifier() comme suit:

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

Voilà c’est tout !

BasicButton va appliquer les styles à notre vue Button sans plus d’effort.

Pour rendre le code un peu plus élégant, voici une petite astuce :

  1. On ajoute tout d’abord une fonction à la classe View qui va appeler le modifier.Notons également l’utilisation des paramètres optionnels pour ne pas devoir les mentionner s’ils ne changent pas.
  2. Et on appelle cette fonction depuis notre vue Bouton comme les modifiers classiques

Vous pouvez également limiter l'utilisation de ce modificateur à la vue Button en étendant la classe Button plutôt que View, comme nous l'avons fait ici.

L'inconvénient est que vous ne pourrez appeler le modificateur que directement sur la vue. Il ne pourra pas être chaîné à la suite d'autres modificateurs, car ceux-ci retournent un objet de type View et non Button.

Conclusion

Comme déjà discuté, éviter la duplication de code n'est pas qu'une bonne pratique pour faire "joli". Il s'agit plutôt d'une des façons les plus simples d'optimiser son code pour réduire le temps de développement et le temps de maintenance d'une application.

N'hésitez pas à utiliser les ViewModifier ou la création de composants custom pour optimiser votre code SwiftUI.

Rejoins 250+ développeurs de notre liste de diffusion et sois reçois les articles directement dans ta boite mail.

S'inscrire à la newsletter

Aucun spam. Désabonnes-toi en un seul clic à tout moment.

Si vous avez des questions ou des remarques/conseils, n'hésitez pas à laisser un commentaire plus bas ! Je serais ravis de vous lire. Et si vous aimez l'article, n'oubliez pas de le partager avec vos amis.