我有一个包含物种和照片的应用程序。我正在向应用程序添加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)
}
}
}
}
}
如果我错过了一个细节,请告诉我。
任何帮助将不胜感激。
答案 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
可以很容易地完成某些事情。您分别在on
和off
中将活动指示器设置为.onSubscribe()
/ .onTerminated()
,并在准备好订阅者/观察者时得到最终结果。专门针对CloudKit
,您可以使用RxCloudKit
库。
答案 2 :(得分:1)
是否有理由将图片设为单独的记录类型?我只是将缩略图和完整照片添加到Species
记录类型:
thumbnail
= 字节数据类型(最大1MB)
photo
= 资产数据类型(实际上是无限的)
这样,当您进行初始“物种”查询时,您将立即使thumbnail
可用,然后可以像您当前正在使用的那样访问CKAsset
,它将在后台下载。无需第二次查询,这将使您的代码更简单。