diff --git a/FileProvider.podspec b/FileProvider.podspec index 086553d..3b7bb38 100644 --- a/FileProvider.podspec +++ b/FileProvider.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "FileProvider" - s.version = "0.14.4" + s.version = "0.14.5" 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 bb4c2df..8c39871 100644 --- a/FileProvider.xcodeproj/project.pbxproj +++ b/FileProvider.xcodeproj/project.pbxproj @@ -605,7 +605,7 @@ 799396601D48B7BF00086753 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUNDLE_VERSION_STRING = 0.14.4; + BUNDLE_VERSION_STRING = 0.14.5; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -635,7 +635,7 @@ 799396611D48B7BF00086753 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUNDLE_VERSION_STRING = 0.14.4; + BUNDLE_VERSION_STRING = 0.14.5; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -665,7 +665,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; - BUNDLE_VERSION_STRING = 0.8.2; CLANG_ANALYZER_NONNULL = YES; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -718,7 +717,6 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; - BUNDLE_VERSION_STRING = 0.8.2; CLANG_ANALYZER_NONNULL = YES; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; diff --git a/FileProvider.xcodeproj/xcshareddata/xcschemes/FileProvider OSX.xcscheme b/FileProvider.xcodeproj/xcshareddata/xcschemes/FileProvider OSX.xcscheme index b78d028..a054c27 100644 --- a/FileProvider.xcodeproj/xcshareddata/xcschemes/FileProvider OSX.xcscheme +++ b/FileProvider.xcodeproj/xcshareddata/xcschemes/FileProvider OSX.xcscheme @@ -1,6 +1,6 @@ Any { + let copy = CloudFileProvider(containerId: self.containerId, scope: self.scope) + copy?.currentPath = self.currentPath + copy?.delegate = self.delegate + copy?.fileOperationDelegate = self.fileOperationDelegate + return copy as Any + } + /** Returns an Array of `FileObject`s identifying the the directory entries via asynchronous completion handler. @@ -611,14 +638,6 @@ open class CloudFileProvider: LocalFileProvider { return monitors[path] != nil } - open override func copy(with zone: NSZone? = nil) -> Any { - let copy = CloudFileProvider(containerId: self.containerId) - copy?.currentPath = self.currentPath - copy?.delegate = self.delegate - copy?.fileOperationDelegate = self.fileOperationDelegate - return copy as Any - } - fileprivate func mapFileObject(attributes attribs: [String: Any]) -> FileObject? { guard let url = (attribs[NSMetadataItemURLKey] as? URL)?.standardizedFileURL, let name = attribs[NSMetadataItemFSNameKey] as? String else { return nil diff --git a/Sources/DropboxFileProvider.swift b/Sources/DropboxFileProvider.swift index 8582db2..c9b7718 100644 --- a/Sources/DropboxFileProvider.swift +++ b/Sources/DropboxFileProvider.swift @@ -86,7 +86,34 @@ open class DropboxFileProvider: FileProviderBasicRemote { dispatch_queue = DispatchQueue(label: "FileProvider.\(type(of: self).type)", attributes: .concurrent) operation_queue = OperationQueue() operation_queue.name = "FileProvider.\(type(of: self).type).Operation" - + } + + public required convenience init?(coder aDecoder: NSCoder) { + self.init(credential: aDecoder.decodeObject(forKey: "credential") as? URLCredential) + self.currentPath = aDecoder.decodeObject(forKey: "currentPath") as? String ?? "" + self.useCache = aDecoder.decodeBool(forKey: "useCache") + self.validatingCache = aDecoder.decodeBool(forKey: "validatingCache") + } + + public func encode(with aCoder: NSCoder) { + aCoder.encode(self.credential, forKey: "credential") + aCoder.encode(self.currentPath, forKey: "currentPath") + aCoder.encode(self.useCache, forKey: "useCache") + aCoder.encode(self.validatingCache, forKey: "validatingCache") + } + + public static var supportsSecureCoding: Bool { + return true + } + + open func copy(with zone: NSZone? = nil) -> Any { + let copy = DropboxFileProvider(credential: self.credential, cache: self.cache) + copy.currentPath = self.currentPath + copy.delegate = self.delegate + copy.fileOperationDelegate = self.fileOperationDelegate + copy.useCache = self.useCache + copy.validatingCache = self.validatingCache + return copy } deinit { @@ -348,16 +375,16 @@ extension DropboxFileProvider: FileProviderReadWrite { } extension DropboxFileProvider { - /// *DEPRECATED:* Use `publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?))` function instead. - @available(*, deprecated, renamed: "publicLink(to:completionHandler:)", message: "Use publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?)) function instead.") + /// *OBSOLETED:* Use `publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?))` function instead. + @available(*, obsoleted: 1.0, renamed: "publicLink(to:completionHandler:)", message: "Use publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?)) function instead.") open func temporaryLink(to path: String, completionHandler: @escaping ((_ link: URL?, _ attribute: DropboxFileObject?, _ error: Error?) -> Void)) { self.publicLink(to: path) { (url, file, _, error) in completionHandler(url, file, error) } } - /// *DEPRECATED:* Use `publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?))` function instead. - @available(*, deprecated, renamed: "publicLink(to:completionHandler:)", message: "Use publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?)) function instead.") + /// *OBSOLETED:* Use `publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?))` function instead. + @available(*, obsoleted: 1.0, renamed: "publicLink(to:completionHandler:)", message: "Use publicLink(to:, completionHandler: (URL?, DropboxFileObject?, Date?, Error?)) function instead.") open func temporaryLink(to path: String, completionHandler: @escaping ((_ link: URL?, _ attribute: DropboxFileObject?, _ expiration: Date?, _ error: Error?) -> Void)) { self.publicLink(to: path) { (url, file, expiration, error) in completionHandler(url, file, expiration, error) @@ -577,14 +604,4 @@ extension DropboxFileProvider: ExtendedFileProvider { } } -extension DropboxFileProvider: FileProvider { - open func copy(with zone: NSZone? = nil) -> Any { - let copy = DropboxFileProvider(credential: self.credential, cache: self.cache) - copy.currentPath = self.currentPath - copy.delegate = self.delegate - copy.fileOperationDelegate = self.fileOperationDelegate - copy.useCache = self.useCache - copy.validatingCache = self.validatingCache - return copy - } -} +extension DropboxFileProvider: FileProvider { } diff --git a/Sources/FileObject.swift b/Sources/FileObject.swift index 9e5d1a3..9aa015f 100644 --- a/Sources/FileObject.swift +++ b/Sources/FileObject.swift @@ -25,7 +25,7 @@ open class FileObject: Equatable { } /// url to access the resource, not supported by Dropbox provider - @available(*, deprecated, renamed: "url", message: "Use url.absoluteURL instead.") + @available(*, obsoleted: 1.0, renamed: "url", message: "Use url.absoluteURL instead.") open var absoluteURL: URL? { return url?.absoluteURL } diff --git a/Sources/FileProvider.swift b/Sources/FileProvider.swift index a505f81..2212503 100644 --- a/Sources/FileProvider.swift +++ b/Sources/FileProvider.swift @@ -19,7 +19,7 @@ public typealias ImageClass = NSImage public typealias SimpleCompletionHandler = ((_ error: Error?) -> Void)? /// This protocol defines FileProvider neccesary functions and properties to connect and get contents list -public protocol FileProviderBasic: class { +public protocol FileProviderBasic: class, NSCoding, NSSecureCoding { /// An string to identify type of provider. static var type: String { get } @@ -142,12 +142,6 @@ extension FileProviderBasic { self.searchFiles(path: path, recursive: recursive, query: predicate, foundItemHandler: foundItemHandler, completionHandler: completionHandler) } - /// Converts Spotlight search predicate to `FileProvider.searchFiles()` method usable predicate. - @available(*, obsoleted: 1.0, renamed: "FileObject.convertProdicate(fromSpotlight:)", message: "Use FileObject.convertProdicate(fromSpotlight:) instead.") - public func convertSpotlightPredicateTo(_ query: NSPredicate) -> NSPredicate { - return FileObject.convertPredicate(fromSpotlight: query) - } - /// The maximum number of queued operations that can execute at the same time. /// /// The default value of this property is `OperationQueue.defaultMaxConcurrentOperationCount`. @@ -159,6 +153,8 @@ extension FileProviderBasic { operation_queue.maxConcurrentOperationCount = newValue } } + + } /// Checking equality of two file provider, regardless of current path queues and delegates. @@ -610,9 +606,9 @@ extension FileProviderBasic { return type(of: self).type } - /// **DEPRECATED** This property never worked as expected and is redundant as only supported by `LocalFileProvider`. + /// **OBSOLETED** This property never worked as expected and is redundant as only supported by `LocalFileProvider`. /// To simulate `false` value, assign `URL(fileURLWithPath: "/")` to `baseURL`. - @available(*, deprecated, message: "Redundant property, now is always true.") + @available(*, obsoleted: 1.0, message: "Redundant property, now is always true.") var isPathRelative: Bool { return true } public func url(of path: String? = nil) -> URL { diff --git a/Sources/LocalFileProvider.swift b/Sources/LocalFileProvider.swift index 7fa7595..34f2662 100644 --- a/Sources/LocalFileProvider.swift +++ b/Sources/LocalFileProvider.swift @@ -42,12 +42,6 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndo */ open var isCoorinating: Bool - /// **OBSOLETED**: Use FileProvider.init(for:in:) instead. - @available(*, obsoleted: 1.0, renamed: "init(for:in:)", message: "Use FileProvider.init(for:in:) instead.") - public convenience init (directory: FileManager.SearchPathDirectory = .documentDirectory, domainMask: FileManager.SearchPathDomainMask = .userDomainMask) { - self.init(baseURL: FileManager.default.urls(for: directory, in: domainMask).first!) - } - /** Initializes provider for the specified common directory in the requested domains. default values are `directory: .documentDirectory, domainMask: .userDomainMask`. @@ -115,11 +109,39 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndo fileProviderManagerDelegate = LocalFileProviderManagerDelegate(provider: self) opFileManager.delegate = fileProviderManagerDelegate - } - /// **DEPRECATED:** No longer is in use and overriding this method has no effect anymore. - @available(*, deprecated, message: "Overriding this method has no effect anymore.") + public required convenience init?(coder aDecoder: NSCoder) { + guard let baseURL = aDecoder.decodeObject(forKey: "baseURL") as? URL else { + return nil + } + self.init(baseURL: baseURL) + self.currentPath = aDecoder.decodeObject(of: NSString.self, forKey: "currentPath") as? String ?? "" + self.isCoorinating = aDecoder.decodeBool(forKey: "isCoorinating") + } + + open func encode(with aCoder: NSCoder) { + aCoder.encode(self.baseURL, forKey: "currentPath") + aCoder.encode(self.currentPath, forKey: "currentPath") + aCoder.encode(self.isCoorinating, forKey: "isCoorinating") + } + + public static var supportsSecureCoding: Bool { + return true + } + + public func copy(with zone: NSZone? = nil) -> Any { + let copy = LocalFileProvider(baseURL: self.baseURL!) + copy.currentPath = self.currentPath + copy.undoManager = self.undoManager + copy.isCoorinating = self.isCoorinating + copy.delegate = self.delegate + copy.fileOperationDelegate = self.fileOperationDelegate + return copy + } + + /// **OBSOLETED:** No longer is in use and overriding this method has no effect anymore. + @available(*, obsoleted: 1.0, message: "Overriding this method has no effect anymore.") open class func defaultBaseURL() -> URL { return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! } @@ -505,16 +527,6 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndo open func isRegisteredForNotification(path: String) -> Bool { return monitors.map( { self.relativePathOf(url: $0.url) } ).contains(path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))) } - - open func copy(with zone: NSZone? = nil) -> Any { - let copy = LocalFileProvider(baseURL: self.baseURL!) - copy.currentPath = self.currentPath - copy.undoManager = self.undoManager - copy.isCoorinating = self.isCoorinating - copy.delegate = self.delegate - copy.fileOperationDelegate = self.fileOperationDelegate - return copy - } } public extension LocalFileProvider { diff --git a/Sources/OneDriveFileProvide.swift b/Sources/OneDriveFileProvide.swift index 0fe25b0..e57895a 100644 --- a/Sources/OneDriveFileProvide.swift +++ b/Sources/OneDriveFileProvide.swift @@ -81,6 +81,38 @@ open class OneDriveFileProvider: FileProviderBasicRemote { operation_queue.name = "FileProvider.\(type(of: self).type).Operation" } + public required convenience init?(coder aDecoder: NSCoder) { + self.init(credential: aDecoder.decodeObject(forKey: "credential") as? URLCredential, + serverURL: aDecoder.decodeObject(forKey: "baseURL") as? URL, + drive: aDecoder.decodeObject(forKey: "drive") as? String ?? "root") + self.currentPath = aDecoder.decodeObject(forKey: "currentPath") as? String ?? "" + self.useCache = aDecoder.decodeBool(forKey: "useCache") + self.validatingCache = aDecoder.decodeBool(forKey: "validatingCache") + } + + open func encode(with aCoder: NSCoder) { + aCoder.encode(self.credential, forKey: "credential") + aCoder.encode(self.baseURL, forKey: "baseURL") + aCoder.encode(self.drive, forKey: "drive") + aCoder.encode(self.currentPath, forKey: "currentPath") + aCoder.encode(self.useCache, forKey: "useCache") + aCoder.encode(self.validatingCache, forKey: "validatingCache") + } + + public static var supportsSecureCoding: Bool { + return true + } + + open func copy(with zone: NSZone? = nil) -> Any { + let copy = OneDriveFileProvider(credential: self.credential, serverURL: self.baseURL, drive: self.drive, cache: self.cache) + copy.currentPath = self.currentPath + copy.delegate = self.delegate + copy.fileOperationDelegate = self.fileOperationDelegate + copy.useCache = self.useCache + copy.validatingCache = self.validatingCache + return copy + } + deinit { if fileProviderCancelTasksOnInvalidating { _session?.invalidateAndCancel() @@ -442,14 +474,4 @@ extension OneDriveFileProvider: ExtendedFileProvider { } } -extension OneDriveFileProvider: FileProvider { - open func copy(with zone: NSZone? = nil) -> Any { - let copy = OneDriveFileProvider(credential: self.credential, serverURL: self.baseURL, drive: self.drive, cache: self.cache) - copy.currentPath = self.currentPath - copy.delegate = self.delegate - copy.fileOperationDelegate = self.fileOperationDelegate - copy.useCache = self.useCache - copy.validatingCache = self.validatingCache - return copy - } -} +extension OneDriveFileProvider: FileProvider { } diff --git a/Sources/RemoteSession.swift b/Sources/RemoteSession.swift index d0b4542..367657a 100644 --- a/Sources/RemoteSession.swift +++ b/Sources/RemoteSession.swift @@ -77,18 +77,22 @@ extension FileProviderHTTPError { public var description: String { return code.description } + + public var localizedDescription: String { + return description + } } class SessionDelegate: NSObject, URLSessionDataDelegate, URLSessionDownloadDelegate { - weak var fileProvider: FileProvider? + weak var fileProvider: (FileProviderBasicRemote & FileProviderOperations)? var credential: URLCredential? var finishDownloadHandler: ((_ session: Foundation.URLSession, _ downloadTask: URLSessionDownloadTask, _ didFinishDownloadingToURL: URL) -> Void)? var didSendDataHandler: ((_ session: Foundation.URLSession, _ task: URLSessionTask, _ bytesSent: Int64, _ totalBytesSent: Int64, _ totalBytesExpectedToSend: Int64) -> Void)? var didReceivedData: ((_ session: Foundation.URLSession, _ downloadTask: URLSessionDownloadTask, _ bytesWritten: Int64, _ totalBytesWritten: Int64, _ totalBytesExpectedToWrite: Int64) -> Void)? - init(fileProvider: FileProvider, credential: URLCredential?) { + init(fileProvider: FileProviderBasicRemote & FileProviderOperations, credential: URLCredential?) { self.fileProvider = fileProvider self.credential = credential } diff --git a/Sources/SMBFileProvider.swift b/Sources/SMBFileProvider.swift index 4c8a384..f7ac059 100644 --- a/Sources/SMBFileProvider.swift +++ b/Sources/SMBFileProvider.swift @@ -19,7 +19,7 @@ class SMBFileProvider: FileProvider, FileProviderMonitor { public typealias FileObjectClass = FileObject - public init? (baseURL: URL, credential: URLCredential, afterInitialized: SimpleCompletionHandler) { + public init? (baseURL: URL, credential: URLCredential?) { guard baseURL.uw_scheme.lowercased() == "smb" else { return nil } @@ -30,7 +30,26 @@ class SMBFileProvider: FileProvider, FileProviderMonitor { self.credential = credential } - + + public required convenience init?(coder aDecoder: NSCoder) { + guard let baseURL = aDecoder.decodeObject(forKey: "baseURL") as? URL else { + return nil + } + self.init(baseURL: baseURL, + credential: aDecoder.decodeObject(forKey: "credential") as? URLCredential) + self.currentPath = aDecoder.decodeObject(forKey: "currentPath") as? String ?? "" + } + + open func encode(with aCoder: NSCoder) { + aCoder.encode(self.baseURL, forKey: "baseURL") + aCoder.encode(self.credential, forKey: "credential") + aCoder.encode(self.currentPath, forKey: "currentPath") + } + + public static var supportsSecureCoding: Bool { + return true + } + open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObjectClass], _ error: Error?) -> Void)) { NotImplemented() } @@ -116,7 +135,7 @@ class SMBFileProvider: FileProvider, FileProviderMonitor { } open func copy(with zone: NSZone? = nil) -> Any { - let copy = SMBFileProvider(baseURL: self.baseURL!, credential: self.credential!, afterInitialized: { _ in })! + let copy = SMBFileProvider(baseURL: self.baseURL!, credential: self.credential!)! copy.currentPath = self.currentPath copy.delegate = self.delegate copy.fileOperationDelegate = self.fileOperationDelegate diff --git a/Sources/WebDAVFileProvider.swift b/Sources/WebDAVFileProvider.swift index 659e086..f77e6eb 100644 --- a/Sources/WebDAVFileProvider.swift +++ b/Sources/WebDAVFileProvider.swift @@ -75,6 +75,39 @@ open class WebDAVFileProvider: FileProviderBasicRemote { operation_queue.name = "FileProvider.\(type(of: self).type).Operation" } + public required convenience init?(coder aDecoder: NSCoder) { + guard let baseURL = aDecoder.decodeObject(forKey: "baseURL") as? URL else { + return nil + } + self.init(baseURL: baseURL, + credential: aDecoder.decodeObject(forKey: "credential") as? URLCredential) + self.currentPath = aDecoder.decodeObject(forKey: "currentPath") as? String ?? "" + self.useCache = aDecoder.decodeBool(forKey: "useCache") + self.validatingCache = aDecoder.decodeBool(forKey: "validatingCache") + } + + open func encode(with aCoder: NSCoder) { + aCoder.encode(self.baseURL, forKey: "baseURL") + aCoder.encode(self.credential, forKey: "credential") + aCoder.encode(self.currentPath, forKey: "currentPath") + aCoder.encode(self.useCache, forKey: "isCoorinating") + aCoder.encode(self.validatingCache, forKey: "undoManager") + } + + public static var supportsSecureCoding: Bool { + return true + } + + open func copy(with zone: NSZone? = nil) -> Any { + let copy = WebDAVFileProvider(baseURL: self.baseURL!, credential: self.credential, cache: self.cache)! + copy.currentPath = self.currentPath + copy.delegate = self.delegate + copy.fileOperationDelegate = self.fileOperationDelegate + copy.useCache = self.useCache + copy.validatingCache = self.validatingCache + return copy + } + deinit { if fileProviderCancelTasksOnInvalidating { _session?.invalidateAndCancel() @@ -463,17 +496,7 @@ extension WebDAVFileProvider: FileProviderReadWrite { // TODO: implements methods for lock mechanism } -extension WebDAVFileProvider: FileProvider { - open func copy(with zone: NSZone? = nil) -> Any { - let copy = WebDAVFileProvider(baseURL: self.baseURL!, credential: self.credential, cache: self.cache)! - copy.currentPath = self.currentPath - copy.delegate = self.delegate - copy.fileOperationDelegate = self.fileOperationDelegate - copy.useCache = self.useCache - copy.validatingCache = self.validatingCache - return copy - } -} +extension WebDAVFileProvider: FileProvider { } // MARK: WEBDAV XML response implementation @@ -524,8 +547,13 @@ struct DavResponse { guard let hrefString = node[hreftag].value else { return nil } // trying to figure out relative path out of href - let hrefAbsolute = URL(string: hrefString, relativeTo: baseURL)?.absoluteString ?? hrefString - let relativePath = hrefAbsolute.replacingOccurrences(of: baseURL?.absoluteString ?? "", with: "", options: .anchored, range: nil) + let hrefAbsolute = URL(string: hrefString, relativeTo: baseURL)?.absoluteURL + let relativePath: String + if hrefAbsolute?.host?.replacingOccurrences(of: "www.", with: "", options: .anchored) == baseURL?.host?.replacingOccurrences(of: "www.", with: "", options: .anchored) { + relativePath = hrefAbsolute?.path.replacingOccurrences(of: baseURL?.absoluteURL.path ?? "", with: "", options: .anchored, range: nil) ?? hrefString + } else { + relativePath = hrefAbsolute?.absoluteString.replacingOccurrences(of: baseURL?.absoluteString ?? "", with: "", options: .anchored, range: nil) ?? hrefString + } let hrefURL = URL(string: removeSlash(relativePath), relativeTo: baseURL) ?? baseURL guard let href = hrefURL?.standardized else { return nil }