mirror of
https://github.com/SagerNet/sing-box-for-apple.git
synced 2026-05-04 11:32:29 +00:00
112 lines
4.0 KiB
Swift
112 lines
4.0 KiB
Swift
import FileProvider
|
|
|
|
class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
|
|
private let directoryURL: URL?
|
|
private let workingDirectory: URL
|
|
private let isWorkingSet: Bool
|
|
|
|
init(url: URL, workingDirectory: URL) {
|
|
directoryURL = url
|
|
self.workingDirectory = workingDirectory
|
|
isWorkingSet = false
|
|
super.init()
|
|
}
|
|
|
|
init(workingSet: Bool, workingDirectory: URL) {
|
|
directoryURL = nil
|
|
self.workingDirectory = workingDirectory
|
|
isWorkingSet = workingSet
|
|
super.init()
|
|
}
|
|
|
|
func invalidate() {}
|
|
|
|
func enumerateItems(for observer: NSFileProviderEnumerationObserver, startingAt _: NSFileProviderPage) {
|
|
guard !isWorkingSet else {
|
|
var allItems: [FileProviderItem] = []
|
|
enumerateRecursively(at: workingDirectory, into: &allItems)
|
|
observer.didEnumerate(allItems)
|
|
observer.finishEnumerating(upTo: nil)
|
|
return
|
|
}
|
|
|
|
guard let url = directoryURL else {
|
|
observer.finishEnumerating(upTo: nil)
|
|
return
|
|
}
|
|
|
|
let contents = (try? FileManager.default.contentsOfDirectory(
|
|
at: url,
|
|
includingPropertiesForKeys: [.isDirectoryKey, .fileSizeKey, .creationDateKey, .contentModificationDateKey],
|
|
options: [.skipsHiddenFiles]
|
|
)) ?? []
|
|
|
|
let items: [FileProviderItem] = contents.map { childURL in
|
|
let identifier = itemIdentifier(for: childURL)
|
|
return FileProviderItem(url: childURL, identifier: identifier, workingDirectory: workingDirectory)
|
|
}
|
|
|
|
observer.didEnumerate(items)
|
|
observer.finishEnumerating(upTo: nil)
|
|
}
|
|
|
|
func enumerateChanges(for observer: NSFileProviderChangeObserver, from _: NSFileProviderSyncAnchor) {
|
|
var allItems: [FileProviderItem] = []
|
|
|
|
if isWorkingSet {
|
|
enumerateRecursively(at: workingDirectory, into: &allItems)
|
|
} else if let url = directoryURL {
|
|
let contents = (try? FileManager.default.contentsOfDirectory(
|
|
at: url,
|
|
includingPropertiesForKeys: nil,
|
|
options: [.skipsHiddenFiles]
|
|
)) ?? []
|
|
|
|
allItems = contents.map { childURL in
|
|
let identifier = itemIdentifier(for: childURL)
|
|
return FileProviderItem(url: childURL, identifier: identifier, workingDirectory: workingDirectory)
|
|
}
|
|
}
|
|
|
|
observer.didUpdate(allItems)
|
|
observer.finishEnumeratingChanges(upTo: currentSyncAnchor(), moreComing: false)
|
|
}
|
|
|
|
func currentSyncAnchor(completionHandler: @escaping (NSFileProviderSyncAnchor?) -> Void) {
|
|
completionHandler(currentSyncAnchor())
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
private func currentSyncAnchor() -> NSFileProviderSyncAnchor {
|
|
let timestamp = Date().timeIntervalSince1970
|
|
return NSFileProviderSyncAnchor(withUnsafeBytes(of: timestamp) { Data($0) })
|
|
}
|
|
|
|
private func itemIdentifier(for url: URL) -> NSFileProviderItemIdentifier {
|
|
let relativePath = url.path.replacingOccurrences(of: workingDirectory.path + "/", with: "")
|
|
if relativePath == url.path || relativePath.isEmpty {
|
|
return .rootContainer
|
|
}
|
|
return NSFileProviderItemIdentifier(relativePath)
|
|
}
|
|
|
|
private func enumerateRecursively(at url: URL, into items: inout [FileProviderItem]) {
|
|
guard let contents = try? FileManager.default.contentsOfDirectory(
|
|
at: url,
|
|
includingPropertiesForKeys: [.isDirectoryKey],
|
|
options: [.skipsHiddenFiles]
|
|
) else { return }
|
|
|
|
for childURL in contents {
|
|
let identifier = itemIdentifier(for: childURL)
|
|
items.append(FileProviderItem(url: childURL, identifier: identifier, workingDirectory: workingDirectory))
|
|
|
|
var isDir: ObjCBool = false
|
|
if FileManager.default.fileExists(atPath: childURL.path, isDirectory: &isDir), isDir.boolValue {
|
|
enumerateRecursively(at: childURL, into: &items)
|
|
}
|
|
}
|
|
}
|
|
}
|