Переключение с файла JSON на Core Data
С этой целью сначала определим новое расширение для
BookManagedObject
. Для
этого можно создать новый файл. В Swift считается обычной практикой, ког
-
да расширения определяются в файлах с именами вида:
BookManaged Object+
Extensions.swift
. Внутри этого файла определим новый контроллер результатов,
который по сути является объектом, управляющим результатами выборки из
Core Data. Вот наше расширение:
import Foundation
import CoreData
extension BookManagedObject {
class func newCatalogResultsController(with dataController: DataController,
delegate: NSFetchedResultsControllerDelegate?) >
NSFetchedResultsController {
let request = NSFetchRequest(entityName: "Book")
// Добавить в запрос сортировку по названию
let titleSort = NSSortDescriptor(key: "title", ascending: true)
request.sortDescriptors = [titleSort]
let context = dataController.persistentContainer.viewContext
let fetchedResultsController =
NSFetchedResultsController(fetchRequest: request,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: "catalog.cache")
// Назначить делегата для обработки обновлений
fetchedResultsController.delegate = delegate
do {
try fetchedResultsController.performFetch()
} catch {
fatalError("Catalog fetch could not be completed. \(error)")
}
return fetchedResultsController
}
}
Здесь выполняется довольно много разных операций, но наибольший инте
-
рес представляют лишь некоторые из них. Сначала мы создаем новый запрос на
извлечение данных. Запросы на извлечение – это способ получения объектов
модели из Core Data. Мы указываем, что результаты должны сортироваться по
названию – это, пожалуй, наиболее подходящий способ сортировки для списка
книг. Затем мы записываем в переменную
viewContext
контекст управляемо
-
го объекта, который собираемся использовать для выборки данных. В данном
случае
viewContext
– это контекст, в котором выполняется главный поток. Счи
-
336
Сохранность данных
тается допустимым и даже рекомендуется использовать управляемые объекты
в главном потоке. Однако если бы мы сами писали объекты, то выполняли бы
эти действия в фоновом потоке.
После получения контекста мы создаем объект контроллера результатов
типа
NSFetchedResultsController
. Это специальный объект, используемый в Core
Data для управления извлеченными объектами и передачи их в источники
данных, такие как источник данных табличного представления или источник
данных представления коллекции. Позже мы реализуем в
ListDataSource
под
-
держку протокола делегирования
NSFetchedResultsControllerDelegate
, который
назначается здесь, чтобы дать ему возможность получать обновления и запол
-
нять табличное представление, но сейчас мы просто записываем в свойство
fetchedResultsController
то, что было передано.
Наконец, вызовом
executeFetch()
выполняется фактическая выборка из Core
Data. Этот метод может сгенерировать исключение, поэтому мы заключили
его в блок
try
, в котором перехватываем любые ошибки. В этом примере мы
просто вызываем
fatalError
, но в действующем приложении важно перехва
-
тить и обработать ошибку соответствующим образом или показать сообщение
пользователю.
Теперь воспользуемся этим расширением и подключим наш источник дан
-
ных.
Вернитесь к определению класса
ListDataSource
. Нам нужно внести в него
несколько изменений. Во-первых, нужно удалить свойство
data
, которое мы
использовали для отложенной загрузки файла JSON. Замените это новым свой
-
ством
fetchedResultsController
:
var fetchedResultsController: NSFetchedResultsController?
Во-вторых, нужно добавить новый метод с именем
fetchCatalogResults
, кото
-
рый создаст контроллер результатов, определенный нами только что, и сохра
-
нит его в новом свойстве, добавленном выше:
func fetchCatalogResults(with dataController: DataController) {
fetchedResultsController =
BookManagedObject.newCatalogResultsController(
with: dataController, delegate: nil)
}
В-третьих, добавим новый метод с именем
fetchCatalogResults
, который ини
-
циализирует экземпляр контроллера результатов, созданный перед этим. За
-
тем сохраним этот экземпляр в новом свойстве:
import UIKit
import CoreData
class ListDataSource: NSObject {
var fetchedResultsController: NSFetchedResultsController?
func fetchCatalogResults(with dataController: DataController,
delegate: NSFetchedResultsControllerDelegate?) {
fetchedResultsController =
BookManagedObject.newCatalogResultsController(
with: dataController, delegate: delegate)
Запись книг в хранилище
337
}
func book(for indexPath: IndexPath) > Book? {
return fetchedResultsController?.object(at: indexPath).book
}
}
extension ListDataSource: UITableViewDataSource {
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) > Int {
return fetchedResultsController?.sections?[section].numberOfObjects ?? 0
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) > UITableViewCell {
// Получить ячейку из пула свободных ячеек
let cell =
tableView.dequeueReusableCell(withIdentifier: "CatalogTableViewCell",
for: indexPath)
// Найти книгу, соответствующую заполняемой ячейке
guard let book = fetchedResultsController?.object(at: indexPath) else {
fatalError("Could not retrieve book instance.")
}
// Заполнить метку с заголовком ячейки названием книги
cell.textLabel?.text = book.title
return cell
}
}
Мы использовали
fetchedResultsController
для передачи результатов везде,
где это возможно. Единственное, на что нужно обратить внимание, – в методе
book(for:)
мы получаем
BookMana
gedObject
, а затем преобразуем его с помощью
метода расширения, созданного ранее, в экземпляр
Book
. Мы продолжим ис
-
пользовать экземпляры
Book
повсюду в контроллере представления и в слое
представлений нашего приложения вместо передачи экземпляров управляе
-
мых объектов.
Последнее, что осталось сделать, – переключить контроллер
CatalogViewCon
troller
на использование новых методов источника данных. Это делается вы
-
зовом
dataSource.fetchCatalogResults(with:delegate:)
в свойстве
dataSource
:
lazy var dataSource: ListDataSource = {
let listDataSource = ListDataSource()
let dataController = (UIApplication.shared.delegate as?
AppDelegate)!.dataController!
listDataSource.fetchCatalogResults(with: dataController, delegate: nil)
return listDataSource
}()
Соберите и запустите приложение, и... вы увидите, что каталог пуст!
Все дело в том, что мы создали объекты Core Data, но
не заполнили
их дан
-
ными. С этой целью приложения в iOS часто связывают либо с предварительно
заполненной базой данных, либо с какими-то данными, хранящимися в ин
-
338
Сохранность данных
тернете или внутри пакета приложения. В данном случае мы будем использо
-
вать файл
catalog.json
и заполним базу данных его содержимым, но только если
перед этим базы данных не существовало.
Do'stlaringiz bilan baham: |