Optimisez votre code SwiftUI : Deux méthodes pour éviter la duplication
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 :
- 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. - 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.