Dans la leçon précédente de Swift From Scratch, nous avons créé une application de tâches fonctionnelle. Le modèle de données pourrait utiliser un peu d'amour, cependant. Dans cette dernière leçon, nous allons refactoriser le modèle de données en implémentant une classe de modèle personnalisée..
Le modèle de données que nous allons implémenter comprend deux classes, une Tâche
classe et un Faire
classe qui hérite de la Tâche
classe. Pendant que nous créons et implémentons ces classes de modèle, nous continuons notre exploration de la programmation orientée objet dans Swift. Dans cette leçon, nous allons zoomer sur l'initialisation d'instances de classe et sur le rôle joué par l'héritage lors de l'initialisation..
Tâche
ClasseCommençons par la mise en œuvre de la Tâche
classe. Créez un nouveau fichier Swift en sélectionnant Nouveau> Fichier… de Xcode Fichier menu. Choisir Fichier rapide du iOS> Source section. Nommez le fichier Task.swift et frapper Créer.
L'implémentation de base est courte et simple. le Tâche
la classe hérite de NSObject
, défini dans le Fondation cadre, et a une propriété variable prénom
de type Chaîne
. La classe définit deux initialiseurs, init ()
et init (nom :)
. Il y a quelques détails qui pourraient vous faire trébucher, alors laissez-moi vous expliquer ce qui se passe.
classe Foundation import tâche: NSObject nom de la variable: substitution de la commodité de la chaîne init () self.init (nom: "Nouvelle tâche") init (nom: Chaîne) self.name = nom
Parce que le init ()
méthode est également définie dans le NSObject
classe, nous devons préfixer l'initialiseur avec le passer outre
mot-clé. Nous avons abordé les méthodes principales plus tôt dans cette série. dans le init ()
méthode, nous invoquons la init (nom :)
méthode, passage "Nouvelle tâche"
comme valeur pour le prénom
paramètre.
le init (nom :)
méthode est un autre initialiseur, acceptant un seul paramètre prénom
de type Chaîne
. Dans cet initialiseur, la valeur du prénom
paramètre est attribué à la prénom
propriété. C'est assez facile à comprendre. Droite?
Ce qui est avec le commodité
mot-clé préfixant le init ()
méthode? Les classes peuvent avoir deux types d'initialiseurs, désigné initialisateurs et commodité initialiseurs. Les initialiseurs de commodité sont précédés du préfixe commodité
mot-clé, ce qui implique que init (nom :)
est un initialiseur désigné. Pourquoi donc? Quelle est la différence entre les initialiseurs désignés et pratiques??
Initialisateurs désignés initialiser complètement une instance d'une classe, ce qui signifie que chaque propriété de l'instance a une valeur initiale après l'initialisation. En regardant le Tâche
classe, par exemple, on voit que le prénom
propriété est définie avec la valeur de la prénom
paramètre de la init (nom :)
initialiseur. Le résultat après l'initialisation est une initialisation complète Tâche
exemple.
Initialisateurs de commodité, cependant, vous vous fiez à un initialiseur désigné pour créer une instance totalement initialisée de la classe. C'est pourquoi le init ()
initialiseur du Tâche
la classe invoque le init (nom :)
initialiseur dans sa mise en œuvre. Ceci est appelé délégation d'initialiseur. le init ()
l’initialisateur délègue l’initialisation à un initialiseur désigné pour créer une instance complètement initialisée du Tâche
classe.
Les initialiseurs de commodité sont facultatifs. Toutes les classes ne disposent pas d'un initialiseur de commodité. Des initialiseurs désignés sont requis et une classe doit avoir au moins un initialiseur désigné pour créer une instance entièrement initialisée.
NSCoding
ProtocoleLa mise en œuvre de la Tâche
la classe n'est pas terminée, cependant. Plus tard dans cette leçon, nous écrirons un tableau de Faire
instances sur le disque. Ceci n’est possible que si des instances du Faire
la classe peut être encodée et décodée.
Ne vous inquiétez pas, ce n'est pas sorcier. Nous avons seulement besoin de faire la Tâche
et Faire
les classes sont conformes à la NSCoding
protocole. C'est pourquoi le Tâche
la classe hérite de la NSObject
classe depuis le NSCoding
protocole ne peut être implémenté que par des classes héritant directement ou indirectement de NSObject
. Comme le NSObject
classe, la NSCoding
le protocole est défini dans le Fondation cadre.
L'adoption d'un protocole est quelque chose que nous avons déjà couvert dans cette série, mais il y a quelques pièges que je veux signaler. Commençons par dire au compilateur que le Tâche
classe conforme à la NSCoding
protocole.
classe Foundation import tâche: NSObject, NSCoding nom var: String…
Ensuite, nous devons implémenter les deux méthodes déclarées dans NSCoding
protocole, init? (codeur :)
et encoder (avec :)
. La mise en œuvre est simple si vous connaissez le NSCoding
protocole.
classe d'importation import Foundation Task: NSObject, NSCoding nom_variable: chaîne @objc obligatoire init? (codeur aDecoder: NSCoder) nom = aDecoder.decodeObject (forKey: "nom") comme! Chaîne @objc func encode (avec aCoder: NSCoder) aCoder.encode (nom, forKey: "nom") remplacement prioritaire init () self.init (nom: "Nouvelle tâche") init (nom: Chaîne) self.name = name
le init? (codeur :)
initializer est un initializer désigné qui initialise un Tâche
exemple. Même si nous mettons en œuvre le init? (codeur :)
méthode pour se conformer à la NSCoding
protocole, vous n’aurez jamais besoin d’appeler cette méthode directement. La même chose est vraie pour encoder (avec :)
, qui code une instance du Tâche
classe.
le Champs obligatoires
mot-clé préfixant le init? (codeur :)
méthode indique que chaque sous-classe du Tâche
la classe doit implémenter cette méthode. le Champs obligatoires
mot clé s'applique uniquement aux initialiseurs, c'est pourquoi nous n'avons pas besoin de l'ajouter à la encoder (avec :)
méthode.
Avant de continuer, nous devons parler de la @objc
attribut. Parce que le NSCoding
protocole est un protocole Objective-C, conformité du protocole ne peut être vérifié qu'en ajoutant le @objc
attribut. Dans Swift, la conformité de protocole et les méthodes de protocole facultatives n’existent pas. En d’autres termes, si une classe adhère à un protocole particulier, le compilateur vérifie et attend que chaque méthode du protocole soit implémentée..
Faire
ClasseAvec le Tâche
classe mise en œuvre, il est temps de mettre en œuvre le Faire
classe. Créez un nouveau fichier Swift et nommez-le ToDo.swift. Regardons la mise en œuvre de la Faire
classe.
classe Foundation à faire: tâche var done: Bool @objc requis init? (codeur aDecoder: NSCoder) self.done = aDecoder.decodeBool (forKey: "done") super.init (codeur: aDecoder) @objc override func encode (avec aCoder: NSCoder) aCoder.encode (done, forKey: "done") super.encode (avec: aCoder) init (nom: String, done: Bool) self.done = done super.init (nom : prénom)
le Faire
la classe hérite de la Tâche
class et déclare une propriété de variable terminé
de type Bool
. En plus des deux méthodes requises de la NSCoding
protocole qu'il hérite de la Tâche
classe, il déclare également un initialiseur désigné, init (nom: fait :)
.
Comme dans Objective-C, le super
mot-clé fait référence à la superclasse, la Tâche
classe dans cet exemple. Il y a un détail important qui mérite l'attention. Avant d'invoquer le init (nom :)
méthode sur la superclasse, chaque propriété déclarée par le Faire
la classe doit être initialisée. En d'autres termes, avant la Faire
classe délègue l’initialisation à sa super-classe, chaque propriété définie par le Faire
la classe doit avoir une valeur initiale valide. Vous pouvez le vérifier en inversant l'ordre des instructions et en inspectant l'erreur qui apparaît..
La même chose s'applique à la init? (codeur :)
méthode. Nous initialisons d'abord le terminé
propriété avant d'appeler init? (codeur :)
sur la superclasse.
Lors du traitement de l'héritage et de l'initialisation, il convient de garder quelques règles à l'esprit. La règle pour les initialiseurs désignés est simple.
Faire
classe, par exemple, le init? (codeur :)
méthode invoque le init? (codeur :)
méthode de sa super-classe. Ceci est également appelé déléguer.Les règles relatives aux initialiseurs de commodité sont un peu plus complexes. Il y a deux règles à garder à l'esprit.
Tâche
classe, par exemple, le init ()
la méthode est un initialiseur de commodité et délègue l'initialisation à un autre initialiseur, init (nom :)
dans l'exemple. Ceci est connu comme déléguer à travers.Avec les deux classes de modèle implémentées, il est temps de refactoriser la ViewController
et AddItemViewController
Des classes. Commençons par ce dernier.
AddItemViewController
AddItemViewControllerDelegate
ProtocoleLes seuls changements que nous devons faire dans le AddItemViewController
classe sont liés à la AddItemViewControllerDelegate
protocole. Dans la déclaration de protocole, changez le type de didAddItem
de Chaîne
à Faire
, la classe de modèle que nous avons implémentée plus tôt.
protocol AddItemViewControllerDelegate contrôleur func (contrôleur _: AddItemViewController, didAddItem: À faire)
créer(_:)
actionCela signifie que nous devons également mettre à jour le créer(_:)
action dans laquelle nous appelons la méthode déléguée. Dans la mise à jour, nous créons un Faire
par exemple, en le passant à la méthode déléguée.
@IBAction func create (_ expéditeur: Any) if let nom = textField.text // Créer un élément let item = ToDo (nom: nom, done: false) // Notifier le délégué délégué? .Controller (self, didAddItem: item )
ViewController
articles
Propriétéle ViewController
la classe nécessite un peu plus de travail. Nous devons d’abord changer le type de articles
propriété à [Faire]
, un étalage de Faire
les instances.
Articles var: [ToDo] = [] didSet (oldValue) let hasItems = items.count> 0 tableView.isHidden =! hasItems messageLabel.isHidden = hasItems
Cela signifie également que nous devons repenser quelques autres méthodes, telles que la tableView (_: cellForRowAt :)
méthode indiquée ci-dessous. Parce que le articles
tableau contient maintenant Faire
Il est beaucoup plus simple de vérifier si un élément est marqué comme terminé. Nous utilisons l'opérateur conditionnel ternaire de Swift pour mettre à jour le type d'accessoire de la cellule de vue tableau.
func tableView (_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell // Extraire l'élément item = éléments [indexPath.row] // Dequeue Cell laisse cell = tableView.dequeueReusableCell (withIdentifier: "TableViewCell", pour: indexPath ) // Configurez la cellule cell.textLabel? .Text = item.name cell.accessoryType = item.done? .checkmark: aucune cellule de retour.
Lorsque l'utilisateur supprime un élément, il suffit de mettre à jour le articles
propriété en supprimant le correspondant Faire
exemple. Cela se reflète dans la mise en œuvre de la tableView (_: commit: forRowAt :)
méthode montrée ci-dessous.
func tableView (_ tableView: UITableView, commettent une édition de style. UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) if éditerStyle == .delete // Mettre à jour les éléments items.remove (à: indexPath.row) // View Table View tableView.deleteRows (at : [indexPath], avec: .right) // Save State saveItems ()
Mise à jour de l’état d’un élément lorsque l’utilisateur appuie sur une ligne gérée dans tableView (_: didSelectRowAt :)
méthode. La mise en œuvre de cette UITableViewDelegate
la méthode est beaucoup plus simple grâce à la Faire
classe.
func tableView (_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) tableView.deselectRow (sous: indexPath, animé: true) // Récupération d'un élément, élément = éléments [indexPath.row] // Mise à jour d'un élément, élément.done =! élément. done // Fetch Cell let cell = tableView.cellForRow (à: indexPath) // Mise à jour de la cellule? .accessoryType = item.done? .checkmark: .none // Etat de sauvegarde saveItems ()
Le correspondant Faire
instance est mise à jour et cette modification est reflétée par la vue tabulaire. Pour sauver l'état, nous invoquons saveItems ()
au lieu de saveCheckedItems ()
.
Parce que nous avons mis à jour le AddItemViewControllerDelegate
protocole, nous devons également mettre à jour le ViewController
la mise en œuvre de ce protocole. Le changement, cependant, est simple. Il suffit de mettre à jour la signature de la méthode.
contrôleur func (contrôleur _: AddItemViewController, didAddItem: à faire) // Mettre à jour le source items.append (didAddItem) // Save State saveItems () // Recharger la vue de table tableView.reloadData () // Dismiss Ajouter un contrôleur de vue d'élément animé: vrai)
pathForItems ()
MéthodeAu lieu de stocker les éléments dans la base de données par défaut des utilisateurs, nous allons les stocker dans le répertoire de documents de l'application. Avant de mettre à jour le loadItems ()
et saveItems ()
méthodes, nous allons implémenter une méthode d'assistance nommée pathForItems ()
. La méthode est privée et renvoie un chemin d'accès, l'emplacement des éléments dans le répertoire de documents.
private func pathForItems () -> String guard laissez documentsDirectory = NSSearchPathForDirectoriesInDomains (.documentDirectory, .userDomainMask, true) .first, laissez url = URL (chaîne: documentsDirectory) else fatalError ("Répertoire de documents non trouvé") return URL. appendingPathComponent ("items"). path
Nous allons d’abord chercher le chemin du répertoire de documents dans le sandbox de l’application en appelant NSSearchPathForDirectoriesInDomains (_: _: _ :)
. Comme cette méthode retourne un tableau de chaînes, nous saisissons le premier élément.
Notez que nous utilisons un garde
déclaration pour vous assurer que la valeur retournée par NSSearchPathForDirectoriesInDomains (_: _: _ :)
est valable. Nous jetons une erreur fatale si cette opération échoue. Cela met immédiatement fin à l'application. Pourquoi faisons-nous cela? Si le système d'exploitation ne parvient pas à nous transmettre le chemin d'accès au répertoire de documents, nous avons de plus gros problèmes à nous inquiéter..
La valeur de retour pathForItems ()
est composé du chemin du répertoire des documents avec la chaîne "articles"
annexé.
loadItems ()
MéthodeLa méthode loadItems change un peu. Nous stockons d’abord le résultat de pathForItems ()
dans une constante, chemin
. Nous désarchivons ensuite l’objet archivé sur ce chemin et le descendons en un tableau facultatif de Faire
les instances. Nous utilisons une liaison facultative pour déballer cette option et l'assigner à une constante., articles
. dans le si
clause, nous affectons la valeur stockée dans articles
au articles
propriété.
func privé loadItems () let path = pathForItems () si let items = NSKeyedUnarchiver.unarchiveObject (withFile: path) as? [ToDo] self.items = items
saveItems ()
Méthodele saveItems ()
la méthode est courte et simple. Nous stockons le résultat de pathForItems ()
dans une constante, chemin
, et invoquer archiveRootObject (_: toFile :)
sur NSKeyedArchiver
, en passant dans le articles
propriété et chemin
. Nous imprimons le résultat de l'opération sur la console.
fonction privée saveItems () let path = pathForItems () si NSKeyedArchiver.archiveRootObject (self.items, toFile: path) print ("Enregistré avec succès") else print ("Enregistrement échoué")
Terminons par la partie amusante, la suppression de code. Commencez par enlever le éléments vérifiés
propriété au sommet puisque nous n'en avons plus besoin. En conséquence, nous pouvons également supprimer le loadCheckedItems ()
et saveCheckedItems ()
méthodes, et chaque référence à ces méthodes dans le ViewController
classe.
Générez et exécutez l'application pour voir si tout fonctionne toujours. Le modèle de données rend le code de l'application beaucoup plus simple et plus fiable. Grace à Faire
classe, la gestion des éléments de notre liste est beaucoup plus facile et moins sujette aux erreurs.
Dans cette leçon, nous avons remanié le modèle de données de notre application. Vous en avez appris davantage sur la programmation orientée objet et l'héritage. L’initialisation d’instance est un concept important dans Swift, alors assurez-vous de bien comprendre ce que nous avons décrit dans cette leçon. Vous pouvez en savoir plus sur l’initialisation et la délégation d’initialiseur dans Swift Programming Language.
En attendant, consultez certains de nos autres cours et tutoriels sur le développement iOS en langage Swift!