iOS: шаблон синхронизации CoreData и Remote API - PullRequest
0 голосов
/ 22 сентября 2019

У меня такая проблема.

/ movies? Page = 0 & size = 20

конечная точка API, которая возвращает разбитые на страницы фильмы

Затем я хочу отобразить эти фильмы в UITableView.Так что легко сделать нумерацию страниц и загрузить следующие страницы фильмов.

Но теперь я хочу добавить функциональность кэширования CoreData и шаблон репозитория между ними.Примерно так:

MoviesRepository (для интеграции обеих указанных ниже зависимостей) MoviesDao (для доступа к базовым данным для фильмов) MoviesService (для выполнения удаленных вызовов API)

Я считаю, что для обработки базовых данных следует использовать один источникправда.Поэтому MoviesRepository -> fetchMovies (страница: 2, размер: 10) должны сделать что-то вроде этого:

  1. вызовите MoviesDao для получения [20,30) фильмов из CoreData
  2. сделать запрос наMoviesService -> getMovies (страница: 2, размер: 10)
  3. после получения ответа он должен вызвать что-то вроде MoviesDao -> syncMovies ([remoteMovies])
  4. , тогда должно быть обнаружено изменение Core Data ифильмы в табличном представлении должны быть обновлены (здесь я думаю о некотором наблюдаемом подходе RxSwift или о другом, как например, объединение фреймворка в функции, может быть, некоторые 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
    }
}
...