• 12 hours
  • Hard

Free online content available in this course.

course.header.alt.is_video

course.header.alt.is_certifying

Got it!

Last updated on 3/22/24

Récupérez les dépenses

Nos dépenses sont maintenant sauvegardées dans Core Data. On va les récupérer pour les afficher dans notre liste.

Récupérez des données

Vous savez déjà récupérer des données, on l'a fait avec Person  précédemment dans notre PeopleRepository. On va donc faire exactement la même chose, cette fois-ci directement dans notre classe SpendingRepository.

Créons donc une méthode getSpendings  dans notre repository :

// MARK: - Repository

   func getSpendings(completion: ([Spending]) -> Void) {
     let request: NSFetchRequest<Spending> = Spending.fetchRequest()
     do {
      let spendings = try coreDataStack.viewContext.fetch(request)
      completion(spendings)
     } catch {
      completion([])
     }
  }

C'est exactement le même code que tout à l'heure pour la classe PeopleRepository  : on récupère la liste des Spendings  enregistrés dans CoreData, et on les retourne dans un tableau grâce à une closure.

Affichez des données

On n'a plus qu'à afficher les données dans la liste. Pour ça, on va commencer par récupérer les données dans une propriété spendings  :

final class ListViewController: UIViewController {

  // MARK: - Properties

  private let spendingsRepository = SpendingsRepository()
  private let settingsRepository = SettingsRepository()
  private var spendings: [Spending] = []

  (....)
}

… que l’on va remplir au moment où la vue s’affichera (donc à viewDidLoad) :

// MARK: - View life cycle

   override func viewWillAppear(_ animated: Bool) {
     super.viewWillAppear(animated)
     spendingsRepository.getSpendings(completion: { [weak self] spendings in
       self?.spendings = spendings
       self?.tableView.reloadData()
     })
  }

Nous allons ensuite utiliser spendings  pour remplir notre Table View :

extension ListViewController: UITableViewDataSource {
  func numberOfSections(in tableView: UITableView) -> Int {
    return 1
  }

  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return spendings.count
  }

     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "SpendingCell", for: indexPath)

    let spending = spendings[indexPath.row]
    cell.textLabel?.text = spending.content
    cell.detailTextLabel?.text = "\(spending.amount) \(settingsRepository.currency)"

    return cell
  }
}

Notre Table View affiche maintenant les données issues de Core Data ! Je suis fier de vous!

Organisez les données

Tout ça, c'est très bien... mais on peut faire mieux ! Notre liste n'est pas du tout organisée. Tout est mélangé, il n'y a pas d'ordre. Ce serait quand même beaucoup mieux si on pouvait organiser nos dépenses par participant et par montant.

L'objectif est le suivant :

Illustration montrant des sections qui seront organisées par participant. Et à l'intérieur, les dépenses vont être rangées par ordre croissant.
Organisons notre liste !

On va créer des sections qui seront organisées par participant. Et à l'intérieur, les dépenses vont être rangées par ordre croissant.

Pour pouvoir organiser la liste ainsi, il faut préalablement que les données le soient. Pour récupérer des données dans un ordre particulier, il faut utiliser la propriété sortDescriptors  de NSFetchRequest.

Il faut fournir à sortDescriptors  un tableau de NSSortDescriptorNSSortDescriptor  permet de préciser la clé avec laquelle on souhaite ranger les données. On l'utilise comme ceci :

NSSortDescriptor(key: "amount", ascending: true),

Ici, je demande de trier les données selon le montant des dépenses par ordre croissant. Je peux aussi fournir comme clé l'attribut d'une autre entité à laquelle je suis relié en utilisant le point :

NSSortDescriptor(key: "person.name", ascending: true),

Ici, je demande de trier dans l'ordre des participants, en utilisant leur nom.

Je peux enfin utiliser la nouvelle forme disponible avec les keyPath. Ce concept nous permet à présent d'atteindre une propriété existante sans avoir à écrire l’élément voulu dans une chaîne de caractères. C’est très pratique, car ça nous évite simplement une éventuelle faute dans la chaîne :

NSSortDescriptor(keyPath: \Spending.amount, ascending: true)

Maintenant, si je mets tout ça ensemble, ça donne :

