• 10 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 24/03/2022

Gérez des collections hétérogènes avec les protocoles

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Bienvenue dans cette troisième partie ! Dans cette partie, nous allons améliorer les fonctionnalités de notre TableView. Et dans ce chapitre, nous allons commencer par l'ajout de plusieurs catégories de cadeaux.

Quoi de neuf ?

Je vous propose de prendre quelques minutes pour parcourir les petits changements que j'ai apportés au projet, pour que vous ne soyez pas perdu pour la suite.

La principale modification est l'ajout de la possibilité de créer dans la liste des objets de trois catégories différentes :

  • Voyage (Trip).

  • Livre (Book).

  • Jouet (Toy).

Pour cela, j'ai ajouté une page qui permet de choisir la catégorie, et qui redirige vers trois pages différentes, une pour chaque catégorie d'objet à créer.

Voilà ce que cela donne dans le détail :

Vue

Dans le fichier  Main.storyboard,  vous pouvez voir les nouvelles interfaces que je viens de mentionner.

Des interfaces de noter application  Navigation Controller (flèche) Table View (flèche) Company (3 flèches qui vont à Trop / Book / Toy)
RDV à Main.storyboard

Modèle

J'ai ajouté dans le modèle deux autres structures pour nos nouvelles catégories  Trip  et  Book  .

// Trip.swift
struct Trip {
var departure = ""
var destination = ""
var durationInDays = 0
}
// Book.swift
struct Book {
var title = ""
var author = ""
}

Contrôleur

1/  ListViewController  a à peine changé. J'ai juste rajouté une méthode pour avoir un unwind segue.

@IBAction func unwindToListVC(segue: UIStoryboardSegue) {}

2/ Dans le même esprit, j'ai à peine changé le code  ToyViewController  . Maintenant, il utilise directement l'unwind segue pour revenir directement à la liste lors de l'appui sur le bouton  save  :

@IBAction func save() {
// (...)
performSegue(withIdentifier: "toyToListUnwind", sender: nil)
}

3/ J'ai rajouté deux contrôleurs,  TripViewController  et  BookViewController  , qui permettent respectivement de gérer les pages de création d'objets  Trip  et  Book  . Ils sont extrêmement similaires à  ToyViewController  .

La seule grosse différence est que je n'ai pas su comment ajouter des  Book  ou des  Trip  à notre liste, car ils ne sont pas du type  Toy  . Donc nous allons faire cela ensemble.

4/ Enfin, par acquit de conscience, j'ai ajouté un  CategoryViewController  , mais il est vide, car toute la navigation est faite dans le storyboard directement.

Une liste hétérogène

Maintenant que nous avons notre projet bien en main, attaquons la suite ! Notre objectif est de rajouter dans la liste nos objets  Trip  et  Book  qui, pour l'instant, ne sont pas gérés.

Le problème est qu'il faut donc créer un tableau avec trois types différents :  Toy  ,  Book  et  Trip  . Comme vous le savez, ce n'est pas possible... sauf si vous avez bien suivi la partie 1 de ce cours !

On va utiliser les protocoles !

Bien ! Je vois que certains suivent... ou font semblant  ! Nous allons en effet utiliser les protocoles.

Nous allons créer un protocole  Present  qui va nous permettre de regrouper nos trois types d'objets derrière un même type.

protocol Present {}

Pour l'instant, nous n'avons pas besoin de préciser les exigences de ce protocole. On veut juste un type.

Maintenant, nous allons faire adopter le protocole à nos trois types :

// Trip.swift
extension Trip: Present {}
// Book.swift
extension Book: Present {}
// Toy.swift
extension Toy: Present {}

Désormais, nos trois types adoptent  Present  , et peuvent donc être regroupés derrière ce nouveau type.

Nous pouvons maintenant modifier notre classe  ToyService  pour utiliser  Present  :

