Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xcode 16 build #12

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 79 additions & 84 deletions Sources/SwiftCloudDrive/CoordinatedFileManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,135 +21,130 @@ public actor CoordinatedFileManager {
public init(presenter: (any NSFilePresenter)? = nil) {
self.presenter = presenter
}
public func fileExists(coordinatingAccessAt fileURL: URL) throws -> (exists: Bool, isDirectory: Bool) {
var isDir: ObjCBool = false
var exists: Bool = false
try coordinate(readingItemAt: fileURL) { [self] url in
exists = fileManager.fileExists(atPath: url.path, isDirectory: &isDir)

public func fileExists(coordinatingAccessAt fileURL: URL) async throws -> (exists: Bool, isDirectory: Bool) {
return try await coordinate(readingItemAt: fileURL, options: []) { [self] url -> (Bool, Bool) in
var isDir: ObjCBool = false
let exists = fileManager.fileExists(atPath: url.path, isDirectory: &isDir)
return (exists, isDir.boolValue)
}
return (exists, isDir.boolValue)
}

public func createDirectory(coordinatingAccessAt dirURL: URL, withIntermediateDirectories: Bool) throws {
try coordinate(writingItemAt: dirURL, options: .forMerging) { [self] url in
public func createDirectory(coordinatingAccessAt dirURL: URL, withIntermediateDirectories: Bool) async throws {
try await coordinate(writingItemAt: dirURL, options: .forMerging) { [self] url in
try fileManager.createDirectory(at: url, withIntermediateDirectories: withIntermediateDirectories)
}
}

public func removeItem(coordinatingAccessAt dirURL: URL) throws {
try coordinate(writingItemAt: dirURL, options: .forDeleting) { [self] url in
public func removeItem(coordinatingAccessAt dirURL: URL) async throws {
try await coordinate(writingItemAt: dirURL, options: .forDeleting) { [self] url in
try fileManager.removeItem(at: url)
}
}

public func copyItem(coordinatingAccessFrom fromURL: URL, to toURL: URL) throws {
try coordinate(readingItemAt: fromURL, readOptions: [], writingItemAt: toURL, writeOptions: .forReplacing) { readURL, writeURL in
public func copyItem(coordinatingAccessFrom fromURL: URL, to toURL: URL) async throws {
try await coordinate(readingItemAt: fromURL, readOptions: [], writingItemAt: toURL, writeOptions: .forReplacing) { readURL, writeURL in
try fileManager.copyItem(at: readURL, to: writeURL)
}
}

public func contentsOfDirectory(coordinatingAccessAt dirURL: URL, includingPropertiesForKeys keys: [URLResourceKey]?, options mask: FileManager.DirectoryEnumerationOptions) throws -> [URL] {
var contentsURLs: [URL] = []
try coordinate(readingItemAt: dirURL) { [self] url in
contentsURLs = try fileManager.contentsOfDirectory(at: url, includingPropertiesForKeys: keys, options: mask)
public func contentsOfDirectory(coordinatingAccessAt dirURL: URL, includingPropertiesForKeys keys: [URLResourceKey]?, options mask: FileManager.DirectoryEnumerationOptions) async throws -> [URL] {
return try await coordinate(readingItemAt: dirURL, options: []) { [self] url -> [URL] in
return try fileManager.contentsOfDirectory(at: url, includingPropertiesForKeys: keys, options: mask)
}
return contentsURLs
}

public func contentsOfFile(coordinatingAccessAt url: URL) throws -> Data {
var data: Data = .init()
try coordinate(readingItemAt: url) { url in
data = try Data(contentsOf: url)
public func contentsOfFile(coordinatingAccessAt url: URL) async throws -> Data {
return try await coordinate(readingItemAt: url, options: []) { url -> Data in
return try Data(contentsOf: url)
}
return data
}

public func write(_ data: Data, coordinatingAccessTo url: URL) throws {
try coordinate(writingItemAt: url) { url in
public func write(_ data: Data, coordinatingAccessTo url: URL) async throws {
try await coordinate(writingItemAt: url, options: []) { url in
try data.write(to: url)
}
}

public func updateFile(coordinatingAccessTo url: URL, in block: @Sendable @escaping (URL) throws -> Void) throws {
try coordinate(writingItemAt: url) { url in
try block(url)
}
}

public func readFile(coordinatingAccessTo url: URL, in block: @Sendable @escaping (URL) throws -> Void) throws {
try coordinate(readingItemAt: url) { url in
public func updateFile(coordinatingAccessTo url: URL, in block: @Sendable @escaping (URL) throws -> Void) async throws {
try await coordinate(writingItemAt: url, options: []) { url in
try block(url)
}
}

private var executionBlock: ((URL) throws -> Void)?
private func execute(onSecurityScopedResource url: URL) throws {
guard let executionBlock else { fatalError() }
let shouldStopAccessing = url.startAccessingSecurityScopedResource()
defer {
if shouldStopAccessing {
url.stopAccessingSecurityScopedResource()
}
public func readFile(coordinatingAccessTo url: URL, in block: @Sendable @escaping (URL) throws -> Void) async throws {
try await coordinate(readingItemAt: url, options: []) { url in
try block(url)
}
try executionBlock(url)
self.executionBlock = nil
}

private func coordinate(readingItemAt url: URL, options: NSFileCoordinator.ReadingOptions = [], with block: @escaping (URL) throws -> Void) throws {
private func coordinate<T>(readingItemAt url: URL, options: NSFileCoordinator.ReadingOptions = [], with block: @escaping (URL) throws -> T) async throws -> T {
var coordinatorError: NSError?
var managerError: Swift.Error?
let coordinator = NSFileCoordinator(filePresenter: presenter)
executionBlock = block
coordinator.coordinate(readingItemAt: url, options: options, error: &coordinatorError) { url in
do {
try execute(onSecurityScopedResource: url)
} catch {
managerError = error
let result: T = try await withCheckedThrowingContinuation { continuation in
let coordinator = NSFileCoordinator(filePresenter: presenter)
coordinator.coordinate(readingItemAt: url, options: options, error: &coordinatorError) { url in
do {
let shouldStopAccessing = url.startAccessingSecurityScopedResource()
defer {
if shouldStopAccessing {
url.stopAccessingSecurityScopedResource()
}
}
let res = try block(url)
continuation.resume(returning: res)
} catch {
continuation.resume(throwing: error)
}
}
if let coordinatorError = coordinatorError {
continuation.resume(throwing: coordinatorError)
}
}
guard coordinatorError == nil else { throw coordinatorError! }
guard managerError == nil else { throw managerError! }
return result
}

private func coordinate(writingItemAt url: URL, options: NSFileCoordinator.WritingOptions = [], with block: @escaping (URL) throws -> Void) throws {
private func coordinate<T>(writingItemAt url: URL, options: NSFileCoordinator.WritingOptions = [], with block: @escaping (URL) throws -> T) async throws -> T {
var coordinatorError: NSError?
var managerError: Swift.Error?
let coordinator = NSFileCoordinator(filePresenter: presenter)
executionBlock = block
coordinator.coordinate(writingItemAt: url, options: options, error: &coordinatorError) { url in
do {
try execute(onSecurityScopedResource: url)
} catch {
managerError = error
let result: T = try await withCheckedThrowingContinuation { continuation in
let coordinator = NSFileCoordinator(filePresenter: presenter)
coordinator.coordinate(writingItemAt: url, options: options, error: &coordinatorError) { url in
do {
let shouldStopAccessing = url.startAccessingSecurityScopedResource()
defer {
if shouldStopAccessing {
url.stopAccessingSecurityScopedResource()
}
}
let res = try block(url)
continuation.resume(returning: res)
} catch {
continuation.resume(throwing: error)
}
}
if let coordinatorError = coordinatorError {
continuation.resume(throwing: coordinatorError)
}
}
guard coordinatorError == nil else { throw coordinatorError! }
guard managerError == nil else { throw managerError! }
return result
}

private func coordinate(readingItemAt readURL: URL, readOptions: NSFileCoordinator.ReadingOptions = [], writingItemAt writeURL: URL, writeOptions: NSFileCoordinator.WritingOptions = [], with block: (_ readURL: URL, _ writeURL: URL) throws -> Void) throws {
private func coordinate<T>(readingItemAt readURL: URL, readOptions: NSFileCoordinator.ReadingOptions = [], writingItemAt writeURL: URL, writeOptions: NSFileCoordinator.WritingOptions = [], with block: (_ readURL: URL, _ writeURL: URL) throws -> T) async throws -> T {
var coordinatorError: NSError?
var managerError: Swift.Error?
let coordinator = NSFileCoordinator(filePresenter: presenter)
coordinator.coordinate(readingItemAt: readURL, options: readOptions, writingItemAt: writeURL, options: writeOptions, error: &coordinatorError) { (read: URL, write: URL) in
do {
let shouldStopAccessingRead = read.startAccessingSecurityScopedResource()
let shouldStopAccessingWrite = write.startAccessingSecurityScopedResource()
defer {
if shouldStopAccessingRead {
read.stopAccessingSecurityScopedResource()
}
if shouldStopAccessingWrite {
write.stopAccessingSecurityScopedResource()
}
let result: T = try await withCheckedThrowingContinuation { continuation in
let coordinator = NSFileCoordinator(filePresenter: presenter)
coordinator.coordinate(readingItemAt: readURL, options: readOptions, writingItemAt: writeURL, options: writeOptions, error: &coordinatorError) { (read: URL, write: URL) in
do {
let res = try block(read, write)
continuation.resume(returning: res)
} catch {
continuation.resume(throwing: error)
}
try block(read, write)
} catch {
managerError = error
}
if let coordinatorError = coordinatorError {
continuation.resume(throwing: coordinatorError)
}
}
guard coordinatorError == nil else { throw coordinatorError! }
guard managerError == nil else { throw managerError! }
return result
}
}