func getSpendings(completion: ([Spending]) -> Void) {
   let request: NSFetchRequest<Spending> = Spending.fetchRequest()
   request.sortDescriptors = [
     NSSortDescriptor(keyPath: \Spending.person?.name, ascending: true),
     NSSortDescriptor(keyPath: \Spending.amount, ascending: true)
   ]
   do {
     let spendings = try coreDataStack.viewContext.fetch(request)
     completion(spendings)
   } catch {
     completion([])
   }
}

Ici je donne deux NSSortDescriptor  à ma requête. Cela signifie que je veux d'abord que les données soient ordonnées par participant. Et ensuite pour chaque participant, je souhaite obtenir les données rangées par montant croissant.

Organisez la liste

Comme je vais utiliser des sections dans ma liste pour afficher mes données, ce serait pratique si mes données étaient aussi organisées selon des sections. Pour cela, on utilise un tableau de tableaux. En gros, le premier niveau contient les sections, et le deuxième les cellules.

Pour l'instant, on a un tableau de Spending, donc l'objectif est d'en faire un tableau de tableaux de Spending  :

Tableau pour réorganiser les données à l'intérieur du tableau et les mettre dans des tableaux par participant.
Réorganisons nos tableaux de Spending !

On va donc réorganiser les données à l'intérieur du tableau pour les mettre dans des tableaux par participant.

Pour cela, je vais changer la logique montrée au-dessus :

  • Je vais récupérer dans une fetchRequest  la liste des personnes, rangée par ordre alphabétique. 

  • Puis, je vais créer un tableau des tableaux avec la liste des dépenses par personne.

Voici tout d’abord la fonction permettant de retourner un tableau de tableaux de dépenses à partir d’un tableau de personnes. Je vous donne le code pour faire cela, ce n'est pas le plus important. Vous pouvez copier ceci dans votre fichier SpendingsRepository  :

private extension Array where Element == Person {
  var spendingsByPerson: [[Spending]] {
    return self.compactMap {
      ($0.spendings?.allObjects as? [Spending])?.sorted(by: {$0.amount < $1.amount} )
    }
  }
}

Ensuite, je change la logique de notre fonction getSpendings  afin de récupérer en premier lieu la liste des personnes, rangée par ordre alphabétique, puis j’utilise la propriété spendingsByPerson  que l’on vient de créer dans l’extension pour transformer la liste :

func getSpendings(completion: ([[Spending]]) -> Void) {
   let request: NSFetchRequest<Person> = Person.fetchRequest()
   request.sortDescriptors = [
     NSSortDescriptor(keyPath: \Person.name, ascending: true),
   ]
   do {
     let spendings = try coreDataStack.viewContext.fetch(request)
     completion(spendings.spendingsByPerson)
   } catch {
     completion([])
   }
 }

Maintenant, on peut modifier notre Table View pour prendre en compte cette nouvelle organisation de nos données :

extension ListViewController: UITableViewDataSource {
  func numberOfSections(in tableView: UITableView) -> Int {
    return spendings.count // ⇐==
  }

  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return spendings[section].count // ⇐==
  }

      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "SpendingCell", for: indexPath)

    let spending = spendings[indexPath.section][indexPath.row] // ⇐==
    cell.textLabel?.text = spending.content
    cell.detailTextLabel?.text = "\(spending.amount) \(settingsRepository.currency)"

    return cell
  }
}

Ensuite, on peut modifier le titre de nos sections avec la méthode titleForSection  de UITableViewDataSource  .

func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    guard let person = spendings[section].first?.person else { return nil }

    var totalAmount = 0.0
    for spending in spendings[section] {
    totalAmount += spending.amount
    }

    return "\(person.name ?? "🧙‍♂️") \(totalAmount) \(settingsRepository.currency)"
  }

Et voilà ! Vous pouvez tester, et vos données sont maintenant bien organisées !

En résumé

  • On peut utiliser la propriété sortDescriptors  de NSFetchRequest  pour ordonner les données.

  • On peut lui passer plusieurs NSSortDescriptor  pour un tri sur plusieurs niveaux.

C'est tout pour notre démo ! On a fini notre application Cekikapeye ! Bravo ! Dans ce cours, je n'ai eu le temps de vous faire qu'une introduction à Core Data. Mais c'est une technologie très riche et très puissante avec plein de fonctionnalités. Donc j'ai décidé dans le prochain chapitre de vous donner plusieurs aperçus de ses nombreuses possibilités.

Example of certificate of achievement
Example of certificate of achievement