FileManager replacement for Local, iCloud and Remote (WebDAV/FTP/Dropbox/OneDrive) files -- Swift

Overview

File Provider

This Swift library provide a swifty way to deal with local and remote files and directories in a unified way.

Swift Version Platform License Build Status Codebeat Badge

Release version CocoaPods version Carthage compatible Cocoapods Downloads Cocoapods Apps

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.

Features

  • LocalFileProvider a wrapper around FileManager with some additions like builtin coordinating, searching and reading a portion of file.
  • CloudFileProvider A wrapper around app's ubiquitous container API of iCloud Drive.
  • WebDAVFileProvider WebDAV protocol is defacto file transmission standard, supported by some cloud services like ownCloud, Box.com and Yandex.disk.
  • FTPFileProvider While deprecated in 1990s due to serious security concerns, it's still in use on some Web hosts.
  • DropboxFileProvider A wrapper around Dropbox Web API.
    • For now it has limitation in uploading files up to 150MB.
  • OneDriveFileProvider A wrapper around OneDrive REST API, works with onedrive.com and compatible (business) servers.
  • AmazonS3FileProvider Amazon storage backend. Used by many sites.
  • GoogleFileProvider A wrapper around Goodle Drive REST 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!.
    • For now, you can use amosavian/AMSMB2 framework to connect to SMB2.

Requirements

  • Swift 4.0 or higher
  • iOS 8.0 , OSX 10.10
  • XCode 9.0

Legacy version is available in swift-3 branch.

Installation

Important: this library has been renamed to avoid conflict in iOS 11, macOS 10.13 and Xcode 9.0. Please read issue #53 to find more.

Cocoapods / Carthage / Swift Package Manager

Add this line to your pods file:

pod "FilesProvider"

Or add this to Cartfile:

github "amosavian/FileProvider"

Or to use in Swift Package Manager add this line in Dependencies:

.Package(url: "https://github.com/amosavian/FileProvider.git", majorVersion: 0)

Manually

To have latest updates with ease, use this command on terminal to get a clone:

git clone https://github.com/amosavian/FileProvider

You can update your library using this command in FileProvider folder:

git pull

if you have a git based project, use this command in your projects directory to add this project as a submodule to your project:

git submodule add https://github.com/amosavian/FileProvider

Then you can do either:

  • Copy Source folder to your project and Voila!

  • Drop FilesProvider.xcodeproj to you Xcode workspace and add the framework to your Embeded Binaries in target.

Design

To find design concepts and how to implement a custom provider, read Concepts and Design document.

Usage

Each provider has a specific class which conforms to FileProvider protocol and share same syntax.

Find a sample code for iOS here.

Initialization

For LocalFileProvider if you want to deal with Documents folder:

import FilesProvider

let documentsProvider = LocalFileProvider()

// Equals with:
let documentsProvider = LocalFileProvider(for: .documentDirectory, in: .userDomainMask)

// Equals with:
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let documentsProvider = LocalFileProvider(baseURL: documentsURL)

Also for using group shared container:

import FilesProvider

let documentsProvider = LocalFileProvider(sharedContainerId: "group.yourcompany.appContainer")
// Replace your group identifier

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 look at here to see how to update project settings then use below code, This will automatically manager creating Documents folder in container:

import FilesProvider

let documentsProvider = CloudFileProvider(containerId: nil)

For remote file providers authentication may be necessary:

import FilesProvider

let credential = URLCredential(user: "user", password: "pass", persistence: .permanent)
let webdavProvider = WebDAVFileProvider(baseURL: URL(string: "http://www.example.com/dav")!, credential: credential)
  • In case you want to connect non-secure servers for WebDAV (http) or FTP in iOS 9+ / macOS 10.11+ you should disable App Transport Security (ATS) according to this guide.

  • For Dropbox & OneDrive, user is clientID and password is Token which both must be retrieved via OAuth2 API o. There are libraries like p2/OAuth2 or OAuthSwift which can facilate the procedure to retrieve token. The latter is easier to use and prefered. Please see OAuth example for Dropbox and OneDrive for detailed instruction.

For interaction with UI, set delegate property of FileProvider object.

