diff --git a/FileProvider.podspec b/FileProvider.podspec index b9872d3..9f9ea08 100644 --- a/FileProvider.podspec +++ b/FileProvider.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "FileProvider" - s.version = "0.11.0" + s.version = "0.11.1" 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 8778a8d..d0a498a 100644 --- a/FileProvider.xcodeproj/project.pbxproj +++ b/FileProvider.xcodeproj/project.pbxproj @@ -603,7 +603,7 @@ 799396601D48B7BF00086753 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUNDLE_VERSION_STRING = 0.11.0; + BUNDLE_VERSION_STRING = 0.11.1; 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.11.0; + BUNDLE_VERSION_STRING = 0.11.1; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; diff --git a/Sources/CloudFileProvider.swift b/Sources/CloudFileProvider.swift index 2367bf2..4e879f0 100644 --- a/Sources/CloudFileProvider.swift +++ b/Sources/CloudFileProvider.swift @@ -14,7 +14,7 @@ open class CloudFileProvider: LocalFileProvider { return "iCloudDrive" } - /// Actually is readonly + /// Actually is readonly, value is true override open var isCoorinating: Bool { get { return true @@ -35,7 +35,7 @@ open class CloudFileProvider: LocalFileProvider { return nil } self.containerId = containerId - let baseURL = ubiquityURL.standardized.appendingPathComponent("Documents") + let baseURL = ubiquityURL.standardized.appendingPathComponent("Documents/") super.init(baseURL: baseURL) self.isCoorinating = true @@ -51,7 +51,7 @@ open class CloudFileProvider: LocalFileProvider { open override func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) { dispatch_queue.async { - let pathURL = self.absoluteURL(path) + let pathURL = self.url(of: path) let query = NSMetadataQuery() query.predicate = NSPredicate(format: "%K BEGINSWITH %@", NSMetadataItemPathKey, pathURL.path) query.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope] @@ -96,7 +96,7 @@ open class CloudFileProvider: LocalFileProvider { open override func attributesOfItem(path: String, completionHandler: @escaping ((_ attributes: FileObject?, _ error: Error?) -> Void)) { dispatch_queue.async { - let pathURL = self.absoluteURL(path) + let pathURL = self.url(of: path) let query = NSMetadataQuery() query.predicate = NSPredicate(format: "%K LIKE %@", NSMetadataItemPathKey, pathURL.path) query.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope] @@ -171,7 +171,7 @@ open class CloudFileProvider: LocalFileProvider { do { try self.opFileManager.copyItem(at: localFile, to: tmpFile) - let toUrl = self.absoluteURL(toPath) + let toUrl = self.url(of: toPath) try self.opFileManager.setUbiquitous(true, itemAt: tmpFile, destinationURL: toUrl) completionHandler?(nil) DispatchQueue.main.async(execute: { @@ -195,7 +195,7 @@ open class CloudFileProvider: LocalFileProvider { let opType = FileOperationType.copy(source: path, destination: toLocalURL.absoluteString) do { - try self.opFileManager.startDownloadingUbiquitousItem(at: self.absoluteURL(path)) + try self.opFileManager.startDownloadingUbiquitousItem(at: self.url(of: path)) } catch let e { completionHandler?(e) DispatchQueue.main.async(execute: { @@ -228,7 +228,7 @@ open class CloudFileProvider: LocalFileProvider { open override func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) { dispatch_queue.async { - let pathURL = self.absoluteURL(path) + let pathURL = self.url(of: path) let query = NSMetadataQuery() query.predicate = NSPredicate(format: "(%K BEGINSWITH %@) && (%K LIKE %@)", NSMetadataItemPathKey, pathURL.path, NSMetadataItemFSNameKey, query) query.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope] @@ -302,7 +302,7 @@ open class CloudFileProvider: LocalFileProvider { // open override func registerNotifcation(path: String, eventHandler: @escaping (() -> Void)) { self.unregisterNotifcation(path: path) - let pathURL = self.absoluteURL(path) + let pathURL = self.url(of: path) let query = NSMetadataQuery() query.predicate = NSPredicate(format: "(%K BEGINSWITH %@)", NSMetadataItemPathKey, pathURL.path) query.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope] @@ -322,7 +322,7 @@ open class CloudFileProvider: LocalFileProvider { } open override func unregisterNotifcation(path: String) { - let key = absoluteURL(path) + let key = url(of: path) guard let (query, observer) = monitors[key] else { return } @@ -333,7 +333,7 @@ open class CloudFileProvider: LocalFileProvider { } open override func isRegisteredForNotification(path: String) -> Bool { - return monitors[absoluteURL(path)] != nil + return monitors[url(of: path)] != nil } /// may return nil @@ -352,8 +352,9 @@ open class CloudFileProvider: LocalFileProvider { } let path = self.relativePathOf(url: url) - - let file = FileObject(absoluteURL: url, name: name, path: path) + let rpath = path.hasPrefix("/") ? path.substring(from: path.index(after: path.startIndex)) : path + let relativeUrl = URL(string: rpath, relativeTo: self.baseURL) + let file = FileObject(url: relativeUrl ?? url, name: name, path: path) file.size = (attribs[NSMetadataItemFSSizeKey] as? NSNumber)?.int64Value ?? -1 file.creationDate = attribs[NSMetadataItemFSCreationDateKey] as? Date @@ -369,7 +370,7 @@ open class CloudFileProvider: LocalFileProvider { open func evictItem(path: String, completionHandler: SimpleCompletionHandler) { operation_queue.addOperation { do { - try self.opFileManager.evictUbiquitousItem(at: self.absoluteURL(path)) + try self.opFileManager.evictUbiquitousItem(at: self.url(of: path)) completionHandler?(nil) } catch let e { completionHandler?(e) @@ -381,7 +382,7 @@ open class CloudFileProvider: LocalFileProvider { operation_queue.addOperation { do { var expiration: NSDate? - let url = try self.opFileManager.url(forPublishingUbiquitousItemAt: self.absoluteURL(path), expiration: &expiration) + let url = try self.opFileManager.url(forPublishingUbiquitousItemAt: self.url(of: path), expiration: &expiration) completionHandler(url, nil, expiration as Date?, nil) } catch let e { completionHandler(nil, nil, nil, e) diff --git a/Sources/DropboxFileProvider.swift b/Sources/DropboxFileProvider.swift index 302f3b0..50ff5dc 100644 --- a/Sources/DropboxFileProvider.swift +++ b/Sources/DropboxFileProvider.swift @@ -60,9 +60,9 @@ open class DropboxFileProvider: FileProviderBasicRemote { self.apiURL = URL(string: "https://api.dropboxapi.com/2/")! self.contentURL = URL(string: "https://content.dropboxapi.com/2/")! - dispatch_queue = DispatchQueue(label: "FileProvider.\(DropboxFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent) + dispatch_queue = DispatchQueue(label: "FileProvider.\(type(of: self).type)", attributes: DispatchQueue.Attributes.concurrent) operation_queue = OperationQueue() - operation_queue.name = "FileProvider.\(DropboxFileProvider.type).Operation" + operation_queue.name = "FileProvider.\(type(of: self).type).Operation" } @@ -330,13 +330,13 @@ extension DropboxFileProvider { task.resume() } - open func copyItem(path: String, toRemoteURL destURL: URL, completionHandler: @escaping ((_ jobId: String?, _ attribute: DropboxFileObject?, _ error: Error?) -> Void)) { + open func copyItem(remoteURL: URL, to toPath: String, completionHandler: @escaping ((_ jobId: String?, _ attribute: DropboxFileObject?, _ error: Error?) -> Void)) { let url = URL(string: "files/save_url", relativeTo: apiURL)! var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization") request.setValue("application/json", forHTTPHeaderField: "Content-Type") - let requestDictionary = ["path": correctPath(path)! as NSString, "url" : destURL.absoluteString as NSString] + let requestDictionary = ["path": correctPath(toPath)! as NSString, "url" : remoteURL.absoluteString as NSString] request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8) let task = session.dataTask(with: request, completionHandler: { (data, response, error) in var dbError: FileProviderDropboxError? @@ -344,7 +344,7 @@ extension DropboxFileProvider { var fileObject: DropboxFileObject? if let response = response as? HTTPURLResponse { let code = FileProviderHTTPErrorCode(rawValue: response.statusCode) - dbError = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8)) : nil + dbError = code != nil ? FileProviderDropboxError(code: code!, path: toPath, errorDescription: String(data: data ?? Data(), encoding: .utf8)) : nil if let data = data, let jsonStr = String(data: data, encoding: .utf8), let json = jsonToDictionary(jsonStr) { jobId = json["async_job_id"] as? String if let attribDic = json["metadata"] as? [String: AnyObject] { diff --git a/Sources/DropboxHelper.swift b/Sources/DropboxHelper.swift index 08a2a86..2a05897 100644 --- a/Sources/DropboxHelper.swift +++ b/Sources/DropboxHelper.swift @@ -20,7 +20,7 @@ public struct FileProviderDropboxError: Error, CustomStringConvertible { public final class DropboxFileObject: FileObject { internal init(name: String, path: String) { - super.init(absoluteURL: URL(string: path), name: name, path: path) + super.init(url: URL(string: path) ?? URL(string: "/")!, name: name, path: path) } open internal(set) var serverTime: Date? { diff --git a/Sources/ExtendedLocalFileProvider.swift b/Sources/ExtendedLocalFileProvider.swift index 27b22c3..a51c086 100644 --- a/Sources/ExtendedLocalFileProvider.swift +++ b/Sources/ExtendedLocalFileProvider.swift @@ -64,7 +64,7 @@ extension LocalFileProvider: ExtendedFileProvider { (dispatch_queue).async { var thumbnailImage: ImageClass? = nil // Check cache - let fileURL = self.absoluteURL(path) + let fileURL = self.url(of: path) // Create Thumbnail and cache switch fileURL.pathExtension.lowercased() { case LocalFileInformationGenerator.videoThumbnailExtensions: @@ -117,7 +117,7 @@ extension LocalFileProvider: ExtendedFileProvider { var dic = [String: Any]() var keys = [String]() if let getterMethod = getter { - (dic, keys) = getterMethod(self.absoluteURL(path)) + (dic, keys) = getterMethod(self.url(of: path)) } completionHandler(dic, keys, nil) diff --git a/Sources/FileObject.swift b/Sources/FileObject.swift index 9846a0f..7b3a1a9 100644 --- a/Sources/FileObject.swift +++ b/Sources/FileObject.swift @@ -16,20 +16,25 @@ open class FileObject { self.allValues = allValues } - internal init(absoluteURL: URL? = nil, name: String, path: String) { + internal init(url: URL, name: String, path: String) { self.allValues = [String: Any]() - self.absoluteURL = absoluteURL + self.url = url self.name = name self.path = path } /// url to access the resource, not supported by Dropbox provider - open internal(set) var absoluteURL: URL? { + @available(*, deprecated, message: "Use FileObject.url.absoluteURL instead.") + open var absoluteURL: URL? { + return url?.absoluteURL + } + + open internal(set) var url: URL? { get { - return allValues["NSURLAbsoluteURLKey"] as? URL + return allValues["NSURLFileURLKey"] as? URL } set { - allValues["NSURLAbsoluteURLKey"] = newValue + allValues["NSURLFileURLKey"] = newValue } } diff --git a/Sources/FileProvider.swift b/Sources/FileProvider.swift index f581618..b539378 100644 --- a/Sources/FileProvider.swift +++ b/Sources/FileProvider.swift @@ -230,6 +230,10 @@ extension FileProviderBasic { } public func relativePathOf(url: URL) -> String { + if url.baseURL == self.baseURL { + return url.relativePath.removingPercentEncoding! + } + guard let baseURL = self.baseURL else { return url.absoluteString } return url.standardizedFileURL.absoluteString.replacingOccurrences(of: baseURL.absoluteString, with: "/").removingPercentEncoding! } @@ -264,7 +268,7 @@ extension FileProviderBasic { } var i = number ?? 2 let similiar = contents.map { - $0.absoluteURL?.lastPathComponent ?? $0.name + $0.url?.lastPathComponent ?? $0.name }.filter { $0.hasPrefix(result) } @@ -280,7 +284,7 @@ extension FileProviderBasic { } internal func throwError(_ path: String, code: FoundationErrorEnum) -> NSError { - let fileURL = self.absoluteURL(path) + let fileURL = self.url(of: path) let domain: String switch code { case is URLError: diff --git a/Sources/LocalFileProvider.swift b/Sources/LocalFileProvider.swift index 61ee82b..fe9bdf4 100644 --- a/Sources/LocalFileProvider.swift +++ b/Sources/LocalFileProvider.swift @@ -64,9 +64,9 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { self.credential = nil self.isCoorinating = false - dispatch_queue = DispatchQueue(label: "FileProvider.\(LocalFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent) + dispatch_queue = DispatchQueue(label: "FileProvider.\(type(of: self).type)", attributes: DispatchQueue.Attributes.concurrent) operation_queue = OperationQueue() - operation_queue.name = "FileProvider.\(LocalFileProvider.type).Operation" + operation_queue.name = "FileProvider.\(type(of: self).type).Operation" fileProviderManagerDelegate = LocalFileProviderManagerDelegate(provider: self) opFileManager.delegate = fileProviderManagerDelegate @@ -81,7 +81,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) { dispatch_queue.async { do { - let contents = try self.fileManager.contentsOfDirectory(at: self.absoluteURL(path), includingPropertiesForKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .isHiddenKey, .volumeIsReadOnlyKey], options: .skipsSubdirectoryDescendants) + let contents = try self.fileManager.contentsOfDirectory(at: self.url(of: path), includingPropertiesForKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .isHiddenKey, .volumeIsReadOnlyKey], options: .skipsSubdirectoryDescendants) let filesAttributes = contents.flatMap({ (fileURL) -> LocalFileObject? in let path = self.relativePathOf(url: fileURL) return LocalFileObject(fileWithPath: path, relativeTo: self.baseURL) @@ -111,7 +111,7 @@ 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) + "/") - let url = self.absoluteURL(atPath).appendingPathComponent(folderName) + let url = self.url(of: atPath).appendingPathComponent(folderName) let operationHandler: (URL) -> Void = { url in do { @@ -148,7 +148,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { @discardableResult 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)) - let url = self.absoluteURL(atPath).appendingPathComponent(fileName, isDirectory: false) + let url = self.url(of: atPath).appendingPathComponent(fileName, isDirectory: false) let operationHandler: (URL) -> Void = { url in let success = self.opFileManager.createFile(atPath: url.path, contents: data, attributes: nil) @@ -185,8 +185,8 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { @discardableResult open func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? { let opType = FileOperationType.move(source: path, destination: toPath) - let sourceUrl = self.absoluteURL(path) - let destUrl = self.absoluteURL(toPath) + let sourceUrl = self.url(of: path) + let destUrl = self.url(of: toPath) let sourceIntent = NSFileAccessIntent.writingIntent(with: sourceUrl, options: .forDeleting) let destIntent = NSFileAccessIntent.writingIntent(with: destUrl, options: .forReplacing) @@ -230,8 +230,8 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { @discardableResult open func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? { let opType = FileOperationType.copy(source: path, destination: toPath) - let sourceUrl = self.absoluteURL(path) - let destUrl = self.absoluteURL(toPath) + let sourceUrl = self.url(of: path) + let destUrl = self.url(of: toPath) let sourceIntent = NSFileAccessIntent.readingIntent(with: sourceUrl, options: .withoutChanges) let destIntent = NSFileAccessIntent.writingIntent(with: destUrl, options: .forDeleting) @@ -274,7 +274,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { @discardableResult open func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? { let opType = FileOperationType.remove(path: path) - let url = self.absoluteURL(path) + let url = self.url(of: path) let operationHandler: (URL) -> Void = { url in do { @@ -318,7 +318,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath) operation_queue.addOperation { do { - try self.opFileManager.copyItem(at: localFile, to: self.absoluteURL(toPath)) + try self.opFileManager.copyItem(at: localFile, to: self.url(of: toPath)) completionHandler?(nil) DispatchQueue.main.async { self.delegate?.fileproviderSucceed(self, operation: opType) @@ -338,7 +338,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { let opType = FileOperationType.copy(source: path, destination: toLocalURL.absoluteString) operation_queue.addOperation { do { - try self.opFileManager.copyItem(at: self.absoluteURL(path), to: toLocalURL) + try self.opFileManager.copyItem(at: self.url(of: path), to: toLocalURL) completionHandler?(nil) DispatchQueue.main.async { self.delegate?.fileproviderSucceed(self, operation: opType) @@ -356,7 +356,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { @discardableResult open func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? { let opType = FileOperationType.fetch(path: path) - let url = self.absoluteURL(path) + let url = self.url(of: path) let operationHandler: (URL) -> Void = { url in let data = self.fileManager.contents(atPath: url.path) @@ -387,7 +387,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { } let opType = FileOperationType.fetch(path: path) - let url = self.absoluteURL(path) + let url = self.url(of: path) let operationHandler: (URL) -> Void = { url in guard self.fileManager.fileExists(atPath: url.path) && !url.fileIsDirectory else { @@ -426,7 +426,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { @discardableResult open func writeContents(path: String, contents data: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? { let opType = FileOperationType.modify(path: path) - let url = self.absoluteURL(path) + let url = self.url(of: path) var options: Data.WritingOptions = [] if atomically { options.insert(.atomic) @@ -469,7 +469,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { 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 ? [] : [.skipsSubdirectoryDescendants, .skipsPackageDescendants]) { (url, e) -> Bool in + let iterator = self.fileManager.enumerator(at: self.url(of: path), includingPropertiesForKeys: nil, options: recursive ? [] : [.skipsSubdirectoryDescendants, .skipsPackageDescendants]) { (url, e) -> Bool in completionHandler([], e) return true } @@ -491,12 +491,12 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor { open func registerNotifcation(path: String, eventHandler: @escaping (() -> Void)) { self.unregisterNotifcation(path: path) - let absurl = self.absoluteURL(path) - let isdir = (try? absurl.resourceValues(forKeys: [.isDirectoryKey]).isDirectory ?? false) ?? false + let dirurl = self.url(of: path) + let isdir = (try? dirurl.resourceValues(forKeys: [.isDirectoryKey]).isDirectory ?? false) ?? false if !isdir { return } - let monitor = LocalFolderMonitor(url: absurl) { + let monitor = LocalFolderMonitor(url: dirurl) { eventHandler() } monitor.start() @@ -532,7 +532,7 @@ public extension LocalFileProvider { public func create(symbolicLink path: String, withDestinationPath destPath: String, completionHandler: SimpleCompletionHandler) { operation_queue.addOperation { do { - try self.opFileManager.createSymbolicLink(at: self.absoluteURL(path), withDestinationURL: self.absoluteURL(destPath)) + try self.opFileManager.createSymbolicLink(at: self.url(of: path), withDestinationURL: self.url(of: destPath)) completionHandler?(nil) DispatchQueue.main.async { self.delegate?.fileproviderSucceed(self, operation: .link(link: path, target: destPath)) @@ -549,7 +549,7 @@ public extension LocalFileProvider { public func destination(ofSymbolicLink path: String, completionHandler: @escaping (_ url: URL?, _ error: Error?) -> Void) { dispatch_queue.async { do { - let destPath = try self.opFileManager.destinationOfSymbolicLink(atPath: self.absoluteURL(path).path) + let destPath = try self.opFileManager.destinationOfSymbolicLink(atPath: self.url(of: path).path) let destUrl = URL(fileURLWithPath: destPath) completionHandler(destUrl, nil) } catch let e{ diff --git a/Sources/LocalHelper.swift b/Sources/LocalHelper.swift index f6b8912..4b52026 100644 --- a/Sources/LocalHelper.swift +++ b/Sources/LocalHelper.swift @@ -9,32 +9,32 @@ import Foundation public final class LocalFileObject: FileObject { - internal init(absoluteURL: URL, name: String, path: String) { - super.init(absoluteURL: absoluteURL, name: name, path: path) + internal override init(url: URL, name: String, path: String) { + super.init(url: url, name: name, path: path) } public convenience init? (fileWithPath path: String, relativeTo relativeURL: URL?) { let fileURL: URL - if let relativeURL = relativeURL { - fileURL = relativeURL.appendingPathComponent(path) + var rpath = path.replacingOccurrences(of: relativeURL?.absoluteString ?? "", with: "") + if path.hasPrefix("/") { + rpath.remove(at: rpath.startIndex) + } + if rpath.isEmpty { + fileURL = relativeURL ?? URL(fileURLWithPath: path) } else { - fileURL = URL(fileURLWithPath: path) - } - do { - let values = try fileURL.resourceValues(forKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .fileResourceTypeKey, .isHiddenKey, .isWritableKey]) - self.init(absoluteURL: fileURL, name: values.name ?? fileURL.lastPathComponent, path: path) - for (key, value) in values.allValues { - self.allValues[key.rawValue] = value + if #available(iOS 9.0, macOS 10.11, tvOS 9.0, *) { + fileURL = URL(fileURLWithPath: rpath, relativeTo: relativeURL) + } else { + fileURL = relativeURL?.appendingPathComponent(path) ?? URL(fileURLWithPath: path) } - } catch { - return nil } + self.init(fileWithURL: fileURL) } public convenience init?(fileWithURL fileURL: URL) { do { let values = try fileURL.resourceValues(forKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .fileResourceTypeKey, .isHiddenKey, .isWritableKey, .typeIdentifierKey]) - self.init(absoluteURL: fileURL, name: values.name ?? fileURL.lastPathComponent, path: fileURL.path) + self.init(url: fileURL, name: values.name ?? fileURL.lastPathComponent, path: fileURL.path) for (key, value) in values.allValues { self.allValues[key.rawValue] = value } diff --git a/Sources/OneDriveFileProvide.swift b/Sources/OneDriveFileProvide.swift index 89982b0..b1dcce6 100644 --- a/Sources/OneDriveFileProvide.swift +++ b/Sources/OneDriveFileProvide.swift @@ -53,7 +53,7 @@ open class OneDriveFileProvider: FileProviderBasicRemote { } public init? (credential: URLCredential?, serverURL: URL? = nil, drive: String = "root", cache: URLCache? = nil) { - self.baseURL = serverURL ?? URL(string: "https://api.onedrive.com") + self.baseURL = (serverURL ?? URL(string: "https://api.onedrive.com/")!).appendingPathComponent("") self.drive = drive self.isPathRelative = true self.currentPath = "" @@ -61,9 +61,9 @@ open class OneDriveFileProvider: FileProviderBasicRemote { self.validatingCache = true self.cache = cache self.credential = credential - dispatch_queue = DispatchQueue(label: "FileProvider.\(OneDriveFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent) + dispatch_queue = DispatchQueue(label: "FileProvider.\(type(of: self).type)", attributes: DispatchQueue.Attributes.concurrent) operation_queue = OperationQueue() - operation_queue.name = "FileProvider.\(DropboxFileProvider.type).Operation" + operation_queue.name = "FileProvider.\(type(of: self).type).Operation" } deinit { diff --git a/Sources/OneDriveHelper.swift b/Sources/OneDriveHelper.swift index cdd4d4d..d11401c 100644 --- a/Sources/OneDriveHelper.swift +++ b/Sources/OneDriveHelper.swift @@ -19,8 +19,13 @@ public struct FileProviderOneDriveError: Error, CustomStringConvertible { } public final class OneDriveFileObject: FileObject { - internal init(name: String, path: String) { - super.init(absoluteURL: URL(string: path), name: name, path: path) + internal init(baseURL: URL?, name: String, path: String) { + var rpath = path + if path.hasPrefix("/") { + rpath.remove(at: rpath.startIndex) + } + let url = URL(string: rpath, relativeTo: baseURL) ?? URL(string: path)! + super.init(url: url, name: name, path: path) } @@ -199,10 +204,7 @@ internal extension OneDriveFileProvider { guard let name = json["name"] as? String else { return nil } guard let path = (json["parentReference"] as? NSDictionary)?["path"] as? String else { return nil } let lPath = path.replacingOccurrences(of: "/drive/\(drive):", with: "/", options: .anchored, range: nil) - let fileObject = OneDriveFileObject(name: name, path: lPath) - if let webURL = json["webUrl"] as? String, let absolluteURL = URL(string: webURL) { - fileObject.absoluteURL = absolluteURL - } + let fileObject = OneDriveFileObject(baseURL: self.baseURL, name: name, path: lPath) fileObject.size = (json["size"] as? NSNumber)?.int64Value ?? -1 fileObject.modifiedDate = resolve(dateString: json["lastModifiedDateTime"] as? String ?? "") fileObject.creationDate = resolve(dateString: json["createdDateTime"] as? String ?? "") diff --git a/Sources/SMBFileProvider.swift b/Sources/SMBFileProvider.swift index cac7304..d460c5d 100644 --- a/Sources/SMBFileProvider.swift +++ b/Sources/SMBFileProvider.swift @@ -25,10 +25,10 @@ class SMBFileProvider: FileProvider, FileProviderMonitor { guard baseURL.uw_scheme.lowercased() == "smb" else { return nil } - self.baseURL = baseURL - dispatch_queue = DispatchQueue(label: "FileProvider.\(SMBFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent) + self.baseURL = baseURL.appendingPathComponent("") + dispatch_queue = DispatchQueue(label: "FileProvider.\(type(of: self).type)", attributes: DispatchQueue.Attributes.concurrent) operation_queue = OperationQueue() - operation_queue.name = "FileProvider.\(SMBFileProvider.type).Operation" + operation_queue.name = "FileProvider.\(type(of: self).type).Operation" self.credential = credential } diff --git a/Sources/WebDAVFileProvider.swift b/Sources/WebDAVFileProvider.swift index 8bbc91f..007355d 100644 --- a/Sources/WebDAVFileProvider.swift +++ b/Sources/WebDAVFileProvider.swift @@ -9,8 +9,8 @@ import Foundation public final class WebDavFileObject: FileObject { - internal init(absoluteURL: URL, name: String, path: String) { - super.init(absoluteURL: absoluteURL, name: name, path: path) + internal override init(url: URL, name: String, path: String) { + super.init(url: url, name: name, path: path) } open internal(set) var contentType: String { @@ -73,16 +73,16 @@ open class WebDAVFileProvider: FileProviderBasicRemote { if !["http", "https"].contains(baseURL.uw_scheme.lowercased()) { return nil } - self.baseURL = baseURL + self.baseURL = baseURL.appendingPathComponent("") self.isPathRelative = true self.currentPath = "" self.useCache = false self.validatingCache = true self.cache = cache self.credential = credential - dispatch_queue = DispatchQueue(label: "FileProvider.\(WebDAVFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent) + dispatch_queue = DispatchQueue(label: "FileProvider.\(type(of: self).type)", attributes: DispatchQueue.Attributes.concurrent) operation_queue = OperationQueue() - operation_queue.name = "FileProvider.\(DropboxFileProvider.type).Operation" + operation_queue.name = "FileProvider.\(type(of: self).type).Operation" } deinit { @@ -91,7 +91,7 @@ open class WebDAVFileProvider: FileProviderBasicRemote { open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) { let opType = FileOperationType.fetch(path: path) - let url = absoluteURL(path) + let url = self.url(of: path).appendingPathComponent("") var request = URLRequest(url: url) request.httpMethod = "PROPFIND" request.setValue("1", forHTTPHeaderField: "Depth") @@ -118,7 +118,7 @@ open class WebDAVFileProvider: FileProviderBasicRemote { } open func attributesOfItem(path: String, completionHandler: @escaping ((_ attributes: FileObject?, _ error: Error?) -> Void)) { - let url = absoluteURL(path) + let url = self.url(of: path) var request = URLRequest(url: url) request.httpMethod = "PROPFIND" request.setValue("1", forHTTPHeaderField: "Depth") @@ -178,7 +178,7 @@ extension WebDAVFileProvider: FileProviderOperations { guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else { return nil } - let url = absoluteURL((atPath as NSString).appendingPathComponent(folderName) + "/") + let url = self.url(of: atPath).appendingPathComponent(folderName, isDirectory: true) var request = URLRequest(url: url) request.httpMethod = "MKCOL" let task = session.dataTask(with: request, completionHandler: { (data, response, error) in @@ -200,7 +200,7 @@ extension WebDAVFileProvider: FileProviderOperations { guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else { return nil } - let url = absoluteURL(path).appendingPathComponent(fileName) + let url = self.url(of: path).appendingPathComponent(fileName) var request = URLRequest(url: url) request.httpMethod = "PUT" let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in @@ -244,10 +244,10 @@ extension WebDAVFileProvider: FileProviderOperations { } func doOperation(operation opType: FileOperationType, overwrite: Bool? = nil, completionHandler: SimpleCompletionHandler) -> OperationHandle? { - let sourceURL = absoluteURL(opType.source!) + let sourceURL = self.url(of:opType.source!) var request = URLRequest(url: sourceURL) if let dest = opType.destination { - request.setValue(absoluteURL(dest).absoluteString, forHTTPHeaderField: "Destination") + request.setValue(url(of:dest).absoluteString, forHTTPHeaderField: "Destination") } switch opType { case .copy: @@ -294,7 +294,7 @@ extension WebDAVFileProvider: FileProviderOperations { guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else { return nil } - let url = absoluteURL(toPath) + let url = self.url(of:toPath) var request = URLRequest(url: url) request.httpMethod = "PUT" let task = session.uploadTask(with: request, fromFile: localFile, completionHandler: { (data, response, error) in @@ -316,7 +316,7 @@ extension WebDAVFileProvider: FileProviderOperations { guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else { return nil } - let url = absoluteURL(path) + let url = self.url(of:path) let request = URLRequest(url: url) let task = session.downloadTask(with: request, completionHandler: { (sourceFileURL, response, error) in var responseError: FileProviderWebDavError? @@ -344,7 +344,7 @@ extension WebDAVFileProvider: FileProviderReadWrite { @discardableResult public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? { let opType = FileOperationType.fetch(path: path) - let url = absoluteURL(path) + let url = self.url(of: path) var request = URLRequest(url: url) request.httpMethod = "GET" if length > 0 { @@ -370,7 +370,7 @@ extension WebDAVFileProvider: FileProviderReadWrite { return nil } // FIXME: lock destination before writing process - let url = atomically ? absoluteURL(path).appendingPathExtension("tmp") : absoluteURL(path) + let url = atomically ? self.url(of: path).appendingPathExtension("tmp") : self.url(of: path) var request = URLRequest(url: url) request.httpMethod = "PUT" if !overwrite { @@ -379,7 +379,7 @@ extension WebDAVFileProvider: FileProviderReadWrite { let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in var responseError: FileProviderWebDavError? if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) { - responseError = FileProviderWebDavError(code: rCode, url: self.absoluteURL(path)) + responseError = FileProviderWebDavError(code: rCode, url: self.url(of: path)) } defer { self.delegateNotify(opType, error: responseError ?? error) @@ -398,7 +398,7 @@ extension WebDAVFileProvider: FileProviderReadWrite { } public func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) { - let url = absoluteURL(path) + let url = self.url(of: path) var request = URLRequest(url: url) request.httpMethod = "PROPFIND" //request.setValue("1", forHTTPHeaderField: "Depth") @@ -504,7 +504,19 @@ internal extension WebDAVFileProvider { } } let href = node[hreftag].value - if let href = href, let hrefURL = URL(string: href) { + if let href = href { + let phrefURL: URL? + var relativePath = href.replacingOccurrences(of: self.baseURL?.absoluteString ?? "", with: "/") + if relativePath.hasPrefix("http://") || relativePath.hasPrefix("http://") { + phrefURL = URL(string: href) + } else { + if relativePath.hasPrefix("/") { + relativePath.remove(at: relativePath.startIndex) + } + phrefURL = URL(string: relativePath, relativeTo: self.baseURL) ?? self.baseURL + } + //let hrefURL = URL(string: href) + guard let hrefURL = phrefURL else { return nil } var status: Int? let statusDesc = (node[statustag].string).components(separatedBy: " ") if statusDesc.count > 2 { @@ -539,10 +551,10 @@ internal extension WebDAVFileProvider { fileprivate func mapToFileObject(_ davResponse: DavResponse) -> WebDavFileObject { var href = davResponse.href if href.baseURL == nil { - href = absoluteURL(href.path) + href = url(of: href.path) } let name = davResponse.prop["displayname"] ?? (davResponse.hrefString.removingPercentEncoding! as NSString).lastPathComponent - let fileObject = WebDavFileObject(absoluteURL: href, name: name, path: href.path) + let fileObject = WebDavFileObject(url: href, name: name, path: href.path) fileObject.size = Int64(davResponse.prop["getcontentlength"] ?? "-1") ?? NSURLSessionTransferSizeUnknown fileObject.creationDate = self.resolve(dateString: davResponse.prop["creationdate"] ?? "") fileObject.modifiedDate = self.resolve(dateString: davResponse.prop["getlastmodified"] ?? "")