Search code examples
iosswiftfile-iofilesystemsnsfilemanager

Trouble working with basic file system functionality in Swift 2.0


Trying to create a simple example code block in Swift 2.0 on iOS 9.1 using Xcode 7.1. Tried this article in techotopia; which I suspect is based on swift 1.2.

Made a few tiny changes so that it would compile & run, but although it appears to work, it doesn't seem to save my string into the file. Is there capability or something subtle I have missed here.

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var textBox: UITextField!

    var fileMgr: NSFileManager = NSFileManager.defaultManager()
    var docsDir: String?
    var dataFile: String?
    var string: String = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        let dirPaths = NSSearchPathForDirectoriesInDomains(
            .DocumentDirectory, .UserDomainMask, true)

        docsDir = dirPaths[0] as String
        let dataFile = NSURL(fileURLWithPath: docsDir!).URLByAppendingPathComponent("datafile.dat")

        string = "\(dataFile)"
        print(string)
        if fileMgr.fileExistsAtPath(string) {
            let databuffer = fileMgr.contentsAtPath(string)
            let datastring = NSString(data: databuffer!,
                encoding: NSUTF8StringEncoding)
            textBox.text = datastring as? String
        }
    }

    @IBAction func saveText(sender: AnyObject) {
        let databuffer = (textBox.text)
        let data = databuffer?.dataUsingEncoding(NSUTF8StringEncoding)

        fileMgr.createFileAtPath(string, contents: data,
            attributes: nil)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

More testing; noticed I get this error when I try and create the file... error in __connection_block_invoke_2: Connection interrupted; which explains why it isn't working, if only I could workout what it is talking about?

Continued to try to debug; added UIFileSharingEnabled but cannot see Documents directory; added more code to test its presence, and create it if missing; fails telling me it is already there... even if it is evidently invisible...


Solution

  • When you do this, string ends up being a string representation of the file URL, e.g. file://.... That file:// prefix is a URL "scheme" (like http:// or ftp://). But including the scheme at the start of the string means that this is not a valid path. You have to remove the scheme.

    The easiest way to do this is to use the path method to get the path from a NSURL without that scheme. I'd also use URLForDirectory to get the URL for the documents folder nowadays.

    class ViewController: UIViewController {
    
        @IBOutlet weak var textBox: UITextField!
    
        lazy var fileMgr = NSFileManager.defaultManager()
        var path: String!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let documents = try! fileMgr.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
            path = documents.URLByAppendingPathComponent("datafile.dat").path
    
            if fileMgr.fileExistsAtPath(path) {
                if let data = fileMgr.contentsAtPath(path) {
                    textBox.text = String(data: data, encoding: NSUTF8StringEncoding)
                }
            }
        }
    
        @IBAction func saveText(sender: AnyObject) {
            let data = textBox.text?.dataUsingEncoding(NSUTF8StringEncoding)
    
            fileMgr.createFileAtPath(path, contents: data, attributes: nil)
        }
    
    }
    

    Or I might stay entirely in the world of URLs, retiring paths altogether, also using methods that throw meaningful error messages:

    class ViewController: UIViewController {
    
        @IBOutlet weak var textBox: UITextField!
    
        lazy var fileMgr = NSFileManager.defaultManager()
        var fileURL: NSURL!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            do {
                let documents = try fileMgr.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
                fileURL = documents.URLByAppendingPathComponent("datafile.dat")
    
                var reachableError: NSError?
                if fileURL.checkResourceIsReachableAndReturnError(&reachableError) {
                    textBox.text = try String(contentsOfURL: fileURL)
                }
            } catch {
                print(error)
            }
        }
    
        @IBAction func saveText(sender: AnyObject) {
            do {
                try textBox.text?.writeToURL(fileURL, atomically: true, encoding: NSUTF8StringEncoding)
            } catch {
                print(error)
            }
        }
    
    }