You can use url(of:) method if provider to get direct access url (local or remote files) for some file systems which allows to do so (Dropbox doesn't support and returns path simply wrapped in URL).

Delegates

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.

There's simply three method which indicated whether the operation failed, succeed and how much of operation has been done (suitable for uploading and downloading operations).

Your class should conforms FileProviderDelegate class:

override func viewDidLoad() {
	documentsProvider.delegate = self as FileProviderDelegate
}
	
func fileproviderSucceed(_ fileProvider: FileProviderOperations, operation: FileOperationType) {
	switch operation {
	case .copy(source: let source, destination: let dest):
		print("\(source) copied to \(dest).")
	case .remove(path: let path):
		print("\(path) has been deleted.")
	default:
		print("\(operation.actionDescription) from \(operation.source!) to \(operation.destination) succeed")
	}
}

func fileproviderFailed(_ fileProvider: FileProviderOperations, operation: FileOperationType) {
    switch operation {
	case .copy(source: let source, destination: let dest):
		print("copy of \(source) failed.")
	case .remove:
		print("file can't be deleted.")
	default:
		print("\(operation.actionDescription) from \(operation.source!) to \(operation.destination) failed")
	}
}
	
func fileproviderProgress(_ fileProvider: FileProviderOperations, operation: FileOperationType, progress: Float) {
	switch operation {
	case .copy(source: let source, destination: let dest):
		print("Copy\(source) to \(dest): \(progress * 100) completed.")
	default:
		break
	}
}

Note: fileproviderProgress() delegate method is not called by LocalFileProvider currently.

It's recommended to use completion handlers for error handling or result processing.

Controlling file operations

You can also implement FileOperationDelegate protocol to control behaviour of file operation (copy, move/rename, remove and linking), and decide which files should be removed for example and which won't.

fileProvider(shouldDoOperation:) method is called before doing a operation. You sould return true if you want to do operation or false if you want to stop that operation.

fileProvider(shouldProceedAfterError:, operation:) will be called if an error occured during file operations. Return true if you want to continue operation on next files or false if you want stop operation further. Default value is false if you don't implement delegate.

Note: In LocalFileProvider, these methods will be called for files in a directory and its subfolders recursively.

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.

For a single file:

documentsProvider.attributesOfItem(path: "/file.txt", completionHandler: {
	attributes, error in
	if let attributes = attributes {
		print("File Size: \(attributes.size)")
		print("Creation Date: \(attributes.creationDate)")
		print("Modification Date: \(attributes.modifiedDate)")
		print("Is Read Only: \(attributes.isReadOnly)")
	}
})

To get list of files in a directory:

documentsProvider.contentsOfDirectory(path: "/", completionHandler: {
	contents, error in
	for file in contents {
		print("Name: \(file.name)")
		print("Size: \(file.size)")
		print("Creation Date: \(file.creationDate)")
		print("Modification Date: \(file.modifiedDate)")
	}
})

To get size of strage and used/free space:

func storageProperties(completionHandler: { total, used in
	print("Total Storage Space: \(total)")
	print("Used Space: \(used)")
	print("Free Space: \(total - used)")
})
  • if this function is unavailable on provider or an error has been occurred, total space will be reported -1 and used space 0

Creating File and Folders

Creating new directory:

documentsProvider.create(folder: "new folder", at: "/", completionHandler: { error in
    if let error = error {
        // Error handling here
    } else {
        // The operation succeed
    }
})

To create a file, use writeContents(path:, content:, atomically:, completionHandler:) method.

Copy and Move/Rename Files

Copy file old.txt to new.txt in current path:

documentsProvider.copyItem(path: "new folder/old.txt", to: "new.txt", overwrite: false, completionHandler: nil)

Move file old.txt to new.txt in current path:

documentsProvider.moveItem(path: "new folder/old.txt", to: "new.txt", overwrite: false, completionHandler: nil)

Note: To have a consistent behavior, create intermediate directories first if necessary.

Delete Files

documentsProvider.removeItem(path: "new.txt", completionHandler: nil)

Fetching Contents of File

There is two method for this purpose, one of them loads entire file into Data and another can load a portion of file.

documentsProvider.contents(path: "old.txt", completionHandler: {
	contents, error in
	if let contents = contents {
		print(String(data: contents, encoding: .utf8)) // "hello world!"
	}
})

If you want to retrieve a portion of file you can use contents method with offset and length arguments. Please note first byte of file has offset: 0.

documentsProvider.contents(path: "old.txt", offset: 2, length: 5, completionHandler: {
	contents, error in
	if let contents = contents {
		print(String(data: contents, encoding: .utf8)) // "llo w"
	}
})

Write Data To Files

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 Storage

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:

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:

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.

Operation Progress

Creating/Copying/Deleting/Searching functions return a (NS)Progress. It provides operation type, progress and a .cancel() method which allows you to cancel operation in midst. You can check cancellable property to check either you can cancel operation via this object or not.

  • Note: Progress reporting is not supported by native (NS)FileManager so LocalFileProvider.

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:

// To setup a new UndoManager:
documentsProvider.setupUndoManager()
// or if you have an UndoManager object already:
documentsProvider.undoManager = self.undoManager

// e.g.: To undo last operation manually:
documentsProvider.undoManager?.undo()

You can also bind UndoManager object with view controller to use shake gesture and builtin undo support in iOS/macOS, add these code to your ViewController class like this sample code:

class ViewController: UIViewController
    override var canBecomeFirstResponder: Bool {
        return true
    }
    
    override var undoManager: UndoManager? {
        return (provider as? FileProvideUndoable)?.undoManager
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // Your code here
        UIApplication.shared.applicationSupportsShakeToEdit = true
        self.becomeFirstResponder()
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        // Your code here
        UIApplication.shared.applicationSupportsShakeToEdit = false
        self.resignFirstResponder()
    }
    // The rest of your implementation
}

File Coordination

LocalFileProvider and its descendents has a isCoordinating property. By setting this, provider will use NSFileCoordinating class to do all file operations. It's mandatory for iCloud, while recommended when using shared container or anywhere that simultaneous operations on a file/folder is common.

Monitoring File Changes

You can monitor updates in some file system (Local and SMB2), there is three methods in supporting provider you can use to register a handler, to unregister and to check whether it's being monitored or not. It's useful to find out when new files added or removed from directory and update user interface. The handler will be dispatched to main threads to avoid UI bugs with a 0.25 sec delay.

// to register a new notification handler
documentsProvider.registerNotifcation(path: "/") {
	// calling functions to update UI 
}
	
// To discontinue monitoring folders:
documentsProvider.unregisterNotifcation(path: "/")
  • Please note in LocalFileProvider it will also monitor changes in subfolders. This behaviour can varies according to file system specification.

Thumbnail and meta-information

Providers which conform ExtendedFileProvider are able to generate thumbnail or provide file meta-information for images, media and pdf files.

Local, OneDrive and Dropbox providers support this functionality.

Thumbnails

To check either file thumbnail is supported or not and fetch thumbnail, use (and modify) these example code:

