使用CloudKit处理完成

时间:2018-12-10 18:14:13

标签: ios swift cloudkit

我有一个包含物种和照片的应用程序。我正在向应用程序添加cloudKit。我有一个可行的解决方案,但是现在我需要添加一个完成处理程序,就像用户下载包含图像的新物种一样,这需要一些时间(当然取决于多少图像)。但是,该应用程序允许用户在后台运行的大部分过程中进行工作。

问题是,如果图像尚未完全下载,并且用户选择了该类,则应用自然会崩溃。

我需要输入一个完成处理程序(或者如果有人有更好的主意),这将允许我使用活动指示器,直到完成整个过程为止。我找到了一些示例,但是它们没有考虑多个下载过程,例如我的图像和缩略图。

这是我的代码。请注意,我删除了一些不相关的代码以减少显示的数量。

x

这是缩略图的代码(图像是相同的过程)

func moveSpeciesFromCloud() {

    let predicate = NSPredicate(value: true)
    let query = CKQuery(recordType: RemoteRecords.speciesRecord, predicate: predicate)

    CKDbase.share.privateDB.perform(query, inZoneWith: nil) {
        records, error in
        if error != nil {
            print(error!.localizedDescription)
        } else {
            guard let records = records else { return }
            for record in records {

                DispatchQueue.main.async {

                    self.remoteVersion = record[RemoteSpecies.remoteSpeciesVersion] as! Int
                    self.remoteSpeciesID = record[RemoteSpecies.remoteSpeciesID] as! Int
                    self.speciesDetail = AppDelegate.getUserDatabase().getSpeciesDetails(self.remoteSpeciesID)
                    self.localVersion = self.speciesDetail.version

                    // being sure that remote version is newer than local version
                    if self.localVersion >= self.remoteVersion {

                        print("Species version not newer")

                    } else {

                        self.commonNameLabel = record[RemoteSpecies.remoteCommonName] as! String
                        self.speciesLabel = record[RemoteSpecies.remoteSpeciesName] as! String
                        self.genusLabel = record[RemoteSpecies.remoteGenusName] as! String
                        self.groupLabel = record[RemoteSpecies.remoteGroupName] as! String
                        self.subGroupLabel = record[RemoteSpecies.remoteSubGroupName] as! String
                        self.speciesDetailsLabel = record[RemoteSpecies.remoteSpeciesDetails] as! String


                        // Here I sync records to SQLite, but removed code as not relevant.


                        // now syncing Photos, Thumbs, Groups, SubGroups and Favorties

                        self.syncPhotosFromCloud(self.remoteSpeciesID)
                        self.syncThumbsFromCloud(self.remoteSpeciesID)

                    }
                }
            }
        }
    }
}

我在SyncVC上称它为

func syncThumbsFromCloud(_ id: Int) {

    let predicate = NSPredicate(format: "thumbSpeciesID = \(id)")
    let query = CKQuery(recordType: RemoteRecords.thumbsRecord, predicate: predicate)

    CKDbase.share.privateDB!.perform(query, inZoneWith: nil)
    {
        records, error in

        if error != nil {
        print(error!.localizedDescription)
        } else {
            guard let records = records else { return }
            for record in records {
                DispatchQueue.main.async {

                    self.thumbName = (record.object(forKey: RemoteThumbs.remoteThumbName) as? String)!
                    self.thumbID = (record.object(forKey: RemoteThumbs.remoteThumbID) as? Int)!

                    if let asset = record[RemoteThumbs.remoteThumbFile] as? CKAsset,
                        let data = try? Data(contentsOf: (asset.fileURL)),
                        let image = UIImage(data: data)
                    {
                        let filemgr = FileManager.default
                        let dirPaths = filemgr.urls(for: .documentDirectory,
                                                    in: .userDomainMask)

                        let fileURL = dirPaths[0].appendingPathComponent(self.thumbName)

                        if let renderedJPEGData = image.jpegData(compressionQuality: 1.0) {
                            try! renderedJPEGData.write(to: fileURL)
                        }
                    }

                    // syncing records to SQLite
                    AppDelegate.getUserDatabase().syncThumbsFromCloudToSQLite(id: self.thumbID, name: self.thumbName, speciesID: id)
                }
            }
        }
    }
}

如果我错过了一个细节,请告诉我。

任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:2)

我很担心前面的两个答案都无法帮助回答您的问题。一个要求您重组数据库,另一个要求您依赖第三方库。

我的建议是使您的perform(_:inZoneWith:)成为同步操作,以便您可以轻松地一个接一个地执行。例如:

func performSynchronously(query: CKQuery) throws -> [CKRecord] {

    var errorResult: Error?
    var recordsResult: [CKRecord]?
    let semaphore = DispatchSemaphore(value: 0)

    CKDbase.share.privateDB!.perform(query, inZoneWith: nil) { records, error in
        recordsResult = records
        errorResult = error
        semaphore.signal()
    }

    // Block this thread until `semaphore.signal()` occurs
    semaphore.wait()

    if let error = errorResult {
        throw error
    } else {
        return recordsResult ?? []
    }
}

确保您从后台线程调用此命令,以免阻塞您的UI线程!例如:

// ... start your activity indicator

DispatchQueue(label: "background").async {

    do {
        let records1 = try performSynchronously(query: CKQuery...)
        // parse records1
        let records2 = try performSynchronously(query: CKQuery...)
        // parse records2

        DispatchQueue.main.async {
            // stop your activity indicator
        }

    } catch let e {
        // The error e occurred, handle it and stop the activity indicator
    }
}

当然,请仅将此代码用作启发,以了解如何使用信号量将异步操作转换为同步操作。 Here's a good article深入讨论了信号量。

答案 1 :(得分:1)

好吧,一般来说,RxSwift可以很容易地完成某些事情。您分别在onoff中将活动指示器设置为.onSubscribe() / .onTerminated(),并在准备好订阅者/观察者时得到最终结果。专门针对CloudKit,您可以使用RxCloudKit库。

答案 2 :(得分:1)

是否有理由将图片设为单独的记录类型?我只是将缩略图和完整照片添加到Species记录类型:

thumbnail = 字节数据类型(最大1MB)

photo = 资产数据类型(实际上是无限的)

这样,当您进行初始“物种”查询时,您将立即使thumbnail可用,然后可以像您当前正在使用的那样访问CKAsset,它将在后台下载。无需第二次查询,这将使您的代码更简单。