Как выполнить основной запрос на выборку данных в фоновом режиме без зависания интерфейса - PullRequest
0 голосов
/ 21 апреля 2020

Я реализовал данные Core в своем приложении. Каждые 30 секунд в фоновом режиме выполняется вызов API для обновления базы данных из ответа JSON. Поэтому, когда я анализирую и пытаюсь сохранить ответ JSON в моих данных Core, я хочу выполнить его в фоновом режиме. Это не должно влиять на мой интерфейс. В настоящее время, когда происходит сохранение, он блокирует пользовательский интерфейс, поскольку операции с управляемым объектом выполняются в основном потоке. Я следовал подходу из этого блога. enter image description here

Иногда происходит случайный сбой с журналами ниже:

Fatal Exception: NSGenericException
*** Collection <__NSCFSet: 0x283e6a220> was mutated while being enumerated.

1 Ответ

1 голос
/ 22 апреля 2020

Сначала у нас есть правильно настроенный стек CoreData:

    container = NSPersistentContainer(name: "UserData")
    container.persistentStoreDescriptions = [description]
    container.loadPersistentStores(completionHandler: { (description, error) in
        if let error = error as NSError? {
            DataController.isInitialized = false
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
        else {
            DataController.isInitialized = true
        }
    })

    mainContext = container.viewContext
    mainContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
...
    backgroundContext = container.newBackgroundContext()
    backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
...

Во-вторых, у нас есть запрос данных JSON (<- вызывается каждые 30 секунд в главном потоке) </p>

static func request(performSave: Bool, name: String, completionHandler: @escaping (Bool, FooDataResponseResult, Error?) -> Void) {

    var urlComponents = URLComponents()
    ...
    var dataRequest = URLRequest(url: urlComponents.url!)
    dataRequest.httpMethod = "GET"

    let urlRequestCompletionHandler: (Data?, URLResponse?, Error?) -> Void = {
        (data, response, error) in
        guard let data = data, error == nil else {
            // Eroror handling
            return
        }

        if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 {
            print("statusCode should be 200, but is \(httpStatus.statusCode)")
            print("response = \(String(describing: response))")
        }

        let decoder = JSONDecoder()

        do {
            let responseObject = try decoder.decode(FooDataResponse.self, from: data)
            if responseObject.results.count>0 {
                DispatchQueue.global(qos: .userInitiated).async {
                    completionHandler(performSave,responseObject.results[0],nil)
                }
            }
            else {
                // No data
                }
            }
        } catch {
          // Error
        }
    }

    let task = URLSession.shared.dataTask(with: dataRequest, completionHandler: urlRequestCompletionHandler)
    task.resume()
}

Третий completionHandler реализация. Здесь backgroundContext вступает в игру:

    let completionHandlerFooDataRequest: (Bool, FooDataResponseResult?, Error?) -> Void = {

    ...
        let taskContext = AppDelegate.appDelegate.dataController.backgroundContext

        taskContext.performAndWait {
        // all fetches on the taskContext
        // all managed Object creation on taskContext
        // all insert on taskContext
        // dont forget to save your changes
           taskContext.save()
        }

Это только показывает, как выполнить sh обработку JSON запроса, обработку ответа и обновление основных данных в фоновом режиме.

...