let path = "/newImage.jpg"
let thumbSize = CGSize(width: 64, height: 64) // or nil which renders to default dimension of provider
if documentsProvider.thumbnailOfFileSupported(path: path {
    documentsProvider.thumbnailOfFile(path: file.path, dimension: thumbSize, completionHandler: { (image, error) in
        // Interacting with UI must be placed in main thread
        DispatchQueue.main.async {
            self.previewImage.image = image
        }
    }
}
  • Please note it won't cache generated images. if you don't do it yourself, it may hit you app's performance.
Meta-informations

To get meta-information like image/video taken date, location, dimension, etc., use (and modify) these example code:

if documentsProvider..propertiesOfFile(path: file.path, completionHandler: { (propertiesDictionary, keys, error) in
    for key in keys {
        print("\(key): \(propertiesDictionary[key])")
    }
}
  • Bonus: You can modify/extend Local provider generator by setting LocalFileInformationGenerator static variables and methods

Contribute

We would love for you to contribute to FileProvider, check the LICENSE file for more info.

Things you may consider to help us:

  • Implement request/response stack for SMBClient
  • Implement Test-case (XCTest)
  • Add Sample project for iOS
  • Add Sample project for macOS

Projects in use

If you used this library in your project, you can open an issue to inform us.

Meta

Amir-Abbas Mousavian – @amosavian

Thanks to Hootan Moradi for designing logo.

Distributed under the MIT license. See LICENSE for more information.

https://github.com/amosavian/

Issues
  • WebDavFileProvider example

    WebDavFileProvider example

    This is my example code for os x:

    import Cocoa
    import FileProvider
    
    class ViewController: NSViewController {
        
        @IBOutlet weak var writeText: NSTextField!
        @IBOutlet weak var textSavedFromFile: NSTextField!
        @IBOutlet weak var folderList: NSTextField!
        
        var webdavFile: WebDAVFileProvider?
        var operation: OperationHandle?
        
        @IBAction func saveText(_ sender: NSButton) {
            let user = "[email protected]"
            let password = "xxxx"
            
            let credential = URLCredential(user: user, password: password, persistence: .forSession)
            let myUrlWebdav = URL(string: "https://webdav.pcloud.com")
            let webdavFile = WebDAVFileProvider(baseURL: myUrlWebdav!, credential: credential)
            let path = "newFile1.txt"
            let nameFolder = "Text"
            let atFolder = "/"
            let atFile = atFolder + nameFolder + atFolder + path
            
            print(atFile)
    
            if writeText.stringValue != "" {
                let data = writeText.stringValue.data(using: .utf8)
                print("Data is: \(data)")
                
                webdavFile?.create(folder: nameFolder, at: atFolder, completionHandler: { error in
                    if error == nil {
                    print("La cartella è stata creata con successo.")
                    }
                    else
                    {
                    print("La cartella non è stata creata con il seguente errore: \(error)")
                    }
                    webdavFile?.writeContents(path: atFile, contents: data, atomically: false, overwrite: false, completionHandler: { error in
                        if error == nil {
                            print("Il file è stato creato con successo.")
                        }
                        else
                        {
                            print("Il file non è stato creato con il seguente errore: \(error)")
                        }
                    })
                
                })
            }
            
        }
    
        @IBAction func readText(_ sender: NSButton) {
            
        }
        
        @IBAction func folderList(_ sender: NSButton) {
            
        }
        
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            // Do any additional setup after loading the view.
            webdavFile?.delegate = (self as! FileProviderDelegate)
        }
    
        override var representedObject: Any? {
            didSet {
            // Update the view, if already loaded.
            }
        }
    
        func fileproviderSucceed(_ fileProvider: FileProviderOperations, operation: FileOperationType) {
            switch operation {
            case .copy(source: let source, destination: let dest):
                print("\(source) copied to \(dest).")
            case .remove(path: let path):
                print("\(path) has been deleted.")
            default:
                print("\(operation.actionDescription) from \(operation.source!) to \(operation.destination) succeed")
            }
        }
        
        func fileproviderFailed(_ fileProvider: FileProviderOperations, operation: FileOperationType) {
            switch operation {
            case .create(path: let pathFile):
                print("create file: \(pathFile) failed.")
            case .modify(path: let pathFile):
                print("modify file: \(pathFile) failed.")
            case .copy(source: let source, destination: let dest):
                print("copy of \(source) failed.")
            case .remove:
                print("file can't be deleted.")
            default:
                print("\(operation.actionDescription) from \(operation.source!) to \(operation.destination) failed")
            }
        }
        
        func fileproviderProgress(_ fileProvider: FileProviderOperations, operation: FileOperationType, progress: Float) {
            switch operation {
            case .copy(source: let source, destination: let dest):
                print("Copy\(source) to \(dest): \(progress * 100) completed.")
            case .create(path: let pathFile):
                print("Create file or folder to \(pathFile): \(progress * 100) completed.")
            default:
                break
            }
        }
    }
    

    My problems are:

    1. If I do not use "create folder" and the folder does not exist the file is not created without any error.
    2. If the directory exists the file is created, but without any information.
    3. It does not work the delegate method.

    Can anyone change the code so that it works? Thank you Cesare Piersigilli

    bug 
    opened by CPiersigilli 46
  • HELP WANTED - Addchild example use

    HELP WANTED - Addchild example use

    I have this method:

    func upLoadMultiPhotoWithAddChild(folder:String, namePhotos:[String], completion: GeoCompletionHandler) -> Progress {
            progress = Progress(totalUnitCount: Int64(namePhotos.count)*2)
            for namePhoto in namePhotos {
                guard progress?.isCancelled != true else {
                    return progress!
                }
                let createProgress = webdav?.create(folder: "test", at: "/", completionHandler: { (error) in
                    if let error = error {
                        if self.handleWebdavError(error: error).errorCode != 405 {
                            completion!(self.handleWebdavError(error: error))
                            return
                        }
                    }
                    let copyProgress = self.webdav?.copyItem(localFile: namePhoto.toLocalURL(), to: namePhoto.toRemotePath(destFolder: "test"), completionHandler: { (error) in
                        if let error = error {
                            completion!(self.handleWebdavError(error: error))
                            return
                        }
                        completion!(GeoErrorStruct())
                    })
                    // Add the copyProgress task's progress as a child to the overall progress.
                    self.progress?.addChild(copyProgress!, withPendingUnitCount: 1)
                })
                // Add the writeProgress task's progress as a child to the overall progress.
                progress?.addChild(createProgress!, withPendingUnitCount: 1)
            }
            return progress!
        }
    

    In my View Controller:

    @IBAction func uploadMultiPhoto(_ sender: UIButton) {
            WebdavHelper.shared.setWebdav()
            progressView.setProgress(0, animated: true)
            let progress = WebdavHelper.shared.upLoadMultiPhotoWithAddChild(folder: "test", namePhotos: namePhotos) { (geoError) in
                if geoError.errorCode == nil {
                    print("Upload foto riuscito.")
                }
            }
            progressView.observedProgress = progress
        }
    

    Unfortunately, even if the upload of the photos has been properly completed, progressView never reaches 1, that is at the end: why? Where am I wrong?

    opened by CPiersigilli 20
  • WedDavFileProvider. No check error:

    WedDavFileProvider. No check error: "A server with the specified hostname could not be found".

    I'd like to check the credentials within the app (URL, user and password), but if the URL is wrong here's what I get: error.errorDescription = "A server with the specified hostname could not be found" Therefore I thought I could replace it with if let error = error as? FileProviderWebDavError { in order to have the exact error number but I get nil and I'm not able to handle the error. Any idea? Best Regards.

    question 
    opened by CPiersigilli 17
  • FTP Upload TLS/SSL Error

    FTP Upload TLS/SSL Error

    I am getting an error when trying to upload using a file using a FTP handler. Here is the function that I am calling:

    func sendDataToServer(_ file: URL){
            let credentials = URLCredential(user: "sample", password: "sample", persistence: .forSession)
            
            let host = URL(string: "ftp://ftp.site.com")!
            let ftpProvider = FTPFileProvider(baseURL: host, passive: false, credential: credentials)!
            
            ftpProvider.copyItem(localFile: file, to: "/directory/test.csv", overwrite: true, completionHandler: { (error) in
                print(error?.localizedDescription ?? "no error")
            })
        }
    

    Once this is executed I get the following error:

    Invalid sequence of commands (AUTH SSL/TLS required prior to authentication).

    Could you advise on what the next steps would be for this?

    Thanks in advance!

    bug enhancement 
    opened by webmasterjunkie 16
  • FTP data connection request times out every time on iOS 11

    FTP data connection request times out every time on iOS 11

    So the problem is that I'm currently working on a project and when I was testing it on iOS 9, everything was fine , but when we updated the devices to iOS 11, every FTP data connection request times out.

    opened by brashkov 14
  • WebDav File Path Question

    WebDav File Path Question

    So I've started playing with the WebDav file provider, to see how that fares, and I ran into a bit of an issue or question really.

    Say I make a call to list the contents of a directory with a path: "/data/Hello/Settings/background"

    Everything works, and looks OK, at first glance. Except if I look at the relative path of the FileObjects I got back, its got paths like: "/remote.php/dav/files/skela/remote.php/dav/files/skela/data/Hello/Settings/background/portrait_phones.pdf"

    That to me seems wrong, for instance in Dropbox, all the paths are relative and would be for example: "/data/Hello/Settings/background/portrait_phones.pdf"

    Is this a design choice, or a possible issue ? Just asking because it feels wrong to me.

    Again, really appreciate the work you do on this library!

    bug 
    opened by skela 11
  • FTPFileProvider protocol exception?

    FTPFileProvider protocol exception?

    Hi amosavian,

    first of all I would like to thank you! This library is awesome.

    However, I think I found an issue which may happen with server responses. Long time ago I developed a small http-server and discovered that not all servers are "literally" following the specification. Sometimes there is whitespaces missing or just a dash inserted so that the parser gets confused. I am not sure if this is the case here. Maybe you could have a look at this:

    In the

    func ftpRetrieve(_ task: filePath: from position: length: onTask: onProgress: completionHandler:)

    the framework calls the closure

    { (response, error) in
                                            
               do {
                            if let error = error {
                                throw error
                            }
                            
                            guard let response = response else {
                                throw self.urlError(filePath, code: .cannotParseResponse)
                            }
                            
                            if !(response.hasPrefix("1") || response.hasPrefix("2")) {
                                throw FileProviderFTPError(message: response)
                            }
      
         
                        } catch {
                            self.dispatch_queue.async {
                                completionHandler(error)
                            }
                     }
    

    I was curious what happened, because "officially" the connection timed out. I did a debug-dump of the variable response. The result was:

    200 Type set to I\r\n350 Restarting at 0. Send STORE or RETRIEVE to initiate transfer\r\n227 Entering Passive Mode (212,227,24,12,218,208).

    So it looks like the server works properly and accepts the calls from your framework. Is this probably a case which could be covered by this framework?

    Or did I misunderstand how it works? Because I assume the callback is correct. But no data is being transmitted, the frameworks stops with a timeout.

    bug 
    opened by JackPearse 11
  • FTPFileProvider.isReachable returns false with errors even though I can read file contents on FTP server.

    FTPFileProvider.isReachable returns false with errors even though I can read file contents on FTP server.

    I'm trying to use FTPFileProvider with latest iOS public beta and XCode public beta. I am able to read contents of files on FTP server but when I call some of the methods like isRechable(), recursiveList(), attributesOfItem() then it doesn't seem to work and also I get some errors.

    Here is the simple piece of code I've written in viewDidLoad()

        let credential = URLCredential(user: "seenu", password: "mypassword", persistence: .permanent)
        let ftpProvider = FTPFileProvider(baseURL: URL(string: "ftp://192.168.0.3:21")!, credential: credential)
        ftpProvider?.delegate = self as FileProviderDelegate
        ftpProvider?.contents(path: "HD/Photos/test.jpg", completionHandler: { (data, error) in
            DispatchQueue.main.async {
                self.image.image = UIImage(data: data!)
                print("Image has been read using ftpProvider")
            }
        })
        
        ftpProvider?.isReachable(completionHandler: {bool in
            print("ftp is rechable: \(bool)");
        })
        
        ftpProvider?.recursiveList(path: "HD/Photos", useMLST: false, completionHandler: {
            contents, error in
            for file in contents {
                print("Name: \(file.name)")
                print("Size: \(file.size)")
                print("Creation Date: \(file.creationDate)")
                print("Modification Date: \(file.modifiedDate)")
            }
        })
        
        ftpProvider?.attributesOfItem(path: "HD/Photos/test.jpg", completionHandler: {
            attributes, error in
            if let attributes = attributes {
                print("File Size: \(attributes.size)")
                print("Creation Date: \(attributes.creationDate)")
                print("Modification Date: \(attributes.modifiedDate)")
                print("Is Read Only: \(attributes.isReadOnly)")
            }
        })
        
        ftpProvider?.contents(path: "HD/test.txt", completionHandler: {
            contents, error in
            if let contents = contents {
                print("File content is: \(String(data: contents, encoding: .utf8))") // "hello world!"
            }
        })
    

    Reading the test.jpg and test.txt file works fine but the other calls doesn't given any output. Below is the console output.

    2017-08-01 00:18:29.450605-0400 ftp[1183:64255] [] nw_connection_fillout_tcp_statistics 1 Connection is not ready 2017-08-01 00:18:29.450846-0400 ftp[1183:64255] [] tcp_connection_get_statistics Failed to get statistics from connection 2017-08-01 00:18:29.456293-0400 ftp[1183:64255] [] nw_connection_fillout_tcp_statistics 2 Connection is not ready 2017-08-01 00:18:29.456840-0400 ftp[1183:64255] [] tcp_connection_get_statistics Failed to get statistics from connection 2017-08-01 00:18:29.460717-0400 ftp[1183:64255] [] nw_connection_fillout_tcp_statistics 3 Connection is not ready 2017-08-01 00:18:29.461325-0400 ftp[1183:64255] [] tcp_connection_get_statistics Failed to get statistics from connection ftp is rechable: false 2017-08-01 00:18:29.490551-0400 ftp[1183:64272] TIC TCP Conn Missing Error [3:0x60400016f600]: Generating 1:54 2017-08-01 00:18:29.490794-0400 ftp[1183:64272] TIC Read Status [3:0x60400016f600]: 1:57 Image has been read using ftpProvider File content is: Optional("Hello world !!") 2017-08-01 00:20:29.543466-0400 ftp[1183:81206] TIC TCP Conn Missing Error [1:0x60000016e1c0]: Generating 1:54 2017-08-01 00:20:29.544054-0400 ftp[1183:81206] TIC TCP Conn Missing Error [2:0x60000016e4c0]: Generating 1:54

    Let me know if I'm doing something wrong or is it some genuine problem ?

    bug 
    opened by gsrinivas37 11
  • FTPFileProvider not working for some mobile networks and wifi

    FTPFileProvider not working for some mobile networks and wifi

    Hello, I am using FTPFileProvider for downloading and uploading files from FTP server , but it is working for only few networks, can you please let me know if I want to make any additional settings in code? Thanks

    opened by Monika-Tapadiya 0
  • OneDrive Upload Progress Fix

    OneDrive Upload Progress Fix

    Fixed issue where OneDrive upload progress would be a % of the maximum upload size for parts of files rather than the % of total file size

    opened by dl-price 0
  • Fixing upload multi part files to OneDrive

    Fixing upload multi part files to OneDrive

    Fixed issue where OneDrive could not upload files due to handling ranges incorrectly

    opened by dl-price 0
  • Dropbox OAuth changes

    Dropbox OAuth changes

    Dropbox are moving to short lived access tokens, so how is this being handled via this library? Do we just pass the refresh token into the credentials or is there another flow? Has this been considered?

    Below i have pasted the message from Dropbox to developers.

    The Dropbox API now supports OAuth scopes, PKCE, refresh tokens, and short-lived access tokens. We’re asking developers to review their app’s permission settings and ensure their apps support short-lived access tokens. Apps that require background access will need to update to use refresh tokens, which is made easier by using our updated SDKs.

    Starting September 30th, 2021, the Dropbox OAuth flow will no longer return long-lived access tokens. It will instead return short-lived access tokens, and optionally return refresh tokens. Please be sure to review, test, and move to the new permission model before then.

    For detailed instructions, please see:

    Dropbox OAuth Guide Blog post — Now Available: OAuth Scopes and Enhanced Permissions Blog post — Migrating App Permissions and Access Tokens

    opened by AbzA1989 0
  • FTPFileProvider always gives nil date

    FTPFileProvider always gives nil date

    creationDate and modifiedDate is always nil in FTPFileProvider.

    opened by ganeshshirole 0
  • Fatal Error

    Fatal Error

    See picture:

    Screen Shot 2020-11-05 at 1 41 35 PM

    Error while trying to get file contents, in FTPFileProvider.swift - public init? method. (file name: "/ps1.7.4.4upgrade.txt") No other tests.

    opened by matt-matt1 0
  • FTP listing 550 error path

    FTP listing 550 error path

    I can get a root listing fine. But when I try a sub-directory (that exists) ("/vendor") is gives this error:

    FileProviderFTPError(code: 550, path: "/vendor/vendor/vendor", serverDescription: Optional("/vendor/vendor/vendor: No such file or directory"))

    I inserted a breakpoint immediately before the 'contentsOfDirectory(path:completionHandler:)' call. It confirms that path = "/vendor". The next breakpoint is inside it's 'completionHandler', and return the above error.

    Also this section of my code is similar to the section that I employ for a different library which makes a SMB call, and it works fine for SMB, so I think this is a bug in FTPFileProvider.

    Can anyone confirm either yes or no?

    opened by matt-matt1 0
  • Credentials can't be changed after initial login

    Credentials can't be changed after initial login

    If I create a WebDAVFileProvider with given credentials, I can't later create a new one with different credentials until the app is restarted. If I use the same username and host as the first Provider, I can use any password I want and it will authenticate properly, implying it cached the original password. If I use a different username and host, then I cannot login with a new WebDAVFileProvider.

    opened by Isvvc 0
  • Nextcloud Thumbnails

    Nextcloud Thumbnails

    Implement support for getting thumbnails from Nextcloud (and maybe OwnCloud. I haven't tested it).

    Nextcloud WebDAV access is done through remote.php/dav/files/ while thumbnails are accessed through index.php/core/preview.png, so by checking if the baseURL contains remote.php/dav/files/ we can know if it's a Nextcloud server. If it is, we can replace that with the preview URL.

    Also has a minor fix for the existing yandex thumbnail support where file extensions were checked case-sensitively.

    Closes #165

    opened by Isvvc 0
  • Add thumbnail support for Nextcloud/OwnCloud

    Add thumbnail support for Nextcloud/OwnCloud

    Currently the only WebDAV server that this library supports fetching thumbnails from is yandex, but Nextcloud and OwnCloud support thumbnails too, using a different method.

    To fetch a thumbnail from Nextcloud, download the file index.php/core/preview.png with the file parameter set to the file path, the x and y parameters set for dimensions, and optionally a=1 and mode=cover. a=1 will crop the image to fill the dimensions specified whereas omitting it will keep the aspect ratio, making the result equal-to or smaller-than the specified dimensions. I haven't been able to figure out what mode=cover does, but that's what the official Nextcloud app uses.

    Example: https://nc.example.com/index.php/core/preview.png?file=Media/image.png&x=128&y=128&a=1&mode=cover

    Currently thumbnailOfFile and thumbnailOfFileSupported check if the WebDAV host contains dav.yandex., though Nextcloud and OwnCloud servers can have any domain name, so I'm not sure how it could be checked if the server is Nextcloud or OwnCloud in order to use this thumbnail-fetching method, or to check if thumbnails are supported.

    opened by Isvvc 0
Releases(0.26.0)
  • 0.25.1(May 3, 2018)

    • Fixed Cocoapods error

    Changes in version 0.25.0:

    • General

      • Updated Podspec Swift version to 4.1 and fixed warnings.
      • Introduced FileProviderReadWriteProgressive to allow FTP and HTTP-based providers to fetch file content progressively, suitable for streaming.
      • Fixed encoding error using NSCoding.
      • Fixed FileObject equality and hashing methods
      • Known Issue: Progress for thumbnail and properties does not work yet.
    • Local

      • MadeLocalFileMonitor class public to monitor folder/file changes directly without using provider's registerNotification
      • More items for image metadata in ExtendedLocalFileProvider, including GPS info and White-balance.
    • iCloud

      • Fixed iCloud download/upload progress report, Fixed #93 crash.
    • FTP

      • Fixed race conditions, Possible fix for #79.
      • Returning real error instead of timeout.
    Source code(tar.gz)
    Source code(zip)
    FilesProvider.framework.zip(27.99 MB)
  • 0.25.0(May 3, 2018)

    • General

      • Updated Podspec Swift version to 4.1 and fixed warnings.
      • Introduced FileProviderReadWriteProgressive to allow FTP and HTTP-based providers to fetch file content progressively, suitable for streaming.
      • Fixed encoding error using NSCoding.
      • Fixed FileObject equality and hashing methods
      • Known Issue: Progress for thumbnail and properties does not work yet.
    • Local

      • MadeLocalFileMonitor class public to monitor folder/file changes directly without using provider's registerNotification
      • More items for image metadata in ExtendedLocalFileProvider, including GPS info and White-balance.
    • iCloud

      • Fixed iCloud download/upload progress report, Fixed #93 crash.
    • FTP

      • Fixed race conditions, Possible fix for #79.
      • Returning real error instead of timeout.
    Source code(tar.gz)
    Source code(zip)
    FilesProvider.framework.zip(27.99 MB)
  • 0.24.0(Mar 27, 2018)

    • General

      • Fixed podspec Swift 4.0 setting (#90)
      • Known Issue: Progress for thumbnail and properties does not work yet.
    • FTP

      • Added Extended Passive (EPSV) data connection mode
      • Added support for Windows/DOS style directory list
      • Fixed FTP on TLS data connection failure
    • OneDrive

      • Fixed operation fall on files with non-ASCII names
    Source code(tar.gz)
    Source code(zip)
    FilesProvider.framework.zip(27.74 MB)
  • 0.23.0(Mar 6, 2018)

    • General

      • Source breaking change: isReachable() returns error encountered in addition to success.
      • Optimizations and refactorings
      • Added Concepts and design document.
      • Fixed error description for file provider errors.
      • Known Issue: Progress for thumbnail and properties does not work yet.
    • OneDrive

      • OneDrive provider now works with new Microsoft Graph API perfectly
      • Unlimited uploading
    • iCloud

      • iCloud overwrite parameter ignored when uploading file (#82)
    • WebDAV

      • searchFiles() "including" param was being ignored.
    Source code(tar.gz)
    Source code(zip)
    FilesProvider.framework.zip(20.89 MB)
  • 0.22.0(Dec 30, 2017)

    • General

      • Added test cases for Local, Dropbox, WebDAV and FTP.
      • Remove monitoring notification on deinit.
      • Updated Swift Package Manager to version 4.0.
    • WebDAV

      • Directory are parsed as regular files.
      • Handling space and illegal characters in urls inside response.
    • FTP

      • Problems with iOS 11 and NSURLSessionStreamTask.
      • Crash on downloading and uploading.
      • Removed dependency on Apples FTP implementation for downloading.
      • Faster uploading (Single stream instead of muliple)
    • Dropbox

      • attributesOfItem() returned error instead of result.
    Source code(tar.gz)
    Source code(zip)
    FilesProvider.framework.zip(19.81 MB)
  • 0.21.0(Oct 27, 2017)

    This is the last pod which will work with Swift 3. If you are using Swift 3 in your project, please use swift-3 (will be created soon) branch directly using other installation methods. I will maintain code for Swift 3 as far as possible.

    OneDrive provider is updated to latest version according to Microsoft documentations. You can use OneDriveProvider.Route enum to determine which root should be used to access files, use .me to access your own files or other options to access enterprise and shared files.

    -Important: storageProperties() method signature has been changed and you have to rectify it manually.

    contents(path:, progressHandler:) method added to allow fetch data of remote file progressively. This allows buffering and showing files while downloading.

    Other improvements:

    • storageProperties() method now returns VolumeObject instance in completion handler, which contains volume size and other informations.
    • Added progressive downloading method contents(path:, progressHandler:) for HTTP-based providers
    • Removed currentPath property.
    • Code is Swift 4 compatible now.
    • copy(path:toLocalURL) and copy(localURL:to:) reading and writing to local url are coordinated now.
    • refactored paginated listing. (Dropbox, OneDrive and GoogleDrive in future)
    • Added HEIF thumbnail and properties generators. (iOS 11.0, macOS 10.13)

    Bugs fixed:

    • Cloud provider misbehaves in subdirectories.
    • OneDrive download progress was not available due to Transfer-Encoding: chunked.
    • Overwrite flag didn't work in HTTP-based providers.
    • url(of:) crash when path is empty.
    • Progress returned didn't parented correctly.
    • Improved compilation speed.
    Source code(tar.gz)
    Source code(zip)
    FilesProvider.framework.zip(25.61 MB)
  • 0.20.1(Aug 29, 2017)

    currentPath property is now deprecated and out of use. It will be marked as obsoleted soon. Use local var in case you want to track it. Also fileproviderFailed() delegate method signature has changed to send Error object to user.

    Changes:

    • Making more method implementations overridable for user.
    • Consistency to delegate call in all contents(of:) methods, with .fetch case.
    • moved to v2 api in Dropbox for copy, move and delete files.
    • Using URL.checkResourceIsReachable() to check file exists instead if FileManager.fileExists() for iCloud and promised urls.
    • Added convenience methods for URLRequest headers for Content-Type, Accept-Encoding, Range, etc.

    Bugs fixed:

    • Downloading/Uploading files with colon in name would be failed due to percent encoding
    • Recursive search in FTP, OneDrive, WebDAV
    • FTP crash on rucursive remove due to Progress exception
    Source code(tar.gz)
    Source code(zip)
    FilesProvider.framework.zip(31.61 MB)
  • 0.20.0(Aug 18, 2017)

    Dropbox, WebDAV & OneDrive all are refactored to HTTPFileProvider base class, decreases bugs and adds more consistency.

    Because FileOperationType.source is not optional anymore, I had to increase version.

    Also:

    • Added fileURL to Progress
    • critical bugfix: KVO exception crash on task completion
    Source code(tar.gz)
    Source code(zip)
    FilesProvider.framework.zip(30.17 MB)
  • 0.19.0(Aug 15, 2017)

    Important: Deprecating OperationHandle class in favor of Foundation's Progress class is a source breaking change. You may have to update your current code to use new feature.

    Other improvements:

    • Fixed broken OneDrive API & redundant percent encoding of path (#57 , #59)
    • Fixed Dropbox issue with file path in HTTP header (#58)
    Source code(tar.gz)
    Source code(zip)
    FilesProvider.framework.zip(31.23 MB)
  • 0.18.1(Jul 16, 2017)

    • Better handling of FTPS (explicit mode)
    • Fixed FTP login problem (#54)
    • Enhanced PDF meta-information:
      • included Producer and Keywords meta-informations.
      • Boolean pdf properties like Allows printing are now really boolean instead of Yes/No string.
      • Dates (Creation date/Modified date) are now correctly handled and parsed for all types of PDF files.

    Note: the library has been renamed to FilesProvider, in case you missed it

    Source code(tar.gz)
    Source code(zip)
    FilesProvider.framework.zip(30.57 MB)
  • 0.18.0(Jul 1, 2017)

    To avoid conflict with new introduced intrinsic framework in iOS 11, macOS 10.13 and tvOS 11, this project has renamed to FilesProvider you may have to update your project settings see #53 for more information.

    Source code(tar.gz)
    Source code(zip)
  • 0.17.0(Jun 27, 2017)

  • 0.16.2(May 5, 2017)

    • Introduced new protocol FileProviderSharing, contains publicLink() method for Dropbox, OneDrive and iCloud providers.
    • Added WebDAV OAuth support by changing WebDAVFileProvider.credentialType property.
    • Now LocalOperationHandle.inProgress property is usable and determines either local file operation is in progress or not.
    • Fixed bug: problem in LocalFileProvider baseURL assignment.
    • Fixed bug: creating percent-encoded folder in WebDAV provider.
    • Fixed bug: Listing FTP directory when server doesn't support RFC3657 (MLST)
    Source code(tar.gz)
    Source code(zip)
    FileProvider.framework.zip(30.53 MB)
  • 0.16.1(Apr 15, 2017)

  • 0.16.0(Apr 14, 2017)

    This is a patch indeed, but because FileObject.url is now a non-optional, which is source breaking change, I had to increase minor version instead of patch.

    In scenarios which there is no url for FileObject e.g. Dropbox, it will return percent encoded path.

    Bugs fixed in this release:

    • FTP uploading (#36) which caused crash when uploading files and data larger than 64kb (chunked).
    • url initializing were sometimes nil because path/string were not percent encoded.
    Source code(tar.gz)
    Source code(zip)
    FileProvider.framework.zip(27.21 MB)
  • 0.15.5(Apr 14, 2017)

  • 0.15.4(Apr 11, 2017)

  • 0.15.3(Apr 9, 2017)

  • 0.15.2(Apr 3, 2017)

    session property now can be set by developer. Note that it's delegate must be an instance of SessionDelegate class. You can create a session with background configuration and replace main session like this:

    let sessionDelegate = SessionDelegate(fileProvider: fileProvider, credential: fileProvider.credential)
    let backgroundconfig = URLSessionConfiguration.background(withIdentifier: "com.company.myapp")
    fileProvider.session = URLSession(configuration: backgroundconfig, delegate: sessionDelegate as URLSessionDelegate?, delegateQueue: fileProvider.operation_queue)
    
    • Note: setting session after calling a function may have unpreceded result.

    Also:

    • Fixed issue with colliding handlers between sessions.
    Source code(tar.gz)
    Source code(zip)
    FileProvider.framework.zip(29.59 MB)
  • 0.15.1(Apr 3, 2017)

    Bug Fixed: Calling completion handler for upload tasks

    Added including (file object properties) argument to WebDAV provider this is the sample code for fetching ETag and size property of file from WebDAV:

    provider.attributesOfItem(path: "/path/to/file", including: [.fileSizeKey, .entryTagKey], completionHandler: { attributes, error in
        print(attributes.size)
        print(attributes.entryTag)
    }
    

    This is also true for contentsOfDirectory() method

    Source code(tar.gz)
    Source code(zip)
    FileProvider.framework.zip(28.48 MB)
  • 0.15.0(Apr 1, 2017)

    This release adds FTP protocol support with some minor limitations. (see below)

    Background sessions can be set now for sessions while not recommended due to serious bugs in Apple CFNetwork implementation when you want download a file with a ranged request.

    A new class, FileProviderStreamTask is introduced which is technically a replica of Apple's URLSessionStreamTask with iOS 8 support using low level Core Foundation routines.

    Here is the list of improvements:

    • Credential is open to set/change anytime.
    • Deprecating create(file:) method which was a duplicate of writeContents(path:contents:) method. Now you can pass nil for data argument to create an empty file.

    Here is the list of other bug fixes:

    • WebDAV file listing may omit files contains space in name.
    • Delegate's progress handler didn't call when uploading/downloading was in progress.
    • Swift 3.1 warnings.

    FTP provider limitations:

    • searchFiles() is not implemented and removeItem() won't delete not-empty directories in servers that don't support SITE RMDIR extension command. This limitations will be removed when ftpRecursiveList() is implemented.
    • Active mode is not implemented, technically saying, I didn't find a workaround to set tcp port manually in CFNetwork API
    • FTP over TLS (FTPS) protocol is not tested while implemented.
    Source code(tar.gz)
    Source code(zip)
    FileProvider.framework.zip(28.54 MB)
  • 0.14.5(Mar 25, 2017)

  • 0.14.4(Mar 18, 2017)

    Optimized PDF thumbnail/meta handling.

    • Fixed relativePath(of:) crashing bug.
    • Fixed ISO speed and GPS Area image meta, better ExposureTime calculation.
    • Fixed Dropbox name NOT BEGINSWITH % search query.
    • Better LocalFileObject initialization with empty path.
    • Refactored DispatchTime.
    • Refactored methods to extensions.
    Source code(tar.gz)
    Source code(zip)
    FileProvider.framework.zip(24.97 MB)
  • 0.14.3(Feb 24, 2017)

  • 0.14.1(Feb 20, 2017)

  • 0.14.0(Feb 19, 2017)

    searchFiles() method now moved to FileProviderBasic, where it belonged. Now there is two version for searchFiles(), one of them gets String for query to search within files name, also you can set a NSPredicate object to query to filter files based on properties like size and modification date, please read inline help for sample predicates.

    Also:

    • all methods are now open to become overridable
    • Fixed Documentation errors about urlCache
    Source code(tar.gz)
    Source code(zip)
    FileProvider.framework.zip(24.46 MB)
  • 0.12.9(Feb 16, 2017)

  • 0.12.8(Feb 14, 2017)

  • 0.12.7(Feb 13, 2017)

Owner
Amir Abbas Mousavian
A Swift lover based in Tehran
Amir Abbas Mousavian
A micro-framework for observing file changes, both local and remote. Helpful in building developer tools.

KZFileWatchers Wouldn't it be great if we could adjust feeds and configurations of our native apps without having to sit back to Xcode, change code, r

Krzysztof Zabłocki 1k Sep 14, 2021
Swift framework for zipping and unzipping files.

Zip A Swift framework for zipping and unzipping files. Simple and quick to use. Built on top of minizip. Usage Import Zip at the top of the Swift file

Roy Marmelstein 2.1k Sep 17, 2021
Simple and expressive file management in Swift

Installation • Usage • License • Documentation FileKit is a Swift framework that allows for simple and expressive file management. Development happens

Nikolai Vazquez 2.2k Sep 17, 2021
ZipArchive is a simple utility class for zipping and unzipping files on iOS, macOS and tvOS.

SSZipArchive ZipArchive is a simple utility class for zipping and unzipping files on iOS, macOS and tvOS. Unzip zip files; Unzip password protected zi

ZipArchive 4.9k Sep 17, 2021
zip file I/O library for iOS, macOS and tvOS

ZipZap is a zip file I/O library for iOS, macOS and tvOS. The zip file is an ideal container for compound Objective-C documents. Zip files are widely

Glen Low 1.2k Aug 18, 2021