问题描述
我正在尝试使用 iCloud 备份(不同步)我的应用程序的用户数据和数据库。我想要实现的是按需备份和恢复功能。我创建了一个文件夹结构,如 ubiquityContainerRoot/userId/deviceid/Documents/
和 ubiquityContainerRoot/userId/deviceid/Database/
。这两个文件夹都将包含各种类型的文件。
我需要一种方法来正确监控这些文件上传到 iCloud 的进度,即我需要知道所有文件 ubiquityContainerRoot/userId/deviceid/
何时上传到 iCloud 以及整个进度(从文件夹的角度来看) ubiquityContainerRoot/userId/deviceid/
).
到目前为止我所做的是
- 将用户数据和数据库复制到相应的泛在容器 URL 中
- 创建了一个 NSMetaDataQuery 来监控
NSMetadataQueryUbiquitousDataScope
- 手动跟踪每个文件的进度并在每次查询触发时计算整体进度
我的代码如下:
private var query: NSMetadataQuery?
private let notificationCenter = NotificationCenter.default
private var fileSizeMap = [URL: Double]()
private var progressMap = [URL: Double]()
func test() {
let fileManager = FileManager.default
let documentsSourceDirectory = fileManager
.urls(for: .documentDirectory,in: .userDomainMask).first!
print("documentsSourceDirectory: \(documentsSourceDirectory.path)")
let databaseSourceDirectory = DATABASE_URL
print("databaseSourceDirectory: \(databaseSourceDirectory.path)")
let userId = "USER_ID"
let deviceid = DEVICE_ID
let iCloudContainerRoot = fileManager.url(forUbiquityContainerIdentifier: iCLOUD_IDENITIFiER)!
.appendingPathComponent(userId)
.appendingPathComponent(deviceid)
print("iCloudContainerRoot: \(iCloudContainerRoot.path)")
let documentsClouddirectory = iCloudContainerRoot
.appendingPathComponent("Documents")
let databaseClouddirectory = iCloudContainerRoot
.appendingPathComponent("Database")
do {
try fileManager.copyAndOverwriteItem(at: documentsSourceDirectory,to: documentsClouddirectory)
try fileManager.copyAndOverwriteItem(at: databaseSourceDirectory,to: databaseClouddirectory)
print("copied data to iCloud.")
} catch {
fatalError(error.localizedDescription)
}
fileSizeMap = [:]
progressMap = [:]
populateFileSizeMap(cloudURL: documentsClouddirectory)
populateFileSizeMap(cloudURL: databaseClouddirectory)
createquery()
}
private func createquery() {
let query = NSMetadataQuery()
query.operationQueue = .main
query.searchScopes = [NSMetadataQueryUbiquitousDataScope]
query.predicate = nspredicate(format: "%K LIKE %@",NSMetadataItemFSNameKey,"*")
self.query = query
notificationCenter.addobserver(forName: .NSMetadataQueryDidFinishGathering,object: query,queue: query.operationQueue) {
[weak self] (notification) in
print("NSMetadataQueryDidFinishGathering")
self?.queryDidFire(notification: notification)
}
notificationCenter.addobserver(forName: .NSMetadataQueryDidUpdate,queue: query.operationQueue) {
[weak self] (notification) in
print("NSMetadataQueryDidUpdate")
self?.queryDidFire(notification: notification)
}
query.operationQueue?.addOperation {
print("starting query")
query.start()
query.enableupdates()
}
}
private func queryDidFire(notification: Notification) {
guard let query = notification.object as? NSMetadataQuery else {
print("Can not retrieve query from notification.")
return
}
// without disabling the query when processing,app might crash randomly
query.disableupdates()
print("Result count: \(query.results.count)")
for result in query.results {
if let item = result as? NSMetadataItem {
handeMetadataItem(item)
} else {
print("Not a Meta data item")
}
}
// reenable updates on the query
query.enableupdates()
}
private func handeMetadataItem(_ item: NSMetadataItem) {
if let error = item.value(forAttribute: NSMetadataUbiquitousItemUploadingErrorKey) as? NSError {
print("Item error: \(error.localizedDescription)")
return
}
let srcURL = item.value(forAttribute: NSMetadataItemURLKey) as! URL
print("Item URL: \(srcURL.path)")
if let progress = item.value(forAttribute: NSMetadataUbiquitousItemPercentUploadedKey) as? Double {
print("Item upload progress: \(progress)")
handleProgress(for: srcURL,progress: progress)
}
if let isuploaded = item.value(forAttribute: NSMetadataUbiquitousItemIsuploadedKey) as? Bool,let isuploading = item.value(forAttribute: NSMetadataUbiquitousItemIsuploadingKey) as? Bool
{
print("Item isuploaded: \(isuploaded),isuploading: \(isuploading)")
}
}
private func populateFileSizeMap(cloudURL: URL) {
let fileURLs = try! FileManager.default.contentsOfDirectory(at: cloudURL,includingPropertiesForKeys: nil)
for fileURL in fileURLs {
do {
let properties = try fileURL.resourceValues(forKeys: [.fileSizeKey])
let fileSize = properties.fileSize ?? 0
fileSizeMap[fileURL] = Double(fileSize)
} catch {
fatalError("Can not retrieve file size for file at: \(fileURL.path)")
}
}
}
private func handleProgress(for fileURL: URL,progress: Double) {
guard let fileSize = fileSizeMap[fileURL] else { return }
let prevUploadedSize = progressMap[fileURL] ?? 0
let currUploadedSize = fileSize * (progress / 100.0)
guard currUploadedSize >= prevUploadedSize else {
fatalError("Metadata query reported less upload percentage than before")
}
progressMap[fileURL] = currUploadedSize
let totalSizetoUpload = fileSizeMap.values.reduce(0) { $0 + $1 }
print("totalSizetoUpload: \(totalSizetoUpload)")
let totalSizeUploaded = progressMap.values.reduce(0) { $0 + $1 }
print("totalSizeUploaded: \(totalSizeUploaded)")
let uploadPercentage = (totalSizeUploaded / totalSizetoUpload) * 100
print("uploadPercentage: \(uploadPercentage)")
}
现在我想知道
- 这是使用 iCloud 实施备份/恢复的正确方法(此处未发布恢复代码)还是有更好的 API 来实现这一点?
- 有没有办法使用 NSMetaDataQuery 监控整个文件夹的上传进度?
- 如果用户进入后台并稍后进入前台,我如何知道上传进度,我是否需要部署另一个NSMetaDataQuery(只需创建一个查询而不再次复制数据)来监视更改或有没有办法备份完成后,做一些可以在后台唤醒我的应用的操作吗?
我也面临一些问题,非常感谢您的帮助
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)