Swift开发:如何列出iPhone及iCloud Drive的PDF/文本文件?代码返回nil
解决获取设备及iCloud Drive中PDF/文本文件的问题
首先,咱们先拆解你现有代码返回nil的核心问题,再一步步实现完整需求:
一、原代码返回Nil的关键原因
- 错误被静默吞掉:你用
try?调用contentsOfDirectory,如果读取目录时遇到权限不足、目录不存在等问题,会直接返回nil,但你看不到具体错误原因,没法排查问题。 - 缺乏安全判断:直接取
urls(for:directory, in:.userDomainMask)[0],如果这个数组为空(虽然概率极低,但严谨性不足),会直接触发崩溃。 - 仅覆盖本地目录:你的代码只处理了本地
documentDirectory,完全没涉及iCloud Drive的内容,不符合你“设备及iCloud Drive”的需求。
二、修复并实现完整需求的代码
第一步:修复本地目录读取,添加错误捕获
先修改FileManager扩展,让它能抛出错误,方便我们定位问题:
extension FileManager { func urls(for directory: FileManager.SearchPathDirectory, skipsHiddenFiles: Bool = true) throws -> [URL] { guard let baseURL = urls(for: directory, in: .userDomainMask).first else { throw NSError(domain: "FileManagerError", code: -1, userInfo: [NSLocalizedDescriptionKey: "指定目录不存在"]) } let options: FileManager.DirectoryEnumerationOptions = skipsHiddenFiles ? .skipsHiddenFiles : [] return try contentsOfDirectory(at: baseURL, includingPropertiesForKeys: nil, options: options) } }
调用时用do-catch捕获错误:
do { let localFiles = try FileManager.default.urls(for: .documentDirectory) print("本地文件列表:\(localFiles)") } catch { print("读取本地目录出错:\(error.localizedDescription)") }
第二步:添加iCloud Drive文件读取能力
要访问iCloud Drive,先完成项目配置:
- 打开Xcode项目的
Signing & Capabilities,添加iCloud能力,勾选iCloud Documents - 在
Info.plist里添加NSUbiquitousContainers配置,指定你的iCloud容器ID(格式一般是iCloud.你的BundleID)
然后添加读取iCloud Drive的方法:
extension FileManager { func iCloudDriveDocumentsURL() -> URL? { return url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents") } func urlsIniCloudDrive(skipsHiddenFiles: Bool = true) throws -> [URL] { guard let iCloudURL = iCloudDriveDocumentsURL() else { throw NSError(domain: "FileManagerError", code: -2, userInfo: [NSLocalizedDescriptionKey: "未开启iCloud Drive或权限不足"]) } let options: FileManager.DirectoryEnumerationOptions = skipsHiddenFiles ? .skipsHiddenFiles : [] return try contentsOfDirectory(at: iCloudURL, includingPropertiesForKeys: nil, options: options) } }
第三步:过滤PDF和文本文件
添加筛选逻辑,只保留.pdf、.txt(可按需添加.md等其他文本格式)的文件:
func filterPDFAndTextFiles(from urls: [URL]) -> [URL] { let validExtensions = Set(["pdf", "txt", "md"]) // 可根据需求扩展格式 return urls.filter { url in guard let ext = url.pathExtension.lowercased() else { return false } return validExtensions.contains(ext) } }
第四步:整合调用逻辑
// 读取本地PDF/文本文件 do { let localFiles = try FileManager.default.urls(for: .documentDirectory) let localTargetFiles = filterPDFAndTextFiles(from: localFiles) print("本地PDF/文本文件:\(localTargetFiles)") } catch { print("读取本地目录失败:\(error)") } // 读取iCloud Drive的PDF/文本文件 do { let iCloudFiles = try FileManager.default.urlsIniCloudDrive() let iCloudTargetFiles = filterPDFAndTextFiles(from: iCloudFiles) print("iCloud Drive的PDF/文本文件:\(iCloudTargetFiles)") } catch { print("读取iCloud Drive失败:\(error)") }
三、额外注意事项
- 权限请求:第一次访问iCloud Drive时,系统会自动弹出权限请求,用户需要允许才能访问
- 递归遍历子目录:如果需要读取子文件夹里的文件,把
contentsOfDirectory换成enumerator(at:includingPropertiesForKeys:options:errorHandler:)实现递归遍历 - iCloud同步状态:iCloud Drive的文件可能处于同步中,可通过
URLResourceKey.isUbiquitousItemKey和URLResourceKey.ubiquitousItemDownloadingStatusKey检查同步状态,确保文件已下载到本地再处理
内容的提问来源于stack exchange,提问作者Mr Abbasi




