Извлечение изображения CKAsset из CloudKit очень медленно

Я использую CloudKit в качестве сервера для моего приложения iOS. Я использую его для размещения некоторых относительно статических данных вместе с несколькими изображениями (CKAsset). Я столкнулся с проблемой, когда пришло время для того, чтобы на самом деле получить эти активы из публичной базы данных. Они загружаются с невероятно медленной скоростью.

Мой вариант использования - загрузить изображение в каждую ячейку внутри коллекции. Изображения имеют размер только 200 КБ, но процесс извлечения занимает в среднем 2,2 секунды для завершения загрузки и установки изображения в ячейке. Для сравнения я взял URL-адреса похожих изображений и загрузил их с использованием NSURLSession. Для загрузки каждого изображения потребовалось всего 0,18 - 0,25 секунды.

Я попробовал несколько разных способов загрузки изображений из CK: прямой выбор запроса записи, запроса и операции. Все они имеют схожие результаты. Я также отправляю обратно в основную очередь в блоке завершения до установки изображения для ячейки.

Моя база данных настроена на создание первичного объекта с несколькими полями данных. Затем я устанавливаю систему обратных ссылок для фотографий, где каждая фотография имеет ссылку на основной объект. Таким образом, я могу загружать фотографии по требованию, не забивая основные данные.

Это выглядит примерно так:

Первичный объект: title: String, startDate: Date

Объект фото: owner: String(reference to primary object), image: Asset

Вот пример запроса, который я попробовал напрямую получить одну из фотографий:

let publicDb = CKContainer.defaultContainer().publicCloudDatabase
let configRecordId = CKRecordID(recordName: "e783f542-ec0f-46j4-9e99-b3e3ez505adf")

publicDb.fetchRecordWithID(configRecordId) { (record, error) -> Void in
    dispatch_async(dispatch_get_main_queue()) {
        guard let photoRecord = record else { return }
        guard let asset = photoRecord["image"] as? CKAsset else { return }

        guard let photo = NSData(contentsOfURL: asset.fileURL) else { return }

        let image = UIImage(data: photo)!

        cell.cardImageView.image = image
    }
}

Я не могу понять, почему эти загрузки изображений занимают так много времени, но это действительно отличный showstopper, если я не могу заставить их загружать разумное время.

Обновление: я попробовал операцию выборки с меньшим изображением, 23kb. Выборка была быстрее, где-то от 0,3 до 1,1 секунды. Это лучше, но все же не соответствует ожиданию, которое у меня было для того, что CloudKit должен предоставить.

Ответ 1

Я использую CKQueryOperation. Я обнаружил, что как только я добавил в свой код следующую строку, загрузка CKAssets ускорилась примерно на 5-10 раз.

    queryOperation.qualityOfService = .UserInteractive

Вот мой полный код:

func getReportPhotos(report:Report, completionHandler: (report:Report?, error:NSError?) -> ()) {
    let photo : Photo = report.photos![0] as! Photo
    let predicate : NSPredicate = NSPredicate(format: "recordID = %@", CKRecordID(recordName: photo.identifier!))
    let query : CKQuery = CKQuery(recordType: "Photo", predicate: predicate)
    let queryOperation : CKQueryOperation = CKQueryOperation()
    queryOperation.query = query
    queryOperation.resultsLimit = numberOfReportsPerQuery        
    queryOperation.qualityOfService = .UserInteractive
    queryOperation.recordFetchedBlock = { record in
        photo.date = record.objectForKey("date") as? NSDate
        photo.fileType = record.objectForKey("fileType") as? String
        let asset : CKAsset? = record.objectForKey("image") as? CKAsset
        if asset != nil {
            let photoData : NSData? = NSData(contentsOfURL:asset!.fileURL)
            let photo : Photo = report.photos![0] as! Photo
            photo.image = UIImage(data:photoData!)
        }

    }
    queryOperation.queryCompletionBlock = { queryCursor, error in
        dispatch_async(dispatch_get_main_queue(), {
            completionHandler(report: report, error: error)
        })
    }
    publicDatabase?.addOperation(queryOperation)
}

Ответ 2

Кажется, что что-то замедляет ваш основной поток, который вводит задержку в выполнении блока захвата вашего вызова dispatch_async. Возможно ли, что ваш код вызывает эту функцию для сбора данных несколько раз параллельно? Это приведет к обработке NSData (contentsOfURL: asset.fileURL) для зависания основного потока и введения кумулятивных задержек.

В любом случае, если только в качестве хорошей практики, загрузка изображения с помощью NSData должна выполняться в фоновом режиме, а не в основном потоке.