From e4dda12d8ac0c69cd81ec2b5efc034626f2126a3 Mon Sep 17 00:00:00 2001 From: Amir Abbas Mousavian Date: Wed, 29 Jun 2016 02:21:27 +0430 Subject: [PATCH] Updated Readme WebDAV delegate calling --- README.md | 154 +++++++++++++++++- .../FileProvider.swift | 0 .../LocalFileProvider.swift | 5 +- .../SMBFileProvider.swift | 26 +-- .../WebDAVFileProvider.swift | 42 ++++- 5 files changed, 203 insertions(+), 24 deletions(-) rename FileProvider.swift => Source/FileProvider.swift (100%) rename LocalFileProvider.swift => Source/LocalFileProvider.swift (98%) rename SMBFileProvider.swift => Source/SMBFileProvider.swift (99%) rename WebDAVFileProvider.swift => Source/WebDAVFileProvider.swift (91%) diff --git a/README.md b/README.md index e960375..2885c2a 100644 --- a/README.md +++ b/README.md @@ -1 +1,153 @@ -# FileProvider \ No newline at end of file +# FileProvider + +This Swift library provide a swifty way to deal with local and remote files and directories in same way. This library provides implementaion of WebDav and SMB/CIFS (incomplete) and local files. + +All functions are async recalls and it wont block your main thread. + +## Installation + +Copy Source folder to your project! + +## Usage + +Each provider has a specific class which conforms to FileProvider protocol and share same syntax + +### Providers + +For now this providers are supported: + +**LocalFileProvider :** a wrapper for NSFileManager with some additions like searching and reading a portion of file + +**WebDAVFileProvider :** WebDAV protocol is usual file transmission system on Macs + +**SMBFileProvider :** SMB/CIFS and SMB2/3 are file and printer sharing protocol which is originated from Windows and SMB2/3 is now replacing AFP protocol on MacOS too. I Implemented data types and some basic functions but main interface is not implemented yet! + +### Initialization + +For LocalFileProvider if you want to deal with `Documents` folder + + let documentsFileProvider = LocalFileProvider() + +is equal to: + + let documentPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true); + let documentsURL = NSURL(fileURLWithPath: documentPath); + let documentsFileProvider = LocalFileProvider(baseURL: documentsURL) + +You can't change the base url later. and all paths are related to this base url by default. + +For remote file providers authentication may be necessary: + + let credential = NSURLCredential(user: "user", password: "pass", persistence: NSURLCredentialPersistence.Permanent) + let webdavProvider = WebDAVFileProvider(baseURL: "http://www.example.com/dav", credential: credential) + +For interaction with UI, set delegate variable of `FileProvider` object + +### Delegate + +For updating User interface please consider using delegate method instead of completion handlers. Delegate methods are guaranteed to run in main thread to avoid bugs. + + override func viewDidLoad() { + documentsFileProvider.delegate = self + } + + func fileproviderCreateModifyNotify(fileProvider: LocalFileProvider, path: String) { + NSLog("File \(path) modified") + } + + func fileproviderCopyNotify(fileProvider: LocalFileProvider, fromPath: String, toPath: String) { + NSLog("File \(path) copied") + } + + func fileproviderMoveNotify(fileProvider: LocalFileProvider, fromPath: String, toPath: String) { + NSLog("File \(path) moved") + } + + func fileproviderRemoveNotify(fileProvider: LocalFileProvider, path: String) { + NSLog("File \(path) deleted") + } + +Use completion handlers for error handling or result processing as far as possible. + +### Directory contents and file attributes + +There is a `FileObject` class which holds file attributes like size and creation date. You can retrieve information of files inside a directory or get information of a file directly + + documentsFileProvider.attributesOfItemAtPath(path: "/file.txt", completionHandler: { + (attributes: LocalFileObject?, error: ErrorType?) -> Void} in + if let attributes = attributes { + print("File Size: \(attributes.size)") + print("Creation Date: \(attributes.createdDate)") + print("Modification Date: \(modifiedDate)") + print("Is Read Only: \(isReadOnly)") + } + ) + + documentsFileProvider.contentsOfDirectoryAtPath(path: "/", completionHandler: { + (contents: [LocalFileObject], error: ErrorType?) -> Void} in + for file in contents { + print("Name: \(attributes.name)") + print("Size: \(attributes.size)") + print("Creation Date: \(attributes.createdDate)") + print("Modification Date: \(modifiedDate)") + } + ) + +### Change current directory + + documentsFileProvider.currentPath = "/New Folder" + // now path is ~/Documents/New Folder + +### Creating File and Folders + +Creating new directory: + + documentsFileProvider.createFolder(folderName: "new folder", atPath: "/", completionHandler: nil) + +Creating new file from data stream: + + let data = "hello world!".dataUsingEncoding(NSUTF8StringEncoding) + let file = FileObject(absoluteURL: NSURL(), name: "old.txt", size: -1, createdDate: NSDate(), modifiedDate: NSDate(), fileType: .Regular, isHidden: false, isReadOnly: true) + documentsFileProvider.createFile(fileAttribs: file, atPath: "/", contents: data, completionHandler: nil) + +### Copy and Move/Rename Files + + // Copy file old.txt to new.txt in current path + documentsFileProvider.copyItemAtPath(path: "new folder/old.txt", toPath: "new.txt", overwrite: false, completionHandler: nil) + + // Move file old.txt to new.txt in current path + documentsFileProvider.moveItemAtPath(path: "new folder/old.txt", toPath: "new.txt", overwrite: false, completionHandler: nil) + +### Delete Files + + documentsFileProvider.removeItemAtPath(path: "new.txt", completionHandler: nil) + +Caution: This method will not delete directories with content. + + +### Retrieve Content of File + +THere is two method for this purpose, one of them loads entire file into NSData and another can load a portion of file. + + documentsFileProvider.contentsAtPath(path: "old.txt:, completionHandler: { + (contents: NSData?, error: ErrorType?) -> Void + if let contents = contents { + print(String(data: contents, encoding: NSUTF8StringEncoding)) // "hello world!" + } + }) + +If you want to retrieve a portion of file you should can `contentsAtPath` method with offset and length arguments. Please note first byte of file has offset: 0. + + documentsFileProvider.contentsAtPath(path: "old.txt:, offset: 2, length: 5, completionHandler: { + (contents: NSData?, error: ErrorType?) -> Void + if let contents = contents { + print(String(data: contents, encoding: NSUTF8StringEncoding)) // "llo w" + } + }) + +### Write Data To Files + + let data = "What's up Newyork!".dataUsingEncoding(NSUTF8StringEncoding) + documentsFileProvider.writeContentsAtPath(path: "old, contents data: data, atomically: true, completionHandler: nil) + + diff --git a/FileProvider.swift b/Source/FileProvider.swift similarity index 100% rename from FileProvider.swift rename to Source/FileProvider.swift diff --git a/LocalFileProvider.swift b/Source/LocalFileProvider.swift similarity index 98% rename from LocalFileProvider.swift rename to Source/LocalFileProvider.swift index 0d87851..fd3a4b5 100644 --- a/LocalFileProvider.swift +++ b/Source/LocalFileProvider.swift @@ -112,6 +112,7 @@ class LocalFileProvider: FileProvider { dispatch_async(dispatch_get_main_queue(), { self.delegate?.fileproviderCreateModifyNotify(self, path: atPath) }) + } else { completionHandler?(error: self.throwError(atPath, code: NSURLError.CannotCreateFile)) } } @@ -267,8 +268,4 @@ extension LocalFileProvider { } } } - - func extendedAttributes(path: String) -> FileExtendedAttributes { - return FileExtendedAttributes(fileURL: self.absoluteURL(path)) - } } diff --git a/SMBFileProvider.swift b/Source/SMBFileProvider.swift similarity index 99% rename from SMBFileProvider.swift rename to Source/SMBFileProvider.swift index be97fe4..3381e02 100644 --- a/SMBFileProvider.swift +++ b/Source/SMBFileProvider.swift @@ -115,56 +115,56 @@ class SMBFileProvider: NSObject, FileProvider, NSStreamDelegate { func contentsOfDirectoryAtPath(path: String, completionHandler: ((contents: [FileObjectClass], error: ErrorType?) -> Void)) { dispatch_async(dispatch_queue) { - + self.NotImplemented() } } func attributesOfItemAtPath(path: String, completionHandler: ((attributes: FileObjectClass?, error: ErrorType?) -> Void)) { - + NotImplemented() } func createFolder(folderName: String, atPath: String, completionHandler: SimpleCompletionHandler) { - + NotImplemented() } func createFile(fileAttribs: FileObject, atPath: String, contents data: NSData?, completionHandler: SimpleCompletionHandler) { - + NotImplemented() } func moveItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) { - + NotImplemented() } func copyItemAtPath(path: String, toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) { - + NotImplemented() } func removeItemAtPath(path: String, completionHandler: SimpleCompletionHandler) { - + NotImplemented() } func copyLocalFileToPath(localFile: NSURL, toPath: String, completionHandler: SimpleCompletionHandler) { - + NotImplemented() } func copyPathToLocalFile(path: String, toLocalURL: NSURL, completionHandler: SimpleCompletionHandler) { - + NotImplemented() } func contentsAtPath(path: String, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) { - + NotImplemented() } func contentsAtPath(path: String, offset: Int64, length: Int, completionHandler: ((contents: NSData?, error: ErrorType?) -> Void)) { - + NotImplemented() } func writeContentsAtPath(path: String, contents data: NSData, atomically: Bool, completionHandler: SimpleCompletionHandler) { - + NotImplemented() } func searchFilesAtPath(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObjectClass) -> Void)?, completionHandler: ((files: [FileObjectClass], error: ErrorType?) -> Void)) { - + NotImplemented() } } diff --git a/WebDAVFileProvider.swift b/Source/WebDAVFileProvider.swift similarity index 91% rename from WebDAVFileProvider.swift rename to Source/WebDAVFileProvider.swift index c1f4712..cc872ff 100644 --- a/WebDAVFileProvider.swift +++ b/Source/WebDAVFileProvider.swift @@ -169,6 +169,9 @@ class WebDAVFileProvider: NSObject, FileProvider { return } completionHandler?(error: error) + dispatch_async(dispatch_get_main_queue(), { + self.delegate?.fileproviderCreateModifyNotify(self, path: atPath) + }) }.resume() } @@ -177,6 +180,9 @@ class WebDAVFileProvider: NSObject, FileProvider { request.HTTPMethod = "PUT" session.uploadTaskWithRequest(request, fromData: data) { (data, response, error) in completionHandler?(error: error) + dispatch_async(dispatch_get_main_queue(), { + self.delegate?.fileproviderCreateModifyNotify(self, path: path) + }) }.resume() } @@ -191,8 +197,12 @@ class WebDAVFileProvider: NSObject, FileProvider { request.setValue("F", forHTTPHeaderField: "Overwrite") } session.dataTaskWithRequest(request) { (data, response, error) in - if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) where code != .OK { - if code == .MultiStatus, let data = data { + if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) { + if code == .OK { + dispatch_async(dispatch_get_main_queue(), { + self.delegate?.fileproviderMoveNotify(self, fromPath: path, toPath: toPath) + }) + } else if code == .MultiStatus, let data = data { let xresponses = self.parseXMLResponse(data) for xresponse in xresponses { if xresponse.status >= 300 { @@ -202,6 +212,9 @@ class WebDAVFileProvider: NSObject, FileProvider { } else { completionHandler?(error: FileProviderWebDavError(code: code, url: url)) } + dispatch_async(dispatch_get_main_queue(), { + self.delegate?.fileproviderMoveNotify(self, fromPath: path, toPath: toPath) + }) return } completionHandler?(error: error) @@ -219,14 +232,21 @@ class WebDAVFileProvider: NSObject, FileProvider { request.setValue("F", forHTTPHeaderField: "Overwrite") } session.dataTaskWithRequest(request) { (data, response, error) in - if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) where code != .OK { - if code == .MultiStatus, let data = data { + if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) { + if code == .OK { + dispatch_async(dispatch_get_main_queue(), { + self.delegate?.fileproviderCopyNotify(self, fromPath: path, toPath: toPath) + }) + } else if code == .MultiStatus, let data = data { let xresponses = self.parseXMLResponse(data) for xresponse in xresponses { if xresponse.status >= 300 { completionHandler?(error: FileProviderWebDavError(code: code, url: url)) } } + dispatch_async(dispatch_get_main_queue(), { + self.delegate?.fileproviderCopyNotify(self, fromPath: path, toPath: toPath) + }) } else { completionHandler?(error: FileProviderWebDavError(code: code, url: url)) } @@ -243,14 +263,21 @@ class WebDAVFileProvider: NSObject, FileProvider { request.HTTPMethod = "DELETE" request.setValue(baseURL?.absoluteString, forHTTPHeaderField: "Host") session.dataTaskWithRequest(request) { (data, response, error) in - if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) where code != .OK { - if code == .MultiStatus, let data = data { + if let response = response as? NSHTTPURLResponse, let code = FileProviderWebDavErrorCode(rawValue: response.statusCode) { + if code == .OK { + dispatch_async(dispatch_get_main_queue(), { + self.delegate?.fileproviderRemoveNotify(self, path: path) + }) + } else if code == .MultiStatus, let data = data { let xresponses = self.parseXMLResponse(data) for xresponse in xresponses { if xresponse.status >= 300 { completionHandler?(error: FileProviderWebDavError(code: code, url: url)) } } + dispatch_async(dispatch_get_main_queue(), { + self.delegate?.fileproviderRemoveNotify(self, path: path) + }) } else { completionHandler?(error: FileProviderWebDavError(code: code, url: url)) } @@ -265,6 +292,9 @@ class WebDAVFileProvider: NSObject, FileProvider { request.HTTPMethod = "PUT" session.uploadTaskWithRequest(request, fromFile: localFile) { (data, response, error) in completionHandler?(error: error) + dispatch_async(dispatch_get_main_queue(), { + self.delegate?.fileproviderCreateModifyNotify(self, path: toPath) + }) }.resume() }