FileProvider: “CopyItem()” is called twice -> erro

2019-08-18 08:11发布

问题:

The first view of my app (Swift 5, Xcode 10, iOS 12) has a "username" TextField and a "login" Button. Clicking on the button checks if there's a file for the entered username on my FTP server and downloads it to the Documents folder on the device. For this I'm using FileProvider.

My code:

private func download() {
    print("start download") //Only called once!
    let foldername = "myfolder"
    let filename = "mytestfile.txf"
    let server = "192.0.0.1"
    let username = "testuser"
    let password = "testpw"       

    let credential = URLCredential(user: username, password: password, persistence: .permanent)
    let ftpProvider = FTPFileProvider(baseURL: server, mode: FTPFileProvider.Mode.passive, credential: credential, cache: URLCache())

    ftpProvider?.delegate = self as FileProviderDelegate

    let fileManager = FileManager.default
    let source = "/\(foldername)/\(filename)"
    let dest = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent(filename)
    let destPath = dest.path

    if fileManager.fileExists(atPath: destPath) {
        print("file already exists!")

        do {
            try fileManager.removeItem(atPath: destPath)
        } catch {
            print("error removing!") //TODO: Error
        }
        print("still exists: \(fileManager.fileExists(atPath: destPath))")
    } else {
        print("file doesn't already exist!")
    }

    let progress = ftpProvider?.copyItem(path: source, toLocalURL: dest, completionHandler: nil)
    progressBar.observedProgress = progress
}

I'm checking if the file already exists on the device because FileProvider doesn't seem to provide a copyItem function for downloading that also lets you overwrite the local file.

The problem is that copyItem tries to do everything twice: Downloading the file the first time succeeds (and it actually exists in Documents, I checked) because I manually delete the file if it already exists. The second try fails because the file already exists and this copyItem function doesn't know how to overwrite and of course doesn't call my code to delete the original again.

What can I do to fix this?

Edit/Update:

I created a simple "sample.txt" at the root of my ftp server (text inside :"Hello world from sample.txt!"), then tried to just read the file to later save it myself. For this I'm using this code from the "Sample-iOS.swift" file here.

ftpProvider?.contents(path: source, completionHandler: {
    contents, error in
    if let contents = contents {
        print(String(data: contents, encoding: .utf8))
    }
})

But it also does this twice! The output for the "sample.txt" file is:

Optional("Hello world from sample.txt!")
Fetching on sample.txt succeed.
Optional("Hello world from sample.txt!Hello world from sample.txt!")
Fetching on sample.txt succeed.

Why is it calling this twice too? I'm only calling my function once and "start download" is also only printed once.

Edit/Update 2:

I did some more investigating and found out what's called twice in the contents function:

  • It's the whole self.ftpDownload section!
  • And inside FTPHelper.ftpLogin the whole self.ftpRetrieve section is called twice.
  • And inside FTPHelper.ftpRetrieve the whole self.attributesOfItem section is called twice.
  • And probably so on...

ftpProvider?.copyItem uses the same ftpDownload func, so at least I know why both contents() and copyItem() are affected.

The same question remains though: Why is it calling these functions twice and how do I fix this?

回答1:

This isn't an answer that shows an actual fix for FileProvider!

Unfortunately the library is pretty buggy currently, with functions being called twice (which you can kind of prevent by using a "firstTimeCalled" bool check) and if the server's slow(-ish), you also might not get e.g. the full list of files in a directory because FileProvider stops receiving answers before the server's actually done.

I haven't found any other FTP libraries for Swift that work (and are still supported), so now I'm using BlueSocket (which is able to open sockets, send commands to the server and receive commands from it) and built my own small library that can send/receive,... files (using the FTP codes) around it.