欢迎回到咱们关于 iCloud Documents 的深入评论。在前文 iCloud Documents 详解:根底设置与文件操作 中,咱们探索了 iCloud Documents 的基本概念、设置步骤和根底的文件操作。本文将在上文根底上继续评论,因而,如果你还未阅览前文,主张你先了解根底知识,以便更好地了解本文的内容。
本文咱们将评论 iCloud Documents 文件夹的独特性质,占位文件的重要性和运用,之外,咱们还将评论与文件操作和调试有关的技巧。
原文宣布在我的博客fatbobman.com 。 由于技术文章需求不断的迭代,当时耗费了不少的精力在不同的平台之间来维持文章的更新。故从 2024 年起,新的文章将只发布在我的博客上。
iCloud Documents 文件夹
虽然同为运用可以访问到的文件夹,iCloud Documents 文件夹相较于运用沙盒内的文件夹( Documents、Application Support 等)仍有几个显着的不同点:
- iCloud Documents 文件夹不属于运用的沙盒规模,它在文件体系的特殊方位,与运用沙盒隔离。
- iCloud Documents 中的部分文件对其他运用也或许是可见或许可同享的,而运用内部的数据默许是私有的。
- iCloud Documents 中的文件默许便是与 iCloud 云端继续同步的,以支持在 Apple 设备之间的文档同享。而运用沙盒内的文件是否同步到 iCloud 取决于运用的设置( 是否敞开云备份 )、文件的方位( Documents 默许同步 )、文件的装备( Application Support 目录中的文件,可以经过 NSURLIsExcludedFromBackupKey 来设置是否同步 )。
- 在满意 iCloud 备份条件( 网络、电量、当时时刻 )时,运用沙盒内可备份的数据会同步到 iCloud 作为备份,该备份仅在下次安装运用时才会起作用。但是 iCloud Documents 中 的文档变更可以近似实时上传并同步给其他设备。
- 当运用被删除后,该运用的沙盒将被体系清空,而 iCloud Documents 中的文件会仍然保留在 iCloud 和用户设备中。
- iCloud Documents 中的文件可以按需下载或开释空间( 文件仍保存在云端 ),沙盒内的文档没有此能力
- iCloud Documents 供给了版本操控和抵触处理机制,有助于在多设备间同步时保护文件的一致性。
因而,开发者乃至运用者,要根据 iCloud Documents 文件夹的特点来决定运用的策略:
- 由于 iCloud Documents 文件夹内的数据都会被同步,因而只应该在 iCloud Documents 文件夹中放置真实需求即时备份、共享的文档数据。
- 考虑到其数据会在本地和云端占用双份空间,开发者应该供给空间开释空间的能力或提示运用者经过体系运用来开释暂时不需求的资源。
- 虽然 iCloud Documents 的同步效率还可以,但它并不适合保存零散数据或增量数据。如有需求,开发者可以考虑运用 CloudKit 供给的其他服务。
- 考虑到用户的云端空间容量或许有限,开发者不该默许一切的数据都会成功上传到云端并同步到其他设备中。
- 为了减少用户云端容量的压力,开发者应该供给将数据转移至非主动同步目录的能力。
什么是占位文件
在云同步服务中,占位文件扮演者重要的角色。比方我在设备 A 上的 iCloud Documents 目录中创立了文件 lesson1.pdf
,设备 B 在收到同步消息后,多数状况下并不会主动下载该文件( 在 macOS 上,如果关闭优化存储空间,体系会主动下载;在 iOS 中,如果文件很小且运用正在运转,有时体系会主动下载 ),设备 B 会在 iCloud Documents 目录相同方位创立一个对应的占位文件。设备 B 上的运用或运用者可以在需求的时候自主挑选从云端下载完好的文件数据。
占位文件供给了一种平衡本地存储约束和即时云端文件访问的办法。经过它,用户可以有效办理他们的存储空间,同时坚持对重要文件的即时访问。
以设备 B 和文件 file1.txt
举例,设备 B 在收到同步告诉后,会在与设备 A 的 lesson1.pdf
相同的文件方位创立一个名为 .lesson1.pdf.icloud
文件。该文件将作为 lesson1.pdf
在设备 B 上的占位文件。
占位文件以 Property List 的办法保存了一些与原始文件有关的信息( 文件名、文件容量、文件类型 ),经过解析后,大致的信息如下:
[
"NSURLNameKey": lesson1.pdf,
"NSURLFileSizeKey": 206739,
"NSURLFileResourceTypeKey": NSURLFileResourceTypeRegular
]
当文件运用或 Finder 发现文件是占位文件时,它仍会以正常的文件名、文件容量显现给用户,但是,会经过图标的办法提示运用者,这个文件没有下载到本地,运用者可以点击下载从云端下载完好版本。相同,关于现已下载到本地的完好文件,运用者也可以经过点击移除下载项来删除本地的完好文件,体系会主动创立一个新的占位文件。
由于占位文件机制的存在,因而关于开发者来说,在对文件进行某些操作前要先应判别文件的占位状况,然后再作出相应的操作。
相同,由于占位文件运用了特殊的名称标记办法,为此,获取文件列表最好的办法,仍是经过前一篇文章介绍的 NSMetaDataQuery。由于,即便开发者不顾忌多进程文件竞赛,运用 fileManager.contentsOfDirectory
获取到的文件名会包含占位标识符( 关于占位文件 ),开发者还需求做特别的处理。
怎么判别文件是否为占位文件
在处理 iCloud Documents 时,正确地辨认占位文件是一个关键步骤。虽然咱们可以经过查看文件名中是否包含特定的占位标识符来进行判别,但这并不是最准确或最牢靠的办法。更科学的做法是运用咱们经过 NSMetadataQuery
取得的文件列表,并查看每个文件的元数据特点来确定其是否为占位文件。
这种办法的优势在于,它基于文件的实践元数据状况,而不仅仅是文件名。为此,咱们在之前定义的 MetadataItemWrapper
结构体中添加了一个 isPlaceholder
特点,用于存储每个文件的占位状况。
以下是相应的 Swift 代码完结:
struct MetadataItemWrapper: Sendable {
....
let isPlaceholder:Bool
init(metadataItem: NSMetadataItem) {
....
if let downloadingStatus = metadataItem.value(forAttribute: NSMetadataUbiquitousItemDownloadingStatusKey) as? String {
if downloadingStatus == NSMetadataUbiquitousItemDownloadingStatusNotDownloaded {
// 文件是占位文件
isPlaceholder = true
} else if downloadingStatus == NSMetadataUbiquitousItemDownloadingStatusDownloaded || downloadingStatus == NSMetadataUbiquitousItemDownloadingStatusCurrent {
// 文件已下载或是最新的
isPlaceholder = false
} else {
isPlaceholder = false
}
} else {
// 默许值,假定文件不是占位文件
isPlaceholder = false
}
}
}
怎么下载文件
所谓的下载文件,是指让体系将占位文件的原始文件从云端下载下来对占位文件进行替换的进程。经过调用 FileManager.default.startDownloadingUbiquitousItem(at: )
,即可触发对特定占位文件的下载操作。为了安全起见,最好仍是经过 NSFileCoordinator 来进行该操作。
以下是一个下载文件的示例办法,它运用了前文创立的 CloudDocumentsHandler
来保证文件下载的安全性和和谐性:
extension CloudDocumentsHandler {
func download(url: URL) throws {
var coordinationError: NSError?
var downloadError: Error?
coordinator.coordinate(writingItemAt: url, options: [.forDownloading], error: &coordinationError) { newURL in
do {
try FileManager.default.startDownloadingUbiquitousItem(at: newURL)
} catch {
downloadError = error
}
}
// 查看下载进程中是否发生了过错
if let error = downloadError {
throw error
}
// 查看和谐进程中是否发生了过错
if let coordinationError = coordinationError {
throw coordinationError
}
}
}
在下载的进程中,体系并不会将没有完结的文件保存在占位文件当时的目录中,只需等到文件彻底下载后,体系才会用完好的文件替换掉占位文件。
对与现已下载完结的文件,再次调用 startDownloadingUbiquitousItem
不会有任何作用。
虽然官方文档描述该办法可以接收目录 URL 作为参数,但在测验中发现
startDownloadingUbiquitousItem
只能用于下载单个文件。
怎么取得下载进展、下载状况、上传状况
下载进展:从文件的元数据 NSMetadataUbiquitousItemPercentDownloadedKey
中可以获取下载进展。这个值( Double )表明文件现已下载的百分比,可以用来追寻下载进展。
下载状况:结合占位状况和下载进展可以判别当时的下载状况。如果文件是占位文件且下载进展大于 0 且小于 100,则可以以为文件正在下载。
上传状况:从文件的元数据 NSMetadataUbiquitousItemPercentUploadedKey
中可以获取上传进展。这个值只需两个状况,0 表明未上传,100 表明已上传完结。
struct MetadataItemWrapper: Sendable {
....
let isDownloading: Bool
let downloadProgress: Double
let uploaded: Bool
init(metadataItem: NSMetadataItem) {
....
// 获取下载进展
downloadProgress = metadataItem.value(forAttribute: NSMetadataUbiquitousItemPercentDownloadedKey) as? Double ?? 0.0
// 如果是占位文件且下载进展大于 0 且小于 100,则以为文件正在下载
isDownloading = isPlaceholder && downloadProgress > 0.0 && downloadProgress < 100.0
// 是否现已上传完毕(只需 0 和 100 两个状况)
uploaded = (metadataItem.value(forAttribute: NSMetadataUbiquitousItemPercentUploadedKey) as? Double ?? 0.0) == 100
}
}
经过这些特点,咱们可以较精准的掌握文件状况,以便更好地办理和监控文件的同步进程并给予用户提示。
怎么开释将已下载的文件所占用的空间
当你需求开释已下载的文件占用的空间将文件变回占位形式时,可以运用 evictUbiquitousItem
办法。这个办法非常类似于触发下载的操作,但它的作用是将已下载的文件变成占位形式,然后开释空间。evictUbiquitousItem
办法可以用于文件和文件夹,当对文件夹履行此操作时,iCloud 会递归地移除文件夹中子项目的副本。
需求特别注意的是,不要运用和谐器(NSFileCoordinator
)履行此操作,由于这样做或许会导致死锁。你可以简略地调用 evictUbiquitousItem
办法来开释已下载文件的空间,而无需额定的和谐。
extension CloudDocumentsHandler {
func evict(url: URL) throws {
do {
try FileManager.default.evictUbiquitousItem(at: url)
} catch {
throw error
}
}
}
怎么移动 iCloud Documents 目录中的文件而不用下载它们
你可以经过运用 FileManager.default.moveItem(at:to:)
办法在 iCloud Documents 目录中移动文件,而不用关系它的占位状况。即便文件是占位文件,只需方针地址也在 iCloud Documents 的目录中,移动后文件仍会坚持占位状况。
以下是一个示例代码,演示怎么在 iCloud Documents 目录中移动文件:
extension CloudDocumentsHandler {
func moveFile(at sourceURL: URL, to destinationURL: URL) throws {
var coordinationError: NSError?
var moveError: Error?
coordinator.coordinate(writingItemAt: sourceURL, options: .forMoving, writingItemAt: destinationURL, options: .forReplacing, error: &coordinationError) { (newSourceURL, newDestinationURL) in
do {
try FileManager.default.moveItem(at: newSourceURL, to: newDestinationURL)
} catch {
moveError = error
}
}
// 查看移动进程中是否发生了过错
if let error = moveError {
throw error
}
// 查看和谐进程中是否发生了过错
if let coordinationError = coordinationError {
throw coordinationError
}
}
}
请注意,关于特定的操作,如移动文件,应保证设置正确的选项(options),以便在移动进程中坚持文件的正确状况。
怎么在不下载文件的状况下重命名文件
只需运用上面用于移动的代码,更改方针名称即可。即便是占位文件,更名后仍会坚持占位状况。
怎么免除文件的同步状况
你可以经过将文件从 iCloud Documents 目录中移动到其他方位( 非 iCloud Documents 目录 )来免除文件的同步状况。即便文件当时处于占位形式,体系也会在移动前主动开始下载文件,并在下载完结后将再将文件移动到新的方位。这个进程或许会有必定的推迟,特别是关于较大的文件。
调试技巧
在开发和调试触及网络同步的功用时,咱们一般面临一个挑战:快速且稳定的网络环境。这种环境虽然理想,但却不利于测验网络同步的边际状况,例如慢速衔接或不稳定网络。此外,在高速网络环境下,某些关键的传输细节和中间状况或许会被快速跳过,然后无法捕捉到。
为了处理这一问题,开发者可以运用苹果公司供给的一个东西:Network Link Conditioner。这个东西答应咱们模仿各种网络条件,如不同的网速、推迟和丢包率,然后创立出更挨近现实生活中的网络环境。
以本文的编撰进程为例,我在测验捕捉 iCloud Documents 的下载进展中间状况时遇到了困难。原因是网络速度过快,使得下载进程在瞬间完结。然而,经过运用 Network Link Conditioner 人为约束网络速度,成功模仿了较慢的下载环境,使得可以清晰地观察和记录下载的每个阶段。
获取 Network Link Conditioner 的办法:
- 在苹果开发者网站上,下载:Additional Tools for Xcode。
- 翻开下载后的
.dmg
文件,找到Hardware/Network Link Conditioner.prefPane
,将其复制到本地,双击安装即可。
- 在体系设置中,挑选或创立一个 Profile ,敞开该功用后,便可完结对当时开发环境的网络操控。
总结
经过前后两篇文章的评论,咱们可以发现,虽然触及诸多细节,但只需咱们细心地处理每一个步骤,并细心调试,将 iCloud Documents 集成到咱们的项目中并不困难。虽然这一进程需求咱们投入必定的时刻和精力,但最终为运用带来的增值和便利是清楚明了的。
苹果公司供给的 CloudKit 服务,可以说是对开发者的一大福音。它使得开发者可以以极低的本钱,为运用供给强壮而灵敏的网络数据同步功用。运用这些功用,将为运用赋予更强的竞赛力和用户吸引力,也为用户带来更好的体验。
您可以在 此处 获取本文的源代码。
订阅我的电子周报 Fatbobman’s Swift Weekly,你将每周及时获取有关 Swift、SwiftUI、CoreData 和 SwiftData 的最新文章和资讯。
原文宣布在我的博客fatbobman.com
欢迎订阅我的公众号:【肘子的Swift记事本】