Tuto SwiftUI Chapitre CoreData

Bonjour
je suis le cours Créer des apps iPhone avec SwiftUI (Edition 2021 pour iOS 15) et dans le chapitre « Récupérer la liste des tâches sauvegardées », à 6’ 19" du début de la vidéo, je reproduit une situation identique sur mon éditeur et je n’arrive pas a supprimer l’erreur :


Pourtant la classe Bateau est construite comme la classe Task du tuto:

import Foundation

struct Bateau : Identifiable {
    var id = UUID()
    let nom : String
    let longueur : Double
    let largeur : Double

Mes problèmes sont ils dûs à la version 13.12.1 de xCode ou au à celle de swiftUI ou CoreData ?
Si quelqu’un peu me mettre sur la bonne piste?
Merci d’avance

Bonjour,

Une possible explication, si mes souvenirs sont bons:

  • CoreData ne peut jamais te garantir qu’un enregistrement n’est pas corrompu

  • Quand tu interroges un objet CoreData, tu peux obtenir nil même si la valeur n’est jamais censée être nil

  • C’est bien ce que tu fais en faisant un « unwrap » de coreDataObject.id, il faut donc que tu écrives self.id = id et self.nom = nom

Cordialement,
Nicolas

Merci Nicolas
je ne comprend pas bien.

self.id = id et self.nom = nom
ne fonctionne pas et d’ailleurs les autres champs de la table, qui ne sont pas obligatoires, ne provoquent pas d’erreur.
On dirai que le guard qui est sensé interdire que id et nom soient nuls, n’empêche pas le warning.

Pour info, il n’y a pour l’instant aucune donnée dans CoreData.
Je continue à chercher, merci

C’est à dire ? Peux-tu poster le code et le message d’erreur ?
Nicolas

voilà:

Essaye

guard let objectId = coreDataObject.id, let objectName = coreDataObject.nom else {
return nil 
}
self.id = objectId
self.nom = objectName
etc...

Et sinon, après un test fait sur Playground, reconstruis ton projet, ou quitte Xcode et redémarre

Meme erreur : Variable ‹ self.type › used before being initialized
Je redémarre xCode, mais je l’ai déjà fait …

Je teste ça dans SwiftPlayground, qui compile sans problème…


struct Bateau: Identifiable {
    var id = UUID()
    let nom:String
}

struct ProxyCoreDataBateau {
    let id: UUID?
    let nom: String?
}

extension Bateau {
    init?(fromObject proxyObject:ProxyCoreDataBateau) {
        guard let id = proxyObject.id, let nom = proxyObject.nom else { return nil}
        self.id = id
        self.nom = nom
    }
}

Ok donc on met cela

import Foundation
import CoreData
import UIKit


class CoreDataStorage{
    
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "Bateau")
        container.loadPersistentStores { description, error in
            if let error = error {
                fatalError("Unable to load persistent stores: \(error)")
            }
        }
        return container
    }()
    var context: NSManagedObjectContext {
        return persistentContainer.viewContext
    }
    
    
    
    func fetchBateauList() -> [Bateau] {
        let bateauList:[Bateau]
        let fetchRequest:NSFetchRequest<CDBateau> = CDBateau.fetchRequest()
        if let rawBateauList = try? context.fetch(fetchRequest){
            bateauList = rawBateauList.compactMap({(rawBateau:CDBateau) ->
                Bateau? in
                Bateau(fromCoreDataObject: rawBateau)
            })
        } else {
            bateauList = []
        }
        return bateauList
    }
    func addBateau(bateau:Bateau) {
        let newBateau = CDBateau(context: context)
        newBateau.id = bateau.id
        newBateau.nom = bateau.nom
        newBateau.longueur = bateau.longueur
        newBateau.largeur = bateau.largeur
        newBateau.tirantdeau = bateau.tirantdeau
        newBateau.tirant_dair = bateau.tirant_dair
        newBateau.constructeur = bateau.constructeur
        newBateau.modele = bateau.modele
        newBateau.annee_construction = Int64(bateau.annee_construction)
        newBateau.fuel = Int64(bateau.fuel)
        newBateau.eau = Int64(bateau.eau)
        saveData()
    }
    
    private func saveData(){
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                print("Erreur pendant la sauvegarge CoreData : \(error.localizedDescription)")
            }
        }
    }
}
extension Bateau {
    init?(fromCoreDataObject coreDataObject:CDBateau){
       
        guard let id = coreDataObject.id,
              let nom = coreDataObject.nom else
              {
                  return nil
              }
        self.id = id
        self.nom = nom
        self.longueur = coreDataObject.longueur
        self.largeur = coreDataObject.largeur
        self.tirantdeau = coreDataObject.tirantdeau
        self.tirant_dair = coreDataObject.tirant_dair
        self.constructeur = coreDataObject.constructeur!
        self.modele = coreDataObject.modele!
        self.annee_construction = Int(coreDataObject.annee_construction)
        self.fuel = Int(coreDataObject.fuel)
        self.eau = Int(coreDataObject.eau)
    }
}

