diff --git a/FileProvider.podspec b/FileProvider.podspec index 92765e1..b44ee35 100644 --- a/FileProvider.podspec +++ b/FileProvider.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "FileProvider" - s.version = "0.12.2" + s.version = "0.12.3" s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/OneDrive/SMB2) files on iOS and macOS." # This description is used to generate tags and improve search results. diff --git a/FileProvider.xcodeproj/project.pbxproj b/FileProvider.xcodeproj/project.pbxproj index 0b2ace8..d4dee0b 100644 --- a/FileProvider.xcodeproj/project.pbxproj +++ b/FileProvider.xcodeproj/project.pbxproj @@ -603,7 +603,7 @@ 799396601D48B7BF00086753 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUNDLE_VERSION_STRING = 0.12.2; + BUNDLE_VERSION_STRING = 0.12.3; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -633,7 +633,7 @@ 799396611D48B7BF00086753 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUNDLE_VERSION_STRING = 0.12.2; + BUNDLE_VERSION_STRING = 0.12.3; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; diff --git a/Sources/DropboxHelper.swift b/Sources/DropboxHelper.swift index a125cfb..2e93de0 100644 --- a/Sources/DropboxHelper.swift +++ b/Sources/DropboxHelper.swift @@ -43,28 +43,28 @@ public final class DropboxFileObject: FileObject { open internal(set) var serverTime: Date? { get { - return allValues["NSURLServerDateKey"] as? Date + return allValues[.serverDate] as? Date } set { - allValues["NSURLServerDateKey"] = newValue + allValues[.serverDate] = newValue } } open internal(set) var id: String? { get { - return allValues["NSURLDocumentIdentifyKey"] as? String + return allValues[.documentIdentifierKey] as? String } set { - allValues["NSURLDocumentIdentifyKey"] = newValue + allValues[.documentIdentifierKey] = newValue } } open internal(set) var rev: String? { get { - return allValues[URLResourceKey.generationIdentifierKey.rawValue] as? String + return allValues[.generationIdentifierKey] as? String } set { - allValues[URLResourceKey.generationIdentifierKey.rawValue] = newValue + allValues[.generationIdentifierKey] = newValue } } } diff --git a/Sources/ExtendedLocalFileProvider.swift b/Sources/ExtendedLocalFileProvider.swift index f39e17d..e4b40f9 100644 --- a/Sources/ExtendedLocalFileProvider.swift +++ b/Sources/ExtendedLocalFileProvider.swift @@ -123,7 +123,6 @@ extension LocalFileProvider: ExtendedFileProvider { completionHandler(dic, keys, nil) } - } } diff --git a/Sources/FileObject.swift b/Sources/FileObject.swift index c6b0fa3..fe13068 100644 --- a/Sources/FileObject.swift +++ b/Sources/FileObject.swift @@ -11,14 +11,14 @@ import Foundation /// Containts path and attributes of a file or resource. open class FileObject { /// A `Dictionary` contains file information, using `URLResourceKey` keys. - open internal(set) var allValues: [String: Any] + open internal(set) var allValues: [URLResourceKey: Any] - internal init(allValues: [String: Any]) { + internal init(allValues: [URLResourceKey: Any]) { self.allValues = allValues } internal init(url: URL, name: String, path: String) { - self.allValues = [String: Any]() + self.allValues = [URLResourceKey: Any]() self.url = url self.name = name self.path = path @@ -34,70 +34,70 @@ open class FileObject { /// not supported by Dropbox provider. open internal(set) var url: URL? { get { - return allValues["NSURLFileURLKey"] as? URL + return allValues[.fileURL] as? URL } set { - allValues["NSURLFileURLKey"] = newValue + allValues[.fileURL] = newValue } } /// Name of the file, usually equals with the last path component open internal(set) var name: String { get { - return allValues[URLResourceKey.nameKey.rawValue] as! String + return allValues[.nameKey] as! String } set { - allValues[URLResourceKey.nameKey.rawValue] = newValue + allValues[.nameKey] = newValue } } /// Relative path of file object open internal(set) var path: String { get { - return allValues[URLResourceKey.pathKey.rawValue] as! String + return allValues[.pathKey] as! String } set { - allValues[URLResourceKey.pathKey.rawValue] = newValue + allValues[.pathKey] = newValue } } /// Size of file on disk, return -1 for directories. open internal(set) var size: Int64 { get { - return allValues[URLResourceKey.fileSizeKey.rawValue] as? Int64 ?? -1 + return allValues[.fileSizeKey] as? Int64 ?? -1 } set { - allValues[URLResourceKey.fileSizeKey.rawValue] = newValue + allValues[.fileSizeKey] = newValue } } /// The time contents of file has been created, returns nil if not set open internal(set) var creationDate: Date? { get { - return allValues[URLResourceKey.creationDateKey.rawValue] as? Date + return allValues[.creationDateKey] as? Date } set { - allValues[URLResourceKey.creationDateKey.rawValue] = newValue + allValues[.creationDateKey] = newValue } } /// The time contents of file has been modified, returns nil if not set open internal(set) var modifiedDate: Date? { get { - return allValues[URLResourceKey.contentModificationDateKey.rawValue] as? Date + return allValues[.contentModificationDateKey] as? Date } set { - allValues[URLResourceKey.contentModificationDateKey.rawValue] = newValue + allValues[.contentModificationDateKey] = newValue } } /// return resource type of file, usually directory, regular or symLink open internal(set) var type: URLFileResourceType? { get { - return allValues[URLResourceKey.fileResourceTypeKey.rawValue] as? URLFileResourceType + return allValues[.fileResourceTypeKey] as? URLFileResourceType } set { - allValues[URLResourceKey.fileResourceTypeKey.rawValue] = newValue + allValues[.fileResourceTypeKey] = newValue } } @@ -111,20 +111,20 @@ open class FileObject { /// Setting this value on a file begining with dot has no effect open internal(set) var isHidden: Bool { get { - return allValues[URLResourceKey.isHiddenKey.rawValue] as? Bool ?? false + return allValues[.isHiddenKey] as? Bool ?? false } set { - allValues[URLResourceKey.isHiddenKey.rawValue] = newValue + allValues[.isHiddenKey] = newValue } } /// File can not be written open internal(set) var isReadOnly: Bool { get { - return !(allValues[URLResourceKey.isWritableKey.rawValue] as? Bool ?? true) + return !(allValues[.isWritableKey] as? Bool ?? true) } set { - allValues[URLResourceKey.isWritableKey.rawValue] = !newValue + allValues[.isWritableKey] = !newValue } } @@ -296,6 +296,13 @@ extension URLFileResourceType { } } +internal extension URLResourceKey { + static let fileURL = URLResourceKey(rawValue: "NSURLFileURLKey") + static let serverDate = URLResourceKey(rawValue: "NSURLServerDateKey") + static let entryTag = URLResourceKey(rawValue: "NSURLEntryTagKey") + static let mimeType = URLResourceKey(rawValue: "NSURLMIMETypeIdentifierKey") +} + internal extension URL { var uw_scheme: String { return self.scheme ?? "" diff --git a/Sources/LocalFileProvider.swift b/Sources/LocalFileProvider.swift index e5420b4..1a552b9 100644 --- a/Sources/LocalFileProvider.swift +++ b/Sources/LocalFileProvider.swift @@ -114,7 +114,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndo open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) { dispatch_queue.async { do { - let contents = try self.fileManager.contentsOfDirectory(at: self.url(of: path), includingPropertiesForKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .isHiddenKey, .volumeIsReadOnlyKey], options: .skipsSubdirectoryDescendants) + let contents = try self.fileManager.contentsOfDirectory(at: self.url(of: path), includingPropertiesForKeys: nil, options: .skipsSubdirectoryDescendants) let filesAttributes = contents.flatMap({ (fileURL) -> LocalFileObject? in let path = self.relativePathOf(url: fileURL) return LocalFileObject(fileWithPath: path, relativeTo: self.baseURL) @@ -127,9 +127,9 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndo } open func storageProperties(completionHandler: (@escaping (_ total: Int64, _ used: Int64) -> Void)) { - let dict = (try? FileManager.default.attributesOfFileSystem(forPath: baseURL?.path ?? "/")) - let totalSize = (dict?[.systemSize] as? NSNumber)?.int64Value ?? -1; - let freeSize = (dict?[.systemFreeSize] as? NSNumber)?.int64Value ?? 0; + let values = try? baseURL?.resourceValues(forKeys: [.volumeTotalCapacityKey, .volumeAvailableCapacityKey]) + let totalSize = Int64(values??.volumeTotalCapacity ?? -1) + let freeSize = Int64(values??.volumeAvailableCapacity ?? 0) completionHandler(totalSize, totalSize - freeSize) } @@ -228,8 +228,8 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndo } let operationHandler: (URL, URL?) -> Void = { source, dest in + let successfulSecurityScopedResourceAccess = source.startAccessingSecurityScopedResource() do { - let successfulSecurityScopedResourceAccess = source.startAccessingSecurityScopedResource() switch opType { case .create: if sourcePath.hasSuffix("/") { @@ -264,6 +264,9 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndo self.delegate?.fileproviderSucceed(self, operation: opType) } } catch let e { + if successfulSecurityScopedResourceAccess { + source.stopAccessingSecurityScopedResource() + } completionHandler?(e) DispatchQueue.main.async { self.delegate?.fileproviderFailed(self, operation: opType) @@ -352,11 +355,11 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndo let operationHandler: (URL) -> Void = { url in guard self.fileManager.fileExists(atPath: url.path) && !url.fileIsDirectory else { - completionHandler(nil, self.throwError(path, code: URLError.fileDoesNotExist as FoundationErrorEnum)) + completionHandler(nil, self.throwError(path, code: CocoaError.fileNoSuchFile as FoundationErrorEnum)) return } guard let handle = FileHandle(forReadingAtPath: url.path) else { - completionHandler(nil, self.throwError(path, code: URLError.cannotOpenFile as FoundationErrorEnum)) + completionHandler(nil, self.throwError(path, code: CocoaError.fileReadNoPermission as FoundationErrorEnum)) return } @@ -366,13 +369,13 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndo handle.seek(toFileOffset: UInt64(offset)) guard Int64(handle.offsetInFile) == offset else { - completionHandler(nil, self.throwError(path, code: URLError.zeroByteResource as FoundationErrorEnum)) + completionHandler(nil, self.throwError(path, code: CocoaError.fileReadUnknown as FoundationErrorEnum)) return } let data = handle.readData(ofLength: length) guard length > 0 && data.count == length else { - completionHandler(nil, self.throwError(path, code: URLError.cannotOpenFile as FoundationErrorEnum)) + completionHandler(nil, self.throwError(path, code: CocoaError.fileReadTooLarge as FoundationErrorEnum)) return } diff --git a/Sources/LocalHelper.swift b/Sources/LocalHelper.swift index 14d6195..5b27082 100644 --- a/Sources/LocalHelper.swift +++ b/Sources/LocalHelper.swift @@ -37,12 +37,12 @@ public final class LocalFileObject: FileObject { public convenience init?(fileWithURL fileURL: URL) { do { - let values = try fileURL.resourceValues(forKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .fileResourceTypeKey, .isHiddenKey, .isWritableKey, .typeIdentifierKey, .generationIdentifierKey]) + let values = try fileURL.resourceValues(forKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .fileResourceTypeKey, .isHiddenKey, .isWritableKey, .typeIdentifierKey, .generationIdentifierKey, .documentIdentifierKey]) let path = fileURL.relativePath.hasPrefix("/") ? fileURL.relativePath : "/" + fileURL.relativePath self.init(url: fileURL, name: values.name ?? fileURL.lastPathComponent, path: path) for (key, value) in values.allValues { - self.allValues[key.rawValue] = value + self.allValues[key] = value } } catch { return nil @@ -51,20 +51,28 @@ public final class LocalFileObject: FileObject { open internal(set) var allocatedSize: Int64 { get { - return allValues[URLResourceKey.fileAllocatedSizeKey.rawValue] as? Int64 ?? 0 + return allValues[.fileAllocatedSizeKey] as? Int64 ?? 0 } set { - allValues[URLResourceKey.fileAllocatedSizeKey.rawValue] = Int(exactly: newValue) ?? Int.max + allValues[.fileAllocatedSizeKey] = Int(exactly: newValue) ?? Int.max + } + } + + open internal(set) var id: Int? { + get { + return allValues[.documentIdentifierKey] as? Int + } + set { + allValues[.documentIdentifierKey] = newValue } } open var rev: String? { get { - let data = allValues[URLResourceKey.generationIdentifierKey.rawValue] as? Data + let data = allValues[.generationIdentifierKey] as? Data return data?.map { String(format: "%02hhx", $0) }.joined() } } - } internal final class LocalFolderMonitor { diff --git a/Sources/OneDriveHelper.swift b/Sources/OneDriveHelper.swift index e6df3c1..c813417 100644 --- a/Sources/OneDriveHelper.swift +++ b/Sources/OneDriveHelper.swift @@ -48,28 +48,28 @@ public final class OneDriveFileObject: FileObject { open internal(set) var id: String? { get { - return allValues["NSURLDocumentIdentifyKey"] as? String + return allValues[.documentIdentifierKey] as? String } set { - allValues["NSURLDocumentIdentifyKey"] = newValue + allValues[.documentIdentifierKey] = newValue } } open internal(set) var contentType: String { get { - return allValues["NSURLContentTypeKey"] as? String ?? "" + return allValues[.mimeType] as? String ?? "" } set { - allValues["NSURLContentTypeKey"] = newValue + allValues[.mimeType] = newValue } } open internal(set) var entryTag: String? { get { - return allValues["NSURLEntryTagKey"] as? String + return allValues[.entryTag] as? String } set { - allValues["NSURLEntryTagKey"] = newValue + allValues[.entryTag] = newValue } } } diff --git a/Sources/WebDAVFileProvider.swift b/Sources/WebDAVFileProvider.swift index e83d3f8..1a39810 100644 --- a/Sources/WebDAVFileProvider.swift +++ b/Sources/WebDAVFileProvider.swift @@ -582,20 +582,20 @@ public final class WebDavFileObject: FileObject { /// MIME type of the file open internal(set) var contentType: String { get { - return allValues["NSURLContentTypeKey"] as? String ?? "" + return allValues[.mimeType] as? String ?? "" } set { - allValues["NSURLContentTypeKey"] = newValue + allValues[.mimeType] = newValue } } /// HTTP E-Tag, can be used to mark changed files open internal(set) var entryTag: String? { get { - return allValues["NSURLEntryTagKey"] as? String + return allValues[.entryTag] as? String } set { - allValues["NSURLEntryTagKey"] = newValue + allValues[.entryTag] = newValue } } }