首页 > 解决方案 > iOS:CoreData 和 Remote API 同步模式

问题描述

我有这样的问题。

/movies?page=0&size=20

返回分页电影的 api 端点

然后我想在 UITableView 中显示这部电影。所以很容易进行分页和加载电影的下一页。

但现在我想在两者之间添加 CoreData 缓存功能和存储库模式。像这样的东西

MoviesRepository(集成以下两个依赖项) MoviesDao(访问电影的核心数据) MoviesService(进行远程 api 调用)

我认为将核心数据视为单一的事实来源。所以 MoviesRepository -> fetchMovies(page: 2, size: 10) 应该是这样的:

  1. 调用 MoviesDao 从 CoreData 获取 [20,30) 电影
  2. 向 MoviesService 发出请求 -> getMovies(page: 2, size: 10)
  3. 得到响应后,它应该调用类似 MoviesDao -> syncMovies([remoteMovies])
  4. 然后应该检测核心数据更改并更新表视图中的电影(这里我考虑一些 RxSwift 可观察方法或其他类似功能中的组合框架,可能是 som CoreData nofitications 或 FetchResultsController?

但是这里最关键的是将 CoreData 与远程数据同步,因为同时远程数据可能会更改,并且第 2 页不等于核心数据中的第 2 页,以及如何在不重新获取所有数据的情况下解决此问题。

这是我在一些教程中找到的代码,但它替换了所有数据而不是页面。

private func syncFilms(jsonDictionary: [[String: Any]], taskContext: NSManagedObjectContext) -> Bool {
        var successfull = false
        taskContext.performAndWait {
            let matchingEpisodeRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Film")
            let episodeIds = jsonDictionary.map { $0["episode_id"] as? Int }.compactMap { $0 }
            matchingEpisodeRequest.predicate = NSPredicate(format: "episodeId in %@", argumentArray: [episodeIds])

            let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: matchingEpisodeRequest)
            batchDeleteRequest.resultType = .resultTypeObjectIDs

            // Execute the request to de batch delete and merge the changes to viewContext, which triggers the UI update
            do {
                let batchDeleteResult = try taskContext.execute(batchDeleteRequest) as? NSBatchDeleteResult

                if let deletedObjectIDs = batchDeleteResult?.result as? [NSManagedObjectID] {
                    NSManagedObjectContext.mergeChanges(fromRemoteContextSave: [NSDeletedObjectsKey: deletedObjectIDs],
                                                        into: [self.persistentContainer.viewContext])
                }
            } catch {
                print("Error: \(error)\nCould not batch delete existing records.")
                return
            }

            // Create new records.
            for filmDictionary in jsonDictionary {

                guard let film = NSEntityDescription.insertNewObject(forEntityName: "Film", into: taskContext) as? Film else {
                    print("Error: Failed to create a new Film object!")
                    return
                }

                do {
                    try film.update(with: filmDictionary)
                } catch {
                    print("Error: \(error)\nThe quake object will be deleted.")
                    taskContext.delete(film)
                }
            }

            // Save all the changes just made and reset the taskContext to free the cache.
            if taskContext.hasChanges {
                do {
                    try taskContext.save()
                } catch {
                    print("Error: \(error)\nCould not save Core Data context.")
                }
                taskContext.reset() // Reset the context to clean up the cache and low the memory footprint.
            }
            successfull = true
        }
        return successfull
    }
}

标签: ioscore-datapaginationdata-synchronization

解决方案


推荐阅读