Webservice vers CoreData

Bonjour,

j’ai suivi le cours sur le Webservice et je souhaite maintenant intégrer les les données récupérer pour les intégrer à CoreData. Le but est de pouvoir consulter les données même si je suis hors ligne.

Le soucis est que je récupère plusieurs articles en même temps, et je ne sais pas comment l’intégrer dans Coredata. je pense qu’il faut faire une boucle pour intégrer les articles au fur et à mesure mais je ne sais pas comment m’y prendre.

Exemple de json (chaque article est fait de la même façon) :

[
  {
    "id": 321535,
    "date": "2021-06-04T20:38:55",
    "link": "https://xxxx.fr",
    "title": {
      "rendered": "TITRE ARTICLE 1"
    },
    "content": {
      "rendered": "CONTENU ARTICLE 1",
    },
    "comment_status": "open",
    "categories": [
      14
    ],
    "author_meta": {
      "nickname": "JB"
    },
    "amp_enabled": true,
    "thumbnail": "https://xxxx.fr",
    "comments": "0",
    },
   {
    "id": 321535,
    "date": "2021-06-04T20:38:55",
    "link": "https://xxxx.fr",
    "title": {
      "rendered": "TITRE ARTICLE 1"
    },
    "content": {
      "rendered": "CONTENU ARTICLE 1",
    },
    "comment_status": "open",
    "categories": [
      14
    ],
    "author_meta": {
      "nickname": "JB"
    },
    "amp_enabled": true,
    "thumbnail": "https://xxxx.fr",
    "comments": "0",
    },
   ...
]

En utilisant webservice, je recupere le json que transforme en tableau dans une variable (même fonction que dans le cours de Maxime). Ma variable newsList est de type [Post]

Voici struct Post

struct Post: Hashable, Codable, Identifiable {
    var id: Int
    var type: String
    var thumbnail: String
    var date: Date
    var link: String
    var author_meta: Author_meta
    var categories : Categories
    var comment_status: String
    var comments: Int
    //Title is of type struct Title
    var title: Title
    var content: Content
}


//Structure to map Title
struct Title: Hashable, Codable {
    var rendered: String
}
//Structure to map authors meta
struct Author_meta: Hashable, Codable {
    var nickname: String
}
//Structure to map Content
struct Content: Hashable, Codable {
    var rendered: String
}

Pour CoreData j’ai aussi pris le ciurs de maxime que j’ai adapté. le soucis est la passerelle entre les deux car j’ai un tableau de type [Post] mais je ne peux pas l’isnererer comme cela dans CoreData

Merci d’avance pour votre aide.

En suivant mon idée de boucle j’ai un crash systematique avec coreData :

                for number in 0...19 {
                    
                    //print(self.newsList[number].id)
                    
                    dataManager.addNewPost(id: self.newsList[number].id,
                                             type: self.newsList[number].type,
                                             thumbnail: self.newsList[number].thumbnail,
                                             date: self.newsList[number].date,
                                             link: self.newsList[number].link,
                                             author_meta: self.newsList[number].author_meta,
                                             //categories : self.newsList[number].categories,
                                             comment_status: self.newsList[number].comment_status,
                                             comments: self.newsList[number].comments,
                                             title: self.newsList[number].title,
                                             content: self.newsList[number].content)
                    
                }

la fonction addNewPost est la suivante :

func addNewPost (id: Int,
                       type: String,
                       thumbnail: String,
                       date: String,
                       link: String,
                       author_meta: Author_meta,
                       //categories : [Int],
                       comment_status: String,
                       comments: Int,
                       title: Title,
                       content: Content) -> Post {
    
   
let resultat = Post(id: id, type: type, thumbnail: thumbnail, date: date, link: link, author_meta: author_meta, comment_status: comment_status, comments: comments, title: title, content: content)

storage.addNewPostInCoreData(news: resultat) 
    
return resultat

}

et addNewPostInCoreData dans la class CoreDataStorage:

func addNewPostInCoreData(news:Post) {
    let newList = CDIaddict(context: context)
    newList.id = Int16(news.id)
    newList.type = news.type
    //newList.categories = Int16(news.categories[0])
    newList.comment_status = news.comment_status
    newList.comments = Int16(news.comments) 
    newList.content = news.content.rendered
    newList.date = news.date
    newList.link = news.link
    newList.title = news.title.rendered
    newList.author_meta = news.author_meta.nickname
    newList.thumbnail = news.thumbnail

    // On sauvegarde
    saveData()

}

private func saveData() {
    if context.hasChanges {
        do {
            try context.save()
        } catch {
            print("Erreur pendant la sauvegarde CoreData : \(error.localizedDescription)")
        }
    }
}

Dans la console je vois l’erreur suivante « Can’t find mapping model for migration »

Je n’utilise pas CoreData au quotidien mais apparemment ce problème pourrait être lié à Xcode lui même et à sa gestion du cache. Sur SO ils parlent de supprimer et recréer le modèle pour régler ce bug : objective c - Can't find mapping model for migration - UIManagedDocument Core Data Migration - Stack Overflow

Merci @mbritto j’ai reussi à faire marcher.

Voila ma portion de code pour ajouter de nouvelles entrées dans coreData à condition qu’elles n’existent pas. Si elles existent, elles sont mises à jour.

Je ne sais pas si c’est la meilleur méthode, mais cela fonctionne

func addNewPostInCoreData(news:[Post]) {

func addNewPostInCoreData(news:[Post]) {
var i:Int = 0

for _ in news {

    // on vérifie si l'article existe via son ID

        if let existingNews = fetchCDposts(withId: Int32(news[i].id)) {
            //existe on met à jour, on ne crée pas
            print("Màj news")
            
            existingNews.id = Int32(news[i].id)
            existingNews.modified = news[i].modified //dateFormatter.date(from: news[i].modified)
            existingNews.type =news[i].type
            // On sauvegarde
            saveData()

            
        } else {
            //n'existe pas, donc on l'ajoute

            let newList = CDposts(context: context)

            print("Add news")
            
            newList.id = Int32(news[i].id)
            newList.modified = news[i].modified //dateFormatter.date(from: news[i].modified)
            newList.type = news[i].type
            // On sauvegarde
            saveData()
            
            
            
        }
    i += 1
}
    
    
}

private func saveData() {
    if context.hasChanges {
        do {
            try context.save()
            print("SAVED SUCCESS")
        } catch {
            print("Erreur pendant la sauvegarde CoreData : \(error.localizedDescription)")
        }
    }
}

private func fetchCDposts(withId newsId:Int32) -> CDposts? {
    let fetchRequest:NSFetchRequest<CDposts> = CDposts.fetchRequest()
    fetchRequest.predicate = NSPredicate(format: "id == \(newsId)")
    //fetchRequest.fetchLimit = 1
    let fetchResult:[CDposts]? = try? context.fetch(fetchRequest)
    return fetchResult?.first
}

Du coup j’ai mon code qui est bien séparé en 3 parties : parie design, technique et data.

Des améliorations à me suggerer ?