0

I am trying to load a .txt file that has the following format

Person Position Data Average Goal
Person One Director 37 45 80
Person Two Assistant 23 56 34
Person Three CEO 34 45 67

There are five columns, with the first row being a header. Below is the code I am using within my viewDidLoad:


override func viewDidLoad() {
    super.viewDidLoad()
    // load txt file from dropbox
    let url = URL(string: "https://www.dropbox.com/s/pr0ldvdeab48mpp/prueba.txt?dl=0")
    let task = URLSession.shared.downloadTask(with: url!) {(urlresponse, response, error) in
        guard let originalURL = urlresponse else { return }
        do {
            // get path to directory
            let path = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
            // giving name to file
            let newUrl = path.appendingPathComponent("myTextFile")
            // move file from old to new url
            try FileManager.default.moveItem(at: originalURL, to: newUrl)
        } catch { 
            print(error.localizedDescription)
            return
        }
    }
    task.resume()
    //reading the file
    let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
    do {
        let fileUrls = try FileManager.default.contentsOfDirectory(at: path!, includingPropertiesForKeys: nil)
        //read the text
        let textContent = try String(contentsOf: fileUrls[0], encoding: .utf8)
        print(textContent)
    } catch {
        print("ERROR")
    }
}

However, I get the error below on the line

let textContent = try String(contentsOf: fileUrls[0], encoding: .utf8)

Thread 1: Fatal error: Index out of range

My goal is to be able to read the file and access elements of each column/row individually when needed but not able to identify the source of the problem. Appreciate any help.

Note: If it helps, I can be flexible loading the file as a .csv or other format if that is easier.

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Jdv
  • 329
  • 1
  • 10
  • Show your dropbox link. Are you sure the file was downloaded successfully? – Leo Dabus Sep 21 '20 at 22:45
  • I am not showing it on purpose as I can't share the actual file, but the data sample I shared on the post reflects the structure of the file. I was hoping this suffices but understand if it doesn't. – Jdv Sep 21 '20 at 22:47
  • dropbox link it is not a direct link that's why I am asking. You create a sample file just for testing – Leo Dabus Sep 21 '20 at 22:48
  • Also make sure you only try to load its content after it finishes. We don't know where you loading code is being executed – Leo Dabus Sep 21 '20 at 22:51
  • Your issue is that `dl=0` doesn't point to the file. It will point to a html file. You need to use `dl=1` to be able to download the file directly. – Leo Dabus Sep 21 '20 at 22:53
  • I added the link and replaced the 0 with a 1 at the end but still getting the same error. – Jdv Sep 21 '20 at 22:56
  • I edit the link because the error is the same – Jdv Sep 21 '20 at 22:56
  • It works for me here but I am loading newUrl inside the closure – Leo Dabus Sep 21 '20 at 22:59
  • could you share the code? – Jdv Sep 21 '20 at 23:01

1 Answers1

2

Your issue is that dl=0 doesn't point to the file. It will point to a html file. You need to use dl=1 to be able to download the file directly. You should also move your loading code inside the completion closure from your download task. You can also get the file name from the url response. Try like this:

// load txt file from dropbox (make sure you change the string suffix from dl=0 to dl=1)
let url = URL(string: "https://www.dropbox.com/s/pr0ldvdeab48mpp/prueba.txt?dl=1")!
let task = URLSession.shared.downloadTask(with: url) { location, response, error in
    guard let location = location else { return }
    do {
        // get the documentDirectory url
        let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
        print(documentDirectory.path)
        // get the suggested name fro the url response or the url last path component
        let name = (response as? HTTPURLResponse)?.suggestedFilename ?? location.lastPathComponent
        // create a destination url
        let destination = documentDirectory.appendingPathComponent(name)
        // check if the file already exists
        if FileManager.default.fileExists(atPath: destination.path) {
            // remove the file (if you would like to use the new one)
            try FileManager.default.removeItem(at: destination)
        }
        // move file from the temporary location to the destination
        try FileManager.default.moveItem(at: location, to: destination)
        // reading the downloaded file
        let textContent = try String(contentsOf: destination, encoding: .utf8)
        print(textContent)
    } catch {
        print("ERROR")
    }
}
task.resume()
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • I cam across another issue. The file loads when I launch the simulator first time, but every time after that it prints ERROR. I have to go to the folder where the file is and remove it manually. Then I launch the simulator again and it works. Is there a way to add this functionality to the code? Not sure if that will give me trouble down the road. Happy to add this as a separate question if needed. – Jdv Sep 22 '20 at 16:37
  • `print("Error", error)` and check what it says. "ERROR" is too vague – Leo Dabus Sep 22 '20 at 16:43
  • here is the error: {Error Domain = NSPOSIXErrorDomain Code = 17 "File exists"} – Jdv Sep 22 '20 at 18:25
  • You are trying to replace a file that already exists. You have to manually delete de previous file before moving the new one or just skip it. – Leo Dabus Sep 22 '20 at 18:30
  • correct. Yes I can skip the moving step and it works. Just wondering if that would cause problems later – Jdv Sep 22 '20 at 18:33
  • check my last edit. If you want to make sure that the file is up to date you better remove it and save it again. – Leo Dabus Sep 22 '20 at 18:35
  • Thank you. From your edit, I had to adjust **FileManager.default.removeItem(at: destination.path)** as **try FileManager.default.removeItem(atPath: destination.path)** and it seems to work – Jdv Sep 22 '20 at 18:46
  • 1
    Yes it `throws`. I forgot to add the try keyword when editing and it should be the url not its path. `try FileManager.default.removeItem(at: destination)` – Leo Dabus Sep 22 '20 at 18:47