diff --git a/FileProvider.podspec b/FileProvider.podspec index d822948..42a9039 100644 --- a/FileProvider.podspec +++ b/FileProvider.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "FileProvider" - s.version = "0.7.0" + s.version = "0.7.1" s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/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 87e8f9b..25defbe 100644 --- a/FileProvider.xcodeproj/project.pbxproj +++ b/FileProvider.xcodeproj/project.pbxproj @@ -557,6 +557,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; + BUNDLE_VERSION_STRING = 0.7.1; CLANG_ANALYZER_NONNULL = YES; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -609,6 +610,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; + BUNDLE_VERSION_STRING = 0.7.1; CLANG_ANALYZER_NONNULL = YES; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; diff --git a/Sources/DropboxFileProvider.swift b/Sources/DropboxFileProvider.swift index 8e42e59..f2e615d 100644 --- a/Sources/DropboxFileProvider.swift +++ b/Sources/DropboxFileProvider.swift @@ -113,8 +113,9 @@ extension DropboxFileProvider: FileProviderOperations { return doOperation(.create(path: path), completionHandler: completionHandler) } - public func create(file fileAttribs: FileObject, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? { - return self.writeContents(path: path, contents: data ?? Data(), completionHandler: completionHandler) + public func create(file fileName: String, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? { + let filePath = (path as NSString).appendingPathComponent(fileName) + return self.writeContents(path: filePath, contents: data ?? Data(), completionHandler: completionHandler) } public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? { diff --git a/Sources/FileProvider.swift b/Sources/FileProvider.swift index 0632c8e..0ef1868 100644 --- a/Sources/FileProvider.swift +++ b/Sources/FileProvider.swift @@ -175,7 +175,7 @@ public protocol FileProviderOperations: FileProviderBasic { @discardableResult func create(folder: String, at: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? @discardableResult - func create(file: FileObject, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? + func create(file: String, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? @discardableResult func moveItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? @discardableResult diff --git a/Sources/LocalFileProvider.swift b/Sources/LocalFileProvider.swift index 599d568..c7b5520 100644 --- a/Sources/LocalFileProvider.swift +++ b/Sources/LocalFileProvider.swift @@ -104,28 +104,30 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { @discardableResult open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? { + let opType = FileOperationType.create(path: (atPath as NSString).appendingPathComponent(folderName) + "/") operation_queue.async { do { try self.opFileManager.createDirectory(at: self.absoluteURL(atPath).appendingPathComponent(folderName), withIntermediateDirectories: true, attributes: [:]) completionHandler?(nil) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderSucceed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(folderName) + "/")) + self.delegate?.fileproviderSucceed(self, operation: opType) }) } catch let e as NSError { completionHandler?(e) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderFailed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(folderName) + "/")) + self.delegate?.fileproviderFailed(self, operation: opType) }) } } - return LocalOperationHandle(operationType: .create(path: (atPath as NSString).appendingPathComponent(folderName)), baseURL: self.baseURL) + return LocalOperationHandle(operationType: opType, baseURL: self.baseURL) } @discardableResult - open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? { + open func create(file fileName: String, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? { + let opType = FileOperationType.create(path: (atPath as NSString).appendingPathComponent(fileName)) operation_queue.async { - let fileURL = self.absoluteURL(atPath).appendingPathComponent(fileAttribs.name) - var attributes = [String : Any]() + let fileURL = self.absoluteURL(atPath).appendingPathComponent(fileName) + /*var attributes = [String : Any]() if let createdDate = fileAttribs.createdDate { attributes[FileAttributeKey.creationDate.rawValue] = createdDate as NSDate } @@ -134,29 +136,29 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { } if fileAttribs.isReadOnly { attributes[FileAttributeKey.posixPermissions.rawValue] = NSNumber(value: 365 as Int16) - } - let success = self.opFileManager.createFile(atPath: fileURL.path, contents: data, attributes: attributes) + }*/ + let success = self.opFileManager.createFile(atPath: fileURL.path, contents: data, attributes: nil/*attributes*/) if success { - do { + /*do { try (fileURL as NSURL).setResourceValue(fileAttribs.isHidden, forKey: URLResourceKey.isHiddenKey) - } catch _ {} + } catch _ {}*/ completionHandler?(nil) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderSucceed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(fileAttribs.name))) + self.delegate?.fileproviderSucceed(self, operation: opType) }) } else { completionHandler?(self.throwError(atPath, code: URLError.cannotCreateFile as FoundationErrorEnum)) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderFailed(self, operation: .create(path: (atPath as NSString).appendingPathComponent(fileAttribs.name))) + self.delegate?.fileproviderFailed(self, operation: opType) }) } } - return LocalOperationHandle(operationType: .create(path: (atPath as NSString).appendingPathComponent(fileAttribs.name)), baseURL: self.baseURL) + return LocalOperationHandle(operationType: opType, baseURL: self.baseURL) } @discardableResult open func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? { - // FIXME: progress + let opType = FileOperationType.move(source: path, destination: toPath) operation_queue.async { if !overwrite && self.fileManager.fileExists(atPath: self.absoluteURL(toPath).path) { completionHandler?(self.throwError(toPath, code: URLError.cannotMoveFile as FoundationErrorEnum)) @@ -166,21 +168,21 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { try self.opFileManager.moveItem(at: self.absoluteURL(path), to: self.absoluteURL(toPath)) completionHandler?(nil) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderSucceed(self, operation: .move(source: path, destination: toPath)) + self.delegate?.fileproviderSucceed(self, operation: opType) }) } catch let e as NSError { completionHandler?(e) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderFailed(self, operation: .move(source: path, destination: toPath)) + self.delegate?.fileproviderFailed(self, operation: opType) }) } } - return LocalOperationHandle(operationType: .move(source: path, destination: toPath), baseURL: self.baseURL) + return LocalOperationHandle(operationType: opType, baseURL: self.baseURL) } @discardableResult open func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? { - // FIXME: progress, for files > 100mb, monitor file by another thread, for dirs check copied items count + let opType = FileOperationType.copy(source: path, destination: toPath) operation_queue.async { if !overwrite && self.fileManager.fileExists(atPath: self.absoluteURL(toPath).path) { completionHandler?(self.throwError(toPath, code: URLError.cannotWriteToFile as FoundationErrorEnum)) @@ -190,73 +192,76 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { try self.opFileManager.copyItem(at: self.absoluteURL(path), to: self.absoluteURL(toPath)) completionHandler?(nil) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderSucceed(self, operation: .copy(source: path, destination: toPath)) + self.delegate?.fileproviderSucceed(self, operation: opType) }) } catch let e as NSError { completionHandler?(e) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderFailed(self, operation: .copy(source: path, destination: toPath)) + self.delegate?.fileproviderFailed(self, operation: opType) }) } } - return LocalOperationHandle(operationType: .copy(source: path, destination: toPath), baseURL: self.baseURL) + return LocalOperationHandle(operationType: opType, baseURL: self.baseURL) } @discardableResult open func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? { + let opType = FileOperationType.remove(path: path) operation_queue.async { do { try self.opFileManager.removeItem(at: self.absoluteURL(path)) completionHandler?(nil) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderSucceed(self, operation: .remove(path: path)) + self.delegate?.fileproviderSucceed(self, operation: opType) }) } catch let e as NSError { completionHandler?(e) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderFailed(self, operation: .remove(path: path)) + self.delegate?.fileproviderFailed(self, operation: opType) }) } } - return LocalOperationHandle(operationType: .remove(path: path), baseURL: self.baseURL) + return LocalOperationHandle(operationType: opType, baseURL: self.baseURL) } @discardableResult open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? { + let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath) operation_queue.async { do { try self.opFileManager.copyItem(at: localFile, to: self.absoluteURL(toPath)) completionHandler?(nil) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderSucceed(self, operation: .copy(source: localFile.absoluteString, destination: toPath)) + self.delegate?.fileproviderSucceed(self, operation: opType) }) } catch let e as NSError { completionHandler?(e) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderFailed(self, operation: .copy(source: localFile.absoluteString, destination: toPath)) + self.delegate?.fileproviderFailed(self, operation: opType) }) } } - return LocalOperationHandle(operationType: .move(source: localFile.absoluteString, destination: toPath), baseURL: self.baseURL) + return LocalOperationHandle(operationType: opType, baseURL: self.baseURL) } @discardableResult open func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? { + let opType = FileOperationType.copy(source: path, destination: toLocalURL.absoluteString) operation_queue.async { do { try self.opFileManager.copyItem(at: self.absoluteURL(path), to: toLocalURL) completionHandler?(nil) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderSucceed(self, operation: .copy(source: path, destination: toLocalURL.absoluteString)) + self.delegate?.fileproviderSucceed(self, operation: opType) }) } catch let e as NSError { completionHandler?(e) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderFailed(self, operation: .copy(source: path, destination: toLocalURL.absoluteString)) + self.delegate?.fileproviderFailed(self, operation: opType) }) } } - return LocalOperationHandle(operationType: .move(source: path, destination: toLocalURL.absoluteString), baseURL: self.baseURL) + return LocalOperationHandle(operationType: opType, baseURL: self.baseURL) } @discardableResult @@ -270,49 +275,43 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { @discardableResult open func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? { - // Unfortunatlely there is no method provided in NSFileManager to read a segment of file. - // So we have to fallback to POSIX provided methods + let opType = FileOperationType.fetch(path: path) dispatch_queue.async { let aPath = self.absoluteURL(path).path guard !self.attributesOfItem(url: self.absoluteURL(path)).isDirectory && self.fileManager.fileExists(atPath: aPath) else { completionHandler(nil, self.throwError(path, code: URLError.cannotOpenFile as FoundationErrorEnum)) return } - let fd_from = open(aPath, O_RDONLY) - if fd_from < 0 { + guard let handle = FileHandle(forReadingAtPath: aPath) else { completionHandler(nil, self.throwError(path, code: URLError.cannotOpenFile as FoundationErrorEnum)) return } - defer { precondition(close(fd_from) >= 0) } - lseek(fd_from, offset, SEEK_SET) - var buf = [UInt8](repeating: 0, count: length) - let nread = read(fd_from, &buf, buf.count) - if nread < 0 { - completionHandler(nil, self.throwError(path, code: URLError.noPermissionsToReadFile as FoundationErrorEnum)) - } else if nread == 0 { - completionHandler(nil, nil) - } else { - let data = Data(bytesNoCopy: UnsafeMutablePointer(&buf), count: nread, deallocator: .free) - completionHandler(data, nil) + defer { + handle.closeFile() } + handle.seek(toFileOffset: UInt64(offset)) + let data = handle.readData(ofLength: length) + completionHandler(data, nil) + } - return LocalOperationHandle(operationType: .fetch(path: path), baseURL: self.baseURL) + return LocalOperationHandle(operationType: opType, baseURL: self.baseURL) } @discardableResult open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? { + let opType = FileOperationType.modify(path: path) operation_queue.async { try? data.write(to: self.absoluteURL(path), options: atomically ? [.atomic] : []) DispatchQueue.main.async(execute: { - self.delegate?.fileproviderSucceed(self, operation: .modify(path: path)) + self.delegate?.fileproviderSucceed(self, operation: opType) }) } - return LocalOperationHandle(operationType: .modify(path: path), baseURL: self.baseURL) + return LocalOperationHandle(operationType: opType, baseURL: self.baseURL) } open func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) { dispatch_queue.async { - let iterator = self.fileManager.enumerator(at: self.absoluteURL(path), includingPropertiesForKeys: nil, options: recursive ? FileManager.DirectoryEnumerationOptions() : .skipsSubdirectoryDescendants) { (url, e) -> Bool in + let iterator = self.fileManager.enumerator(at: self.absoluteURL(path), includingPropertiesForKeys: nil, options: recursive ? [] : [.skipsSubdirectoryDescendants, .skipsPackageDescendants]) { (url, e) -> Bool in completionHandler([], e) return true } diff --git a/Sources/SMBFileProvider.swift b/Sources/SMBFileProvider.swift index ff941ed..85224cf 100644 --- a/Sources/SMBFileProvider.swift +++ b/Sources/SMBFileProvider.swift @@ -48,7 +48,7 @@ class SMBFileProvider: FileProvider, FileProviderMonitor { return nil } - open func create(file fileAttribs: FileObject, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? { + open func create(file fileName: String, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? { NotImplemented() return nil } diff --git a/Sources/WebDAVFileProvider.swift b/Sources/WebDAVFileProvider.swift index 9893b7e..96d7019 100644 --- a/Sources/WebDAVFileProvider.swift +++ b/Sources/WebDAVFileProvider.swift @@ -176,8 +176,8 @@ extension WebDAVFileProvider: FileProviderOperations { } @discardableResult - public func create(file fileAttribs: FileObject, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? { - let opType = FileOperationType.create(path: (path as NSString).appendingPathComponent(fileAttribs.name)) + public func create(file fileName: String, at path: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? { + let opType = FileOperationType.create(path: (path as NSString).appendingPathComponent(fileName)) guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else { return nil }