// AVANT
class ToyService {
static let shared = ToyService()
private init() {}
private(set) var toys = [Toy]()
func add(toy: Toy) {
toys.append(toy)
}
}
// APRÈS
class PresentService {
static let shared = PresentService()
private init() {}
private(set) var presents = [Present]()
func add(present: Present) {
presents.append(present
}
}

Ma liste est maintenant une liste de cadeaux (  presents  ) et non de jouets. Pour être cohérent, j'ai décidé de modifier le nom de ma classe en  PresentService  .

Nous allons maintenant utiliser cette classe dans nos trois contrôleurs d'ajout d'objets.

// TripViewController.swift
@IBAction func save() {
// (...)
let trip = Trip(departure: departure, destination: destination, durationInDays: Int(daysStepper.value))
PresentService.shared.add(present: trip)
// (...
}
// BookViewController.swift
@IBAction func save() {
// (...)
let book = Book(title: title, author: author)
PresentService.shared.add(present: book)
// (...)
}
// ToyViewController.swift
@IBAction func save() {
// (...)
let toy = Toy(name: name, brand: brand)
PresentService.shared.add(present: toy)
// (...)
}

Et voilà ! Nous avons réussi à remplir notre liste avec des objets de types différents en utilisant un protocole !

Ajouter des exigences

Hé, mais j'ai des erreurs dans mon code !

Ah oui... J'ai oublié  ListViewController  . La boulette ! Cette classe utilise toujours  ToyService  , que nous venons de remplacer par  PresentService  .

Ce n'est pas grave, ce n'est qu'un petit changement rapide. Faisons-le !

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return PresentService.shared.presents.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ToyCell", for: indexPath)
let present = PresentService.shared.presents[indexPath.row]
cell.textLabel?.text = present.name
cell.detailTextLabel?.text = present.brand
return cell
}

Mais j'ai encore des erreurs !

Décidément, je ne suis pas réveillé... En effet, notre type  Present  n'a pas de propriété  name  ou  brand  . Il n'a d'ailleurs pas de propriété du tout. Or, on va en avoir besoin pour remplir notre cellule.

On va donc rajouter deux propriétés à notre protocole  Present  :

protocol Present {
var description: String { get }
var detail: String { get }
}

Ces deux propriétés vont nous permettre d'afficher deux informations sur chaque label de notre cellule :

cell.textLabel?.text = present.description
cell.detailTextLabel?.text = present.detail

Et avant que vous me tombiez dessus avec de nouvelles erreurs, j'anticipe  : maintenant que nous avons rajouté des exigences à notre protocole, il faut que nos trois objets s'y conforment.

L'objectif ici est de répondre à la question : Que veulent dire  description  et detail pour  Trip  ,  Book  et  Toy  ?

// Trip.swift
extension Trip: Present {
var description: String {
return departure + " - " + destination
}
var detail: String {
return "\(durationInDays) Days"
}
}
// Book.swift
extension Book: Present {
var description: String
return title
}
var detail: String {
return author
}
}
// Toy.swift
extension Toy: Present {
var description: String {
return name
}
var detail: String {
return brand
}
}

PAUSE ! Il faut admirer ce que vous venez de faire. Vous avez créé ce que l'on appelle une interface. Votre TableView peut maintenant manipuler un objet  Present  qui a deux propriétés très claires alors qu'en fait, se cachent derrière trois types qui ont des propriétés très différentes les unes des autres.  Present  est donc bien une interface entre les trois types d'un côté et  ListViewController  de l'autre.

À gauche une pile de trois carrés : Struct Trip / Struct Book / Struct Boy. Des doubles flèches relient ces carrés à Protocol Present et une double flèche relie ce dernier au List ViewController.
Present est donc bien une interface entre les trois types d'un côté et ListViewController de l'autre.

Cette méthode est extrêmement utile pour supprimer la dépendance d'une classe envers une autre. Ici,  ListViewController  ne connaît même pas l'existence de  Trip  ,  Book  et  Toy  , car il n'en a pas besoin. Il n'a accès qu'au minimum d'informations que lui fournit  Present  .

En résumé

  • Les protocoles permettent de gérer des collections de type hétérogène.

  • Les protocoles permettent de créer des interfaces qui peuvent uniformiser des types diversifiés, afin de rendre leur utilisation plus simple et d'en supprimer la dépendance.

Dans le prochain chapitre, nous allons sortir nos pinceaux pour magnifier nos cellules qui, pour l'instant, gâchent un peu la beauté de notre interface !

Exemple de certificat de réussite
Exemple de certificat de réussite