Cette semaine, je veux parler de la modélisation d'une couche de données dans SwiftUI. J'ai déjà fini de travailler sur ma toute première application, que je crée en utilisant uniquement SwiftUI. Maintenant, je peux partager la façon de créer une couche de modèle à l'aide des objets Store que j'ai utilisés lors du développement de l'application NapBot.
Objet de magasin
Les objets de magasin sont chargés de maintenir l'état et de fournir l'action pour le changer. Vous pouvez avoir autant d'objets Store que nécessaire, de préférence ils sont simples et responsables d'une petite partie de l'état de votre application. Par exemple, vous pouvez avoir
SettingsStore pour enregistrer l'état des paramètres utilisateur et
TodoStore pour enregistrer des tâches personnalisées.
Pour créer un objet Store, vous devez créer une classe conforme au protocole
ObservableObject . Le protocole ObservableObject permet à SwiftUI d'observer et de répondre aux changements de données. Pour en savoir plus sur ObservableObject, consultez l'article "
Gestion du flux de données dans SwiftUI ". Regardons un exemple simple d'un objet
SettingsStore .
import Foundation import Combine final class SettingsStore: ObservableObject { let objectWillChange = PassthroughSubject<Void, Never>() @UserDefault(Constants.UserDefaults.sleepGoal, defaultValue: 8.0) var sleepGoal: Double @UserDefault(Constants.UserDefaults.notifications, defaultValue: true) var isNotificationsEnabled: Bool private var didChangeCancellable: AnyCancellable? override init() { super.init() didChangeCancellable = NotificationCenter.default .publisher(for: UserDefaults.didChangeNotification) .map { _ in () } .receive(on: DispatchQueue.main) .subscribe(objectWillChange) } }
Dans l'exemple de code ci-dessus, nous avons la classe
SettingsStore , qui donne accès aux paramètres utilisateur. Nous utilisons également
didChangeNotification pour informer SwiftUI chaque fois que l'utilisateur modifie les paramètres par défaut.
Utilisation étendue
Voyons une autre utilisation de l'objet de magasin en créant une application
Todo simple. Nous devons créer un objet de stockage qui stocke une liste de tâches et fournit des actions pour les modifier, par exemple, les supprimer et les filtrer.
import Foundation import Combine struct Todo: Identifiable, Hashable { let id = UUID() var title: String var date: Date var isDone: Bool var priority: Int } final class TodosStore: ObservableObject { @Published var todos: [Todo] = [] func orderByDate() { todos.sort { $0.date < $1.date } } func orderByPriority() { todos.sort { $0.priority > $1.priority } } func removeCompleted() { todos.removeAll { $0.isDone } } }
Il existe une classe
TodosStore conforme au protocole ObservableObject. TodosStore propose plusieurs actions pour changer son état, nous pouvons utiliser ces méthodes à partir de nos vues. Par défaut,
SwiftUI met Ă jour la vue chaque fois que le champ
@Published change . C'est pourquoi le tableau des éléments
Todo est désigné comme
@Published . Dès que nous ajoutons ou supprimons des Ă©lĂ©ments de ce tableau, SwiftUI mettra Ă jour la vue abonnĂ©e Ă
TodosStore .
Vous pouvez maintenant créer une vue qui affiche une liste de tâches et des actions telles que marquer une tâche comme terminée, supprimer et changer l'ordre dans lequel les tâches sont affichées. Commençons par créer une vue qui affiche le titre de la tâche et un commutateur pour marquer la tâche comme terminée.
import SwiftUI struct TodoItemView: View { let todo: Binding<Todo> var body: some View { HStack { Toggle(isOn: todo.isDone) { Text(todo.title.wrappedValue) .strikethrough(todo.isDone.wrappedValue) } } } }
Dans l'exemple ci-dessus, la
liaison a été utilisée pour fournir une référence, par exemple l'accès à un type de valeur. En d'autres termes, accordez un accès en écriture à l'élément todo.
TodoItemView ne possède pas d'instance de la structure Todo, mais il a un accès en écriture à TodoStore via
Binding .
import SwiftUI struct TodosView: View { @EnvironmentObject var store: TodosStore @State private var draft: String = "" var body: some View { NavigationView { List { TextField("Type something...", text: $draft, onCommit: addTodo) ForEach(store.todos.indexed(), id: \.1.id) { index, _ in TodoItemView(todo: self.$store.todos[index]) } .onDelete(perform: delete) .onMove(perform: move) } .navigationBarItems(trailing: EditButton()) .navigationBarTitle("Todos") } } private func delete(_ indexes: IndexSet) { store.todos.remove(atOffsets: indexes) } private func move(_ indexes: IndexSet, to offset: Int) { store.todos.move(fromOffsets: indexes, toOffset: offset) } private func addTodo() { let newTodo = Todo(title: draft, date: Date(), isDone: false, priority: 0) store.todos.insert(newTodo, at: 0) draft = "" } }
Nous avons maintenant
TodosView , un élément qui utilise le composant
List pour afficher les tâches. Le composant
List fournit également la réorganisation et la suppression. Une autre chose intéressante est la fonction
indexée () . Cette fonction renvoie une collection d'éléments avec ses indices. Nous l'utilisons pour accéder aux articles de la boutique via Binding. Voici la source complète de cette extension.
import Foundation struct IndexedCollection<Base: RandomAccessCollection>: RandomAccessCollection { typealias Index = Base.Index typealias Element = (index: Index, element: Base.Element) let base: Base var startIndex: Index { base.startIndex } var endIndex: Index { base.endIndex } func index(after i: Index) -> Index { base.index(after: i) } func index(before i: Index) -> Index { base.index(before: i) } func index(_ i: Index, offsetBy distance: Int) -> Index { base.index(i, offsetBy: distance) } subscript(position: Index) -> Element { (index: position, element: base[position]) } } extension RandomAccessCollection { func indexed() -> IndexedCollection<Self> { IndexedCollection(base: self) } }
L'environnement est un candidat idéal pour stocker des objets de magasin. L'environnement peut les séparer entre plusieurs vues sans implémentation explicite via la méthode
init . Pour en savoir plus sur les avantages de l'environnement dans SwiftUI, consultez l'article «
Fonctions d'environnement dans SwiftUI ».

Conclusion
Cet article explique comment modéliser l'état d'une application à l'aide de plusieurs objets de
magasin . J'aime vraiment la simplicité de cette approche et la facilité avec laquelle il est possible de faire évoluer votre application en ajoutant plus d'objets de magasin. J'espère que cet article vous a plu.