Ios 使用CloudKit完成一项任务

Ios 使用CloudKit完成一项任务,ios,swift,cloudkit,Ios,Swift,Cloudkit,我有一个包含物种和照片的应用程序。我正在向应用程序添加cloudKit。我有一个可行的解决方案,但现在我需要添加一个完成处理程序,就像用户下载包含图像的新物种一样,这需要一些时间(当然取决于图像的数量)。但是,该应用程序允许用户在后台运行时在大部分过程中工作 问题在于,如果图像尚未完全下载,用户选择的物种自然会导致应用程序崩溃 我需要输入一个完成处理程序(或者如果有人有更好的想法),它将允许我使用活动指示器,直到整个流程完成。我找到了一些例子,但它们没有考虑多个下载过程,比如我的图片和缩略图 这

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

问题在于,如果图像尚未完全下载,用户选择的物种自然会导致应用程序崩溃

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

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

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上称之为:

@IBAction func syncCloudToDevice(_ sender: Any) {
    let cloudKit = CloudKit()
    cloudKit.moveSpeciesFromCloud()
    cloudKit.moveFavoritessFromCloud()
}
如果我遗漏了一个细节,请告诉我


任何帮助都将不胜感激。

一般来说,使用
RxSwift
做这类事情都很容易。在
.onSubscribe()
.onTerminated()
中,您分别将活动指示器设置为
/
,当它准备好时,您将在subscriber/observer中获得最终结果。特别是对于
CloudKit
,您可以使用
RxCloudKit
库。

一般来说,使用
RxSwift
做这类事情很容易。在
.onSubscribe()
.onTerminated()
中,您分别将活动指示器设置为
/
,当它准备好时,您将在subscriber/observer中获得最终结果。特别是对于
CloudKit
,您可以使用
RxCloudKit
库。

将图片作为单独的记录类型有什么原因吗?我只想将缩略图和完整照片添加到
物种
记录类型:

缩略图
=字节数据类型(最大1MB)

照片
=资产数据类型(几乎无限)


这样,当您进行初始物种查询时,您将立即获得
缩略图
,然后您可以像当前一样访问
CKAsset
,并在后台下载。不需要第二次查询,这将使您的代码更简单。

您为什么将图片作为单独的记录类型?我只想将缩略图和完整照片添加到
物种
记录类型:

缩略图
=字节数据类型(最大1MB)

照片
=资产数据类型(几乎无限)


这样,当您进行初始物种查询时,您将立即获得
缩略图
,然后您可以像当前一样访问
CKAsset
,并在后台下载。不需要第二次查询,这将使您的代码更简单。

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

我的建议是将您的
执行(u: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 ?? []
    }
}
// ... 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
    }
}
确保您从后台线程调用它,以免阻塞您的UI线程例如:

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 ?? []
    }
}
// ... 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
    }
}

当然,请将这段代码作为如何使用信号量将异步操作转换为同步操作的灵感。这将深入讨论信号量。

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

我的建议是将您的
执行(u: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 ?? []
    }
}
// ... 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
    }
}
确保您从后台线程调用它,以免阻塞您的UI线程例如:

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 ?? []
    }
}
// ... 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
    }
}

当然,请将这段代码作为如何使用信号量将异步操作转换为同步操作的灵感。这将深入讨论信号量。

问题,我没有使用RXSwift的经验,现在我的应用程序没有使用RXSwift,我可以为这个功能添加RXSwift吗?如果你熟悉迦太基,当然可以。但不使用RxSwift是一个很大的缺点,它会使大量的一般性和特定于iOS的复杂性(KVO、GCD、代理等)消失。问题是,我没有RxSwift的经验,现在我的应用程序也不使用RxSwift,我可以只为这个功能添加RxSwift吗?如果你熟悉迦太基,当然可以。但不使用RxSwift是一个很大的缺点,它会消除很多通用和特定于iOS的复杂性(KVO、GCD、代理等)。谢谢Clifton。我面临的挑战是,不同的应用程序(TVos、MacOS)将通过自定义容器访问相同的数据。并非所有应用程序都需要物种数据。这就是为什么我只存储物种ID和照片细节。此外,我可能想将其扩展到一个更大的想法,并认为将它们分开以后可能会有好处。虽然你可能是对的,但我只是想走更安全的路线。谢谢克利夫顿。我面临的挑战是,不同的应用程序(TVos、MacOS)将通过自定义容器访问相同的数据。并非所有应用程序都需要物种数据。这就是为什么我只存储物种ID和照片细节。此外,我可能想将其扩展到一个更大的想法,并认为将它们分开以后可能会有好处。虽然你可能是对的,但我只是想走一条更安全的路线。我真的很喜欢你发布的内容,明天早上会更多地研究它