ce qui génère une seule erreur à la fin


On est bien d’accord que pour les autres champs on maintient coreDataObject.
le Variable ‹ self.type › used before being initialized semble indiquer qu’on de fait pas les choses dans le bon ordre, peut-être à cause de l’extension?
Je vais continuer à fouiner…

Essaye de supprimer les autres lignes, ou de les remplacer par qq chose comme self.longueur = 3.0

En fait le tuto a été fait avec les 2 champs obligatoires dans la table: id et name, mais les autres champs sont à intégrer aussi…
Je suis débutant sur swift donc je suis un peu perdu.
Je vais laisser tout cela en pause car il y a 2 jours que je tourne en rond avec ce problème que je ne suis pas encore en état de dominer. Cela me laisse un peu songeur de voir qu’une simple relation entre une entité et la base de donnée qui va la mémoriser soit si difficile à mettre en place à cause d’un langage hyper strict.
Merci pour ton aide
Cordialement.

Essaye de déclarer longueur, etc… en var et non en let dans Bateau

Bonsoir,

Je viens de faire le même type de test dans Xcode, et j’obtiens l’alerte Variable ‹ self.xxx › used before being initialized si xxx n’est pas initialisé dans la fonction init.

Vérifie donc que TOUTES les constantes et variables de Bateau sont bien initialisées dans ta fonction init.

Dit autrement, pour la variable « longueur »:

  • si tu ne déclares pas self.longueur dans le init(), tu auras cette erreur
  • si tu déclares self.longueur = 3.0 et que tu fais de même pour toutes les constantes et variables de Bateau(*), le message pourrait disparaître

(*) ou plus exactement les constantes et variables qui n’ont pas une valeur par défaut
À suivre
Nicolas

Le code suivant compile sur Xcode 13.2.1 chez moi:

import Foundation

struct Bateau: Identifiable {
    var id = UUID()
    let nom:String
    let longueur: Double
    let largeur: Double
}

struct ProxyCoreDataBateau {
    let id: UUID?
    let nom: String?
    let longueur: Double
    let largeur: Double?
}

extension Bateau {
    init?(fromObject proxyObject:ProxyCoreDataBateau) {
        guard let id = proxyObject.id, let nom = proxyObject.nom else { return nil}
        self.id = id
        self.nom = nom
        longueur = proxyObject.longueur
        largeur = proxyObject.largeur ?? 0.0
    }
}

Si je supprime l’une des deux lignes « longueur = … » ou « largeur = … », j’ai droit à l’erreur

Nicolas

Bravo Nicolas,
c’était bien une faute d’inattention: variable manquante.
Ça compile bien!
Désolé de t’avoir donné tant de mal, mais pas inutilement puisque apparement il fallait bien enlever le coreDataObject. sur les 2 premieres lignes id et nom, contrairement à ce qui est fait dans le tuto (Peut-être l’effet des dernières versions de SwiftUI).
Merci encore, je vais pouvoir continuer dans mon apprentissage.

Merci @Holliver , content que le problème soit réglé !