diff --git a/FileProvider.podspec b/FileProvider.podspec index 1f8e93c..6bbeb8e 100644 --- a/FileProvider.podspec +++ b/FileProvider.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "FileProvider" - s.version = "0.10.2" + s.version = "0.10.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 09e0c69..b67acc4 100644 --- a/FileProvider.xcodeproj/project.pbxproj +++ b/FileProvider.xcodeproj/project.pbxproj @@ -595,7 +595,7 @@ 799396601D48B7BF00086753 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUNDLE_VERSION_STRING = 0.10.2; + BUNDLE_VERSION_STRING = 0.10.3; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -625,7 +625,7 @@ 799396611D48B7BF00086753 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUNDLE_VERSION_STRING = 0.10.2; + BUNDLE_VERSION_STRING = 0.10.3; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; diff --git a/README.md b/README.md index 9fa5e44..0f5910f 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Local and WebDAV providers are fully tested and can be used in production enviro * For now it has limitation in uploading files up to 150MB. - [x] **OneDriveFileProvider** A wrapper around OneDrive Web API, works with `onedrive.com` and compatible (business) servers. * For now it has limitation in uploading files up to 100MB. -- [ ] **CloudFilePRovider** A wrapper around app's ubiquitous container to iCloud Drive in iOS 8+ API. +- [x] **CloudFileProvider** A wrapper around app's ubiquitous container to iCloud Drive in iOS 8+ API. - [ ] **SMBFileProvider** SMB2/3 introduced in 2006, which is a file and printer sharing protocol originated from Microsoft Windows and now is replacing AFP protocol on MacOS. * Data types and some basic functions are implemented but *main interface is not implemented yet!* * SMB1/CIFS is depericated and very tricky to be implemented @@ -103,18 +103,23 @@ For LocalFileProvider if you want to deal with `Documents` folder ``` swift let documentsProvider = LocalFileProvider() -``` -is equal to: +// Equals with: +let documentsProvider = LocalFileProvider(directory: .documentDirectory, domainMask: = .userDomainMask) -``` swift -let documentPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true); -let documentsURL = URL(fileURLWithPath: documentPath); +// Equals with: +let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let documentsProvider = LocalFileProvider(baseURL: documentsURL) ``` You can't change the base url later. and all paths are related to this base url by default. +To initialize an iCloud Container provider use below code, This will automatically manager creating Documents folder in container: + +```swift +let documentsProvider = CloudFileProvider(containerId: nil) +``` + For remote file providers authentication may be necessary: ``` swift diff --git a/Sources/DropboxFileProvider.swift b/Sources/DropboxFileProvider.swift index 486bcb5..f3b1cf4 100644 --- a/Sources/DropboxFileProvider.swift +++ b/Sources/DropboxFileProvider.swift @@ -13,14 +13,14 @@ import CoreGraphics // Because this class uses NSURLSession, it's necessary to disable App Transport Security // in case of using this class with unencrypted HTTP connection. -open class DropboxFileProvider: NSObject, FileProviderBasicRemote { +open class DropboxFileProvider: FileProviderBasicRemote { open static let type: String = "DropBox" - open let isPathRelative: Bool = true + open let isPathRelative: Bool open let baseURL: URL? - open var currentPath: String = "" + open var currentPath: String - open let apiURL = URL(string: "https://api.dropboxapi.com/2/")! - open let contentURL = URL(string: "https://content.dropboxapi.com/2")! + open let apiURL: URL + open let contentURL: URL open var dispatch_queue: DispatchQueue { willSet { @@ -30,8 +30,8 @@ open class DropboxFileProvider: NSObject, FileProviderBasicRemote { open weak var delegate: FileProviderDelegate? open let credential: URLCredential? open private(set) var cache: URLCache? - public var useCache: Bool = false - public var validatingCache: Bool = true + public var useCache: Bool + public var validatingCache: Bool fileprivate var _session: URLSession? fileprivate var sessionDelegate: SessionDelegate? @@ -50,10 +50,17 @@ open class DropboxFileProvider: NSObject, FileProviderBasicRemote { public init? (credential: URLCredential?, cache: URLCache? = nil) { self.baseURL = nil - dispatch_queue = DispatchQueue(label: "FileProvider.\(DropboxFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent) - //let url = baseURL.uw_absoluteString - self.credential = credential + self.isPathRelative = true + self.currentPath = "" + self.useCache = false + self.validatingCache = true self.cache = cache + self.credential = credential + + 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) } deinit { diff --git a/Sources/LocalFileProvider.swift b/Sources/LocalFileProvider.swift index 55ef76c..ed7b065 100644 --- a/Sources/LocalFileProvider.swift +++ b/Sources/LocalFileProvider.swift @@ -9,37 +9,38 @@ import Foundation open class LocalFileProvider: FileProvider, FileProviderMonitor { - open static let type = "Local" - open var isPathRelative: Bool = true - open private(set) var baseURL: URL? = LocalFileProvider.defaultBaseURL() - open var currentPath: String = "" + open static let type: String = "Local" + open var isPathRelative: Bool + open fileprivate(set) var baseURL: URL? + open var currentPath: String open var dispatch_queue: DispatchQueue open var operation_queue: DispatchQueue open weak var delegate: FileProviderDelegate? - open let credential: URLCredential? = nil + open fileprivate(set) var credential: URLCredential? open private(set) var fileManager = FileManager() open private(set) var opFileManager = FileManager() fileprivate var fileProviderManagerDelegate: LocalFileProviderManagerDelegate? = nil - public init () { - dispatch_queue = DispatchQueue(label: "FileProvider.\(LocalFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent) - operation_queue = DispatchQueue(label: "FileProvider.\(LocalFileProvider.type).Operation", attributes: []) - fileProviderManagerDelegate = LocalFileProviderManagerDelegate(provider: self) - opFileManager.delegate = fileProviderManagerDelegate + /// default values are `directory: .documentDirectory, domainMask: .userDomainMask` + public convenience init (directory: FileManager.SearchPathDirectory = .documentDirectory, domainMask: FileManager.SearchPathDomainMask = .userDomainMask) { + self.init(baseURL: FileManager.default.urls(for: directory, in: domainMask).first!) } public init (baseURL: URL) { self.baseURL = baseURL + self.isPathRelative = true + self.currentPath = "" + self.credential = nil + dispatch_queue = DispatchQueue(label: "FileProvider.\(LocalFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent) operation_queue = DispatchQueue(label: "FileProvider.\(LocalFileProvider.type).Operation", attributes: []) fileProviderManagerDelegate = LocalFileProviderManagerDelegate(provider: self) opFileManager.delegate = fileProviderManagerDelegate } - open static func defaultBaseURL() -> URL { - let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true); - return URL(fileURLWithPath: paths[0]) + open class func defaultBaseURL() -> URL { + return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! } open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) { @@ -356,3 +357,33 @@ public extension LocalFileProvider { } } } + +class CloudFileProvider: LocalFileProvider { + // FIXME: convert static var type to class var in next Swift version + + open var type: String { return "iCloudDrive" } + + public init? (containerId: String?) { + assert(!Thread.isMainThread, "LocalFileProvider.init(containerId:) is not recommended to be executed on Main Thread.") + guard FileManager.default.ubiquityIdentityToken == nil else { + return nil + } + guard let ubiquityURL = FileManager.default.url(forUbiquityContainerIdentifier: containerId) else { + return nil + } + super.init(baseURL: ubiquityURL.appendingPathComponent("Documents")) + + dispatch_queue = DispatchQueue(label: "FileProvider.\(self.type)", attributes: DispatchQueue.Attributes.concurrent) + operation_queue = DispatchQueue(label: "FileProvider.\(self.type).Operation", attributes: []) + + fileManager.url(forUbiquityContainerIdentifier: containerId) + opFileManager.url(forUbiquityContainerIdentifier: containerId) + fileProviderManagerDelegate = LocalFileProviderManagerDelegate(provider: self) + opFileManager.delegate = fileProviderManagerDelegate + } + + open override static func defaultBaseURL() -> URL { + return FileManager.default.url(forUbiquityContainerIdentifier: nil) ?? super.defaultBaseURL() + } + +} diff --git a/Sources/OneDriveFileProvide.swift b/Sources/OneDriveFileProvide.swift index a1d54a0..219b087 100644 --- a/Sources/OneDriveFileProvide.swift +++ b/Sources/OneDriveFileProvide.swift @@ -13,15 +13,16 @@ import CoreGraphics // Because this class uses NSURLSession, it's necessary to disable App Transport Security // in case of using this class with unencrypted HTTP connection. -open class OneDriveFileProvider: NSObject, FileProviderBasicRemote { +open class OneDriveFileProvider: FileProviderBasicRemote { open static let type: String = "OneDrive" - open let isPathRelative: Bool = true + open let isPathRelative: Bool open let baseURL: URL? + open var serverURL: URL { return baseURL! } open var drive: String open var driveURL: URL { return URL(string: "/drive/\(drive):/", relativeTo: baseURL)! } - open var currentPath: String = "" + open var currentPath: String open var dispatch_queue: DispatchQueue { willSet { @@ -31,8 +32,8 @@ open class OneDriveFileProvider: NSObject, FileProviderBasicRemote { open weak var delegate: FileProviderDelegate? open let credential: URLCredential? open private(set) var cache: URLCache? - public var useCache: Bool = false - public var validatingCache: Bool = true + public var useCache: Bool + public var validatingCache: Bool fileprivate var _session: URLSession? fileprivate var sessionDelegate: SessionDelegate? @@ -49,13 +50,16 @@ open class OneDriveFileProvider: NSObject, FileProviderBasicRemote { return _session! } - public init? (baseURL: URL?, drive: String = "root", credential: URLCredential?, cache: URLCache? = nil) { - self.baseURL = baseURL ?? URL(string: "https://api.onedrive.com") + public init? (credential: URLCredential?, serverURL: URL? = nil, drive: String = "root", cache: URLCache? = nil) { + self.baseURL = serverURL ?? URL(string: "https://api.onedrive.com") self.drive = drive - dispatch_queue = DispatchQueue(label: "FileProvider.\(OneDriveFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent) - //let url = baseURL.uw_absoluteString - self.credential = credential + self.isPathRelative = true + self.currentPath = "" + self.useCache = false + self.validatingCache = true self.cache = cache + self.credential = credential + dispatch_queue = DispatchQueue(label: "FileProvider.\(OneDriveFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent) } deinit { @@ -341,7 +345,7 @@ extension OneDriveFileProvider: ExtendedFileProvider { extension OneDriveFileProvider: FileProvider { open func copy(with zone: NSZone? = nil) -> Any { - let copy = OneDriveFileProvider(baseURL: self.baseURL, drive: self.drive, credential: self.credential, cache: self.cache)! + 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 diff --git a/Sources/WebDAVFileProvider.swift b/Sources/WebDAVFileProvider.swift index 5311ca5..819738e 100644 --- a/Sources/WebDAVFileProvider.swift +++ b/Sources/WebDAVFileProvider.swift @@ -35,11 +35,11 @@ public final class WebDavFileObject: FileObject { /// Because this class uses NSURLSession, it's necessary to disable App Transport Security /// in case of using this class with unencrypted HTTP connection. -open class WebDAVFileProvider: NSObject, FileProviderBasicRemote { +open class WebDAVFileProvider: FileProviderBasicRemote { open static let type: String = "WebDAV" - open let isPathRelative: Bool = true + open let isPathRelative: Bool open let baseURL: URL? - open var currentPath: String = "" + open var currentPath: String public var dispatch_queue: DispatchQueue { willSet { assert(_session == nil, "It's not effective to change dispatch_queue property after session is initialized.") @@ -48,8 +48,8 @@ open class WebDAVFileProvider: NSObject, FileProviderBasicRemote { public weak var delegate: FileProviderDelegate? open let credential: URLCredential? open private(set) var cache: URLCache? - public var useCache: Bool = false - public var validatingCache: Bool = true + public var useCache: Bool + public var validatingCache: Bool fileprivate var _session: URLSession? fileprivate var sessionDelegate: SessionDelegate? @@ -71,10 +71,13 @@ open class WebDAVFileProvider: NSObject, FileProviderBasicRemote { return nil } self.baseURL = baseURL - dispatch_queue = DispatchQueue(label: "FileProvider.\(WebDAVFileProvider.type)", attributes: DispatchQueue.Attributes.concurrent) - //let url = baseURL.uw_absoluteString - self.credential = credential + 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) } deinit {