From ff4bbdf0de23e6ec06adebc3f0d443cee5884f3a Mon Sep 17 00:00:00 2001 From: Amir Abbas Mousavian Date: Sat, 1 Apr 2017 14:56:20 +0430 Subject: [PATCH] Updated readme and podspec for FTP, minor fixes. --- FileProvider.podspec | 6 ++-- FileProvider.xcodeproj/project.pbxproj | 4 +-- README.md | 45 ++++++++++++++++++-------- Sources/FTPFileProvider.swift | 16 +++------ Sources/FTPHelper.swift | 2 +- Sources/FileProvider.swift | 6 ++-- 6 files changed, 46 insertions(+), 33 deletions(-) diff --git a/FileProvider.podspec b/FileProvider.podspec index 3b7bb38..d44c59a 100644 --- a/FileProvider.podspec +++ b/FileProvider.podspec @@ -16,8 +16,8 @@ Pod::Spec.new do |s| # s.name = "FileProvider" - s.version = "0.14.5" - s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/OneDrive/SMB2) files on iOS and macOS." + s.version = "0.15.0" + s.summary = "FileManager replacement for Local and Remote (WebDAV/FTP/Dropbox/OneDrive/SMB2) files on iOS and macOS." # This description is used to generate tags and improve search results. # * Think: What does it do? Why did you write it? What is the focus? @@ -58,7 +58,7 @@ Pod::Spec.new do |s| s.author = { "Amir Abbas Mousavian" => "a.mosavian@gmail.com" } # Or just: s.author = "Amir Abbas Mousavian" # s.authors = { "Amir Abbas Mousavian" => "a.mosavian@gmail.com" } - # s.social_media_url = "https://twitter.com/amosavian" + s.social_media_url = "https://twitter.com/amosavian" # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # # diff --git a/FileProvider.xcodeproj/project.pbxproj b/FileProvider.xcodeproj/project.pbxproj index 8c8bb05..4d1eab1 100644 --- a/FileProvider.xcodeproj/project.pbxproj +++ b/FileProvider.xcodeproj/project.pbxproj @@ -621,7 +621,7 @@ 799396601D48B7BF00086753 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUNDLE_VERSION_STRING = 0.14.5; + BUNDLE_VERSION_STRING = 0.15.0; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; @@ -651,7 +651,7 @@ 799396611D48B7BF00086753 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUNDLE_VERSION_STRING = 0.14.5; + BUNDLE_VERSION_STRING = 0.15.0; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_EMPTY_BODY = YES; diff --git a/README.md b/README.md index 8564758..9aaebca 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ [![codecov](https://codecov.io/gh/amosavian/FileProvider/branch/master/graph/badge.svg)](https://codecov.io/gh/amosavian/FileProvider) ---> -This library provides implementaion of WebDav, Dropbox, OneDrive and SMB2 (incomplete) and local files. +This library provides implementaion of WebDav, FTP, Dropbox, OneDrive and SMB2 (incomplete) and local files. All functions do async calls and it wont block your main thread. @@ -28,7 +28,10 @@ All functions do async calls and it wont block your main thread. - [x] **LocalFileProvider** a wrapper around `FileManager` with some additions like builtin coordinating, searching and reading a portion of file. - [x] **CloudFileProvider** A wrapper around app's ubiquitous container API of iCloud Drive. -- [x] **WebDAVFileProvider** WebDAV protocol is defacto file transmission standard, used by many cloud services like Yandex. +- [x] **WebDAVFileProvider** WebDAV protocol is defacto file transmission standard, supported by some cloud services like `Box.com` and `Yandex.disk`. +- [x] **FTPFileProvider** While deprecated in 1990s due to serious security concerns, it's still in use on some Web hosts. + * Recursive directory removing & searching is not implemented yet. + * Active mode is not implemented yet (and probably won`t). - [x] **DropboxFileProvider** A wrapper around Dropbox Web API. * For now it has limitation in uploading files up to 150MB. - [x] **OneDriveFileProvider** A wrapper around OneDrive REST API, works with `onedrive.com` and compatible (business) servers. @@ -37,8 +40,7 @@ All functions do async calls and it wont block your main thread. - [ ] **AmazonS3FileProvider** Amazon storage backend. Used by many sites. - [ ] **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 deprecated and very tricky to be implemented. -- [ ] **FTPFileProvider** while deprecated in 1990s, it's still in use on some Web hosts. + * SMB1/CIFS is deprecated and very tricky to be implemented due to strict memory allignment in Swift. ## Requirements @@ -271,12 +273,7 @@ documentsProvider.create(folder: "new folder", at: "/", completionHandler: { err }) ``` -Creating new file from data: - -```swift -let data = "hello world!".data(encoding: .utf8) -documentsProvider.create(file: "newFile.txt", at: "/", contents: data, completionHandler: nil) -``` +To create a file, use `writeContents(path:, content:, atomically:, completionHandler:)` method. ### Copy and Move/Rename Files @@ -300,7 +297,7 @@ documentsProvider.moveItem(path: "new folder/old.txt", to: "new.txt", overwrite: documentsProvider.removeItem(path: "new.txt", completionHandler: nil) ``` -***Caution:*** This method will delete directories with all it's contents recursively. +***Caution:*** This method will delete directories with all it's contents recursively except for FTP providers that don't support `SITE RMDIR` command, this will be fixed later. ### Fetching Contents of File @@ -333,6 +330,27 @@ let data = "What's up Newyork!".data(encoding: .utf8) documentsProvider.writeContents(path: "old.txt", content: data, atomically: true, completionHandler: nil) ``` +### Copying Files to and From Local URL + +There are two methods to download and upload files between provider's and local storage. These methods use `URLSessionDownloadTask` and `URLSessionUploadTask` classes and allows to use background session and provide progress via delegate. + +To upload a file: + +```swift +let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("image.jpg") +documentsProvider.copyItem(localFile: fileURL, to: "/upload/image.jpg", overwrite: true, completionHandler: nil) +``` + +To download a file: + +```swift +let fileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("image.jpg") +documentsProvider.copyItem(path: "/download/image.jpg", toLocalURL: fileURL, overwrite: true, completionHandler: nil) +``` + +* It's safe only to assume these methods **won't** handle directories to upload/download recursively. If you need, you can list directories, create directories on target and copy files using these methods. +* FTP provider allows developer to either use apple implemented `URLSessionDownloadTask` or custom implemented method based on stream task via `useAppleImplementation` property. FTP protocol is not supported by background session. + ### Undo Operations Providers conform to `FileProviderUndoable` can perform undo for **some** operations like moving/renaming, copying and creating (file or folder). **Now, only `LocalFileProvider` supports this feature.** To implement: @@ -443,9 +461,10 @@ if documentsProvider..propertiesOfFile(path: file.path, completionHandler: { (pr We would love for you to contribute to **FileProvider**, check the `LICENSE` file for more info. -Things to do: +Things you may consider to help us: -- [ ] Implement Test-case (XCTest) +- [ ] Implement request/response stack for `SMBClient` +- [ ] Implement Test-case (`XCTest`) - [ ] Add Sample project for iOS - [ ] Add Sample project for macOS diff --git a/Sources/FTPFileProvider.swift b/Sources/FTPFileProvider.swift index 349ce5c..d727993 100644 --- a/Sources/FTPFileProvider.swift +++ b/Sources/FTPFileProvider.swift @@ -104,6 +104,7 @@ open class FTPFileProvider: FileProviderBasicRemote { self.currentPath = aDecoder.decodeObject(forKey: "currentPath") as? String ?? "" self.useCache = aDecoder.decodeBool(forKey: "useCache") self.validatingCache = aDecoder.decodeBool(forKey: "validatingCache") + self.useAppleImplementation = aDecoder.decodeBool(forKey: "useAppleImplementation") } public func encode(with aCoder: NSCoder) { @@ -112,6 +113,7 @@ open class FTPFileProvider: FileProviderBasicRemote { aCoder.encode(self.currentPath, forKey: "currentPath") aCoder.encode(self.useCache, forKey: "useCache") aCoder.encode(self.validatingCache, forKey: "validatingCache") + aCoder.encode(self.useAppleImplementation, forKey: "useAppleImplementation") } public static var supportsSecureCoding: Bool { @@ -683,18 +685,6 @@ extension FTPFileProvider: FileProviderReadWrite { return operation } - - /* - fileprivate func registerNotifcation(path: String, eventHandler: (() -> Void)) { - /* - * There is no ways to monitor folders changing in FTP. - */ - NotImplemented() - } - fileprivate func unregisterNotifcation(path: String) { - NotImplemented() - } - */ } extension FTPFileProvider { @@ -704,6 +694,8 @@ extension FTPFileProvider { to create symbolic links to locations that do not yet exist. Also, if the final path component is a symbolic link, that link is not followed. + - Note: Many servers does't support this functionality. + - Parameters: - symbolicLink: The file path at which to create the new symbolic link. The last component of the path issued as the name of the link. - withDestinationPath: The path that contains the item to be pointed to by the link. In other words, this is the destination of the link. diff --git a/Sources/FTPHelper.swift b/Sources/FTPHelper.swift index eaee21b..3e9192b 100644 --- a/Sources/FTPHelper.swift +++ b/Sources/FTPHelper.swift @@ -379,7 +379,7 @@ extension FTPFileProvider { } func ftpRecursiveList(_ task: FileProviderStreamTask, of path: String, useMLST: Bool, completionHandler: @escaping (_ contents: [String], _ error: Error?) -> Void) { - // TODO: Implement recursive listing + // TODO: Implement recursive listing for search and removing function } func ftpRetrieveData(_ task: FileProviderStreamTask, filePath: String, from position: Int64 = 0, length: Int = -1, onTask: ((_ task: FileProviderStreamTask) -> Void)?, onProgress: ((_ bytesReceived: Int64, _ totalReceived: Int64, _ expectedBytes: Int64) -> Void)?, completionHandler: @escaping (_ data: Data?, _ error: Error?) -> Void) { diff --git a/Sources/FileProvider.swift b/Sources/FileProvider.swift index 8b679e5..74dc2c6 100644 --- a/Sources/FileProvider.swift +++ b/Sources/FileProvider.swift @@ -350,6 +350,8 @@ public protocol FileProviderOperations: FileProviderBasic { Uploads a file from local file url to designated path asynchronously. Method will fail if source is not a local url with `file://` scheme. + - Note: It's safe to assume that this method only works on individual files and **won't** copy folders recursively. + - Parameters: - localFile: a file url to file. - to: destination path of file, including file/directory name. @@ -376,11 +378,11 @@ public protocol FileProviderOperations: FileProviderBasic { func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? } -extension FileProviderOperations { +public extension FileProviderOperations { /// *DEPRECATED:* Use Use FileProviderReadWrite.writeContents(path:, data:, completionHandler:) method instead. @available(*, deprecated, message: "Use FileProviderReadWrite.writeContents(path:, data:, completionHandler:) method instead.") @discardableResult - func create(file: String, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? { + public func create(file: String, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? { let path = (at as NSString).appendingPathComponent(file) return (self as? FileProviderReadWrite)?.writeContents(path: path, contents: data, completionHandler: completionHandler) }