371

I need to read and write data to/from a text file, but I haven't been able to figure out how.

I found this sample code in the Swift's iBook, but I still don't know how to write or read data.

import Cocoa

class DataImporter {
    /*
    DataImporter is a class to import data from an external file.
    The class is assumed to take a non-trivial amount of time to initialize.
    */
    var fileName = "data.txt"
    // the DataImporter class would provide data importing functionality here
}

class DataManager {
    @lazy var importer = DataImporter()
    var data = String[]()
    // the DataManager class would provide data management functionality here
}

let manager = DataManager()
manager.data += "Some data"
manager.data += "Some more data"
// the DataImporter instance for the importer property has not yet been created”

println(manager.importer.fileName)
// the DataImporter instance for the importer property has now been created
// prints "data.txt”    

var str = "Hello World in Swift Language."
Paulo Mattos
  • 18,845
  • 10
  • 77
  • 85
Jorge Vega Sánchez
  • 7,430
  • 14
  • 55
  • 77

21 Answers21

635

For reading and writing you should use a location that is writeable, for example documents directory. The following code shows how to read and write a simple string. You can test it on a playground.

Swift 3.x - 5.x

let file = "file.txt" //this is the file. we will write to and read from it

let text = "some text" //just a text

if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {

    let fileURL = dir.appendingPathComponent(file)

    //writing
    do {
        try text.write(to: fileURL, atomically: false, encoding: .utf8)
    }
    catch {/* error handling here */}

    //reading
    do {
        let text2 = try String(contentsOf: fileURL, encoding: .utf8)
    }
    catch {/* error handling here */}
}

Swift 2.2

let file = "file.txt" //this is the file. we will write to and read from it

let text = "some text" //just a text

if let dir = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first {
    let path = NSURL(fileURLWithPath: dir).URLByAppendingPathComponent(file)

    //writing
    do {
        try text.writeToURL(path, atomically: false, encoding: NSUTF8StringEncoding)
    }
    catch {/* error handling here */}

    //reading
    do {
        let text2 = try NSString(contentsOfURL: path, encoding: NSUTF8StringEncoding)
    }
    catch {/* error handling here */}
}

Swift 1.x

let file = "file.txt"

if let dirs : [String] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String] {
    let dir = dirs[0] //documents directory
    let path = dir.stringByAppendingPathComponent(file);
    let text = "some text"

    //writing
    text.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: nil);

    //reading
    let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
}
Adam
  • 26,549
  • 8
  • 62
  • 79
  • 3
    let text2 = String.stringWithContentsOfFile(path) // XCode 6.0 – Matt Frear Oct 26 '14 at 21:43
  • Using this solution works, but if I open the file there's no text in it. Am I missing anything? – Nuno Gonçalves Nov 08 '14 at 16:20
  • @Adam What is this file at let path = dir.stringByAppendingPathComponent(file);? – zbz.lvlv Dec 06 '14 at 06:54
  • @zbz.lvlv It's the name of the file. – Adam Dec 08 '14 at 12:09
  • @Adam : using .writeToFile overrides the content of the file, is it possible to append a string to already existing content of the file ? – Sashi Apr 23 '15 at 18:42
  • Why did you set `atomically` to `false` in `writeToFile`? – Crashalot Jul 22 '15 at 20:03
  • @Sashi If you want to append string to a file you can do the following: read the file to string, append your string and write it back to the original location. Not the most efficient but it will work. – Adam Jul 23 '15 at 07:50
  • @Crashalot You decide if you need the write to be atomic or not. It depends on your implementation. There is no rule of thumb here. See [NSString Class Reference](https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html#//apple_ref/occ/instm/NSString/writeToFile:atomically:encoding:error:) for more details. – Adam Jul 23 '15 at 08:17
  • casting `as? [String] ` is not necessary in Swift 2.0 – fnc12 Aug 22 '15 at 11:59
  • Just a minor upgrade for Swift 2.0. You can use `let text2 = try? NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)` , if you don't do anything in catch. If you want to just return nil, it's clearer solution. https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html – Maciej Kozieł Nov 05 '15 at 20:38
  • 9
    This should be removed, code does not work for new versions of Swift. –  Apr 16 '16 at 22:28
  • @Yusha Just checked with the newest version of Xcode (7.3) and it is working perfectly. – Adam Apr 17 '16 at 06:53
  • @Adam do you know how to access the files after you write something to it? I am writing a simple app where I want to save strings to a file on daily bases, so how can I get that file afterwards? – timon_the_destroyer Aug 02 '16 at 13:08
  • 1
    @billy_b29 The code after this line: `//reading` does exactly that. – Adam Aug 03 '16 at 13:50
  • In Swift 3.0, you can remove most of the "NS" prefixes if you want: e.g., `let text2 = try String(contentsOf: path, encoding: String.Encoding.utf8)`. – AthanasiusOfAlex Sep 27 '16 at 12:33
  • Is there a `"filestream.close()"` to call? Sometimes, after the program runs, the file is not written to the hard drive.. I wonder why. – Van Du Tran May 01 '17 at 14:48
  • Why do I get "No calls to throwing functions occur within 'try' expression" warning next to the "try text.write..." line? I'm using Swift 3.0 & Xcode 8.2.1 – user1258202 Jul 12 '17 at 11:51
  • I think it would be better if you arrange the code to have the swift 4 at top :-p. – Suhaib Feb 13 '18 at 15:15
  • Delete Handler is also useful `do { try FileManager.default.removeItem(at: fileURL) } catch {/* error handling here */}` – Mike Irving Jun 25 '19 at 15:42
  • You need to `import Foundation`. –  Oct 01 '20 at 07:25
111

Assuming that you have moved your text file data.txt to your Xcode-project (Use drag'n'drop and check "Copy files if necessary") you can do the following just like in Objective-C:

let bundle = NSBundle.mainBundle()
let path = bundle.pathForResource("data", ofType: "txt")        
let content = NSString.stringWithContentsOfFile(path) as String

println(content) // prints the content of data.txt

Update:
For reading a file from Bundle (iOS) you can use:

let path = NSBundle.mainBundle().pathForResource("FileName", ofType: "txt")
var text = String(contentsOfFile: path!, encoding: NSUTF8StringEncoding, error: nil)!
println(text)

Update for Swift 3:

let path = Bundle.main.path(forResource: "data", ofType: "txt") // file path for file "data.txt"
var text = String(contentsOfFile: path!, encoding: NSUTF8StringEncoding, error: nil)!

For Swift 5

let path = Bundle.main.path(forResource: "ListAlertJson", ofType: "txt") // file path for file "data.txt"
let string = try String(contentsOfFile: path!, encoding: String.Encoding.utf8)
Kuntal Gajjar
  • 792
  • 6
  • 12
Dennis Zoma
  • 2,621
  • 2
  • 17
  • 27
76

New simpler and recommended method: Apple recommends using URLs for filehandling and the other solutions here seem deprecated (see comments below). The following is the new simple way of reading and writing with URL's:

Swift 5+, 4 and 3.1

import Foundation  // Needed for those pasting into Playground

let fileName = "Test"
let dir = try? FileManager.default.url(for: .documentDirectory, 
      in: .userDomainMask, appropriateFor: nil, create: true)

guard let fileURL = dir?.appendingPathComponent(fileName).appendingPathExtension("txt") else {
    fatalError("Not able to create URL")
}
    
// Writing to the file named Test
let outString = "Write this text to the file"
do {
    try outString.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
    assertionFailure("Failed writing to URL: \(fileURL), Error: " + error.localizedDescription)
}
    
// Reading it back from the file
var inString = ""
do {
    inString = try String(contentsOf: fileURL)
} catch {
    assertionFailure("Failed reading from URL: \(fileURL), Error: " + error.localizedDescription)
}
print("Read from the file: \(inString)")
Sverrisson
  • 17,970
  • 5
  • 66
  • 62
  • 1
    Can you provide a reference where Apple recommends such way. Or can you elaborate a little more on why is this the recommended way? – Andrej Apr 08 '16 at 15:58
  • 8
    @Andrej "URL objects are the preferred way to refer to local files. Most objects that read data from or write data to a file have methods that accept an NSURL object instead of a pathname as the file reference." https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSURL_Class/ – Sverrisson Apr 09 '16 at 00:35
  • 1
    You don't have to cast the errors as NSError, or even use "catch let error." You can just do catch and you get the error variable for free. – cuomo456 Nov 18 '16 at 00:36
  • @cuomo456 your right I remove it, it's a leftover from previous Swift beta. – Sverrisson Nov 18 '16 at 10:09
  • Probably the most useful answer on this page (there's a joke here somewhere - how many engineers does it take to write Swift code to do text file I/O?). But I do have a question. Am I to assume that the only way to write a text file is to create one humungous String and do a single write()? Because if I try to write multiple Strings to the same URL, the result is that the file contains only the last String written. – Paul Martz Nov 02 '17 at 21:08
  • @user1593861 This is the simplest method when you already have a string and it's not extremely large. The write is done asynchronously. If you have a humungous string then you can write to file using Streams, but it is beyond this answer, see further https://stackoverflow.com/questions/36120854/swift-writing-a-byte-stream-to-file . Thanks for the nice comment :) – Sverrisson Nov 03 '17 at 09:16
  • 1
    @Alshcompiler The create: true informs FileManager to create the directory if it's not already there, instead of failing – Sverrisson Apr 15 '18 at 16:01
  • hi all, tried tis code but I cannot solve my issue, could you help me here: https://stackoverflow.com/questions/53598917/swift-4-how-to-pick-and-send-txt-file-via-post-request/53600049?noredirect=1#comment94067133_53600049 – biggreentree Dec 03 '18 at 22:20
  • If i want to read data from .docx file then how will it be possible? – Birju Jun 21 '19 at 12:09
  • @Birju DOCX is a zipped XML file so you would open it and parse the XML. But the details to do it are way beyond a comment. – Sverrisson Jun 21 '19 at 14:03
  • @HannesSverrisson , thank you for your reply. Can you give me any code stuff or example that how to do it actually ?. Because i am stuck to convert .docx to text. – Birju Jun 24 '19 at 04:37
  • @Birju Thats a lot of work, but if you send me a file I can make a code for you. – Sverrisson Jun 25 '19 at 09:11
  • To elaborate why URL is the preferred method over paths, is mainly that paths can be abused, i.e. (path = "../../secret.txt"). Thus, a good practice is to use url as possible. – Sverrisson Apr 19 '22 at 10:59
71

Xcode 8.x • Swift 3.x or later

do {
    // get the documents folder url
    if let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
        // create the destination url for the text file to be saved
        let fileURL = documentDirectory.appendingPathComponent("file.txt")
        // define the string/text to be saved
        let text = "Hello World !!!"
        // writing to disk 
        // Note: if you set atomically to true it will overwrite the file if it exists without a warning
        try text.write(to: fileURL, atomically: false, encoding: .utf8)
        print("saving was successful")
        // any posterior code goes here
        // reading from disk
        let savedText = try String(contentsOf: fileURL)
        print("savedText:", savedText)   // "Hello World !!!\n"
    }
} catch {
    print("error:", error)
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • What is most common mistakes of "There is no such file." Because i added my .txt files to project navigator and then i try to open them i get this message. (Create them on desktop and dragged them to project navigator) – Darvas Nov 17 '15 at 15:53
31

Xcode 8, Swift 3 way to read file from the app bundle:

if let path = Bundle.main.path(forResource: filename, ofType: nil) {
    do {
        let text = try String(contentsOfFile: path, encoding: String.Encoding.utf8)
        print(text)
    } catch {
        printError("Failed to read text from \(filename)")
    }
} else {
    printError("Failed to load file from app bundle \(filename)")
} 

Here's a convenient copy and paste Extension

public extension String {
    func contentsOrBlank()->String {
        if let path = Bundle.main.path(forResource:self , ofType: nil) {
            do {
                let text = try String(contentsOfFile:path, encoding: String.Encoding.utf8)
                return text
                } catch { print("Failed to read text from bundle file \(self)") }
        } else { print("Failed to load file from bundle \(self)") }
        return ""
    }
    }

For example

let t = "yourFile.txt".contentsOrBlank()

You almost always want an array of lines:

let r:[String] = "yourFile.txt"
     .contentsOrBlank()
     .characters
     .split(separator: "\n", omittingEmptySubsequences:ignore)
     .map(String.init)
Fattie
  • 27,874
  • 70
  • 431
  • 719
Crashalot
  • 33,605
  • 61
  • 269
  • 439
11

I want to show you only the first part, that is read. Here's how simply you can read:

Swift 3:

let s = try String(contentsOfFile: Bundle.main.path(forResource: "myFile", ofType: "txt")!)

Swift 2:

let s = try! String(contentsOfFile: NSBundle.mainBundle().pathForResource("myFile", ofType: "txt")!)
ha100
  • 1,563
  • 1
  • 21
  • 28
A. K. M. Tariqul Islam
  • 2,824
  • 6
  • 31
  • 48
5

Simplest way to read a file in Swift > 4.0

 let path = Bundle.main.path(forResource: "data", ofType: "txt") // file path for file "data.txt"
        do {
            var text = try String(contentsOfFile: path!)
        }
        catch(_){print("error")}
    }
iosMentalist
  • 3,066
  • 1
  • 30
  • 40
4

You may find this tool useful to not only read from file in Swift but also parse your input: https://github.com/shoumikhin/StreamScanner

Just specify the file path and data delimiters like this:

import StreamScanner

if let input = NSFileHandle(forReadingAtPath: "/file/path")
{
    let scanner = StreamScanner(source: input, delimiters: NSCharacterSet(charactersInString: ":\n"))  //separate data by colons and newlines

    while let field: String = scanner.read()
    {
        //use field
    }
}

Hope, this helps.

shoumikhin
  • 1,327
  • 15
  • 23
4

This works with Swift 3.1.1 on Linux:

import Foundation

let s = try! String(contentsOfFile: "yo", encoding: .utf8)
Adam Dingle
  • 3,114
  • 1
  • 16
  • 14
3

The current accepted answer above from Adam had some errors for me but here is how I reworked his answer and made this work for me.

let file = "file.txt"

let dirs: [String]? = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String]

if (dirs != nil) {
    let directories:[String] = dirs!
    let dirs = directories[0]; //documents directory
    let path = dirs.stringByAppendingPathComponent(file);
    let text = "some text"

    //writing
    text.writeToFile(path, atomically: false, encoding: NSUTF8StringEncoding, error: nil);

    //reading
     var error:NSError?

    //reading
    let text2 = String(contentsOfFile: path, encoding:NSUTF8StringEncoding, error: &error)

    if let theError = error {
        print("\(theError.localizedDescription)")
    }
}
DCorrigan
  • 646
  • 1
  • 8
  • 18
3

To avoid confusion and add ease, I have created two functions for reading and writing strings to files in the documents directory. Here are the functions:

func writeToDocumentsFile(fileName:String,value:String) {
    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! NSString
    let path = documentsPath.stringByAppendingPathComponent(fileName)
    var error:NSError?
    value.writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding, error: &error)
}

func readFromDocumentsFile(fileName:String) -> String {
    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! NSString
    let path = documentsPath.stringByAppendingPathComponent(fileName)
    var checkValidation = NSFileManager.defaultManager()
    var error:NSError?
    var file:String

    if checkValidation.fileExistsAtPath(path) {
        file = NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil) as! String
    } else {
        file = "*ERROR* \(fileName) does not exist."
    }

    return file
}

Here is an example of their use:

writeToDocumentsFile("MyText.txt","Hello world!")

let value = readFromDocumentsFile("MyText.txt")
println(value)  //Would output 'Hello world!'

let otherValue = readFromDocumentsFile("SomeText.txt")
println(otherValue)  //Would output '*ERROR* SomeText.txt does not exist.'

Hope this helps!

Xcode Version: 6.3.2

2

I had to recode like this:

let path = NSBundle.mainBundle().pathForResource("Output_5", ofType: "xml")
let text = try? NSString(contentsOfFile: path! as String, encoding: NSUTF8StringEncoding)
print(text)
Nagarjun
  • 6,557
  • 5
  • 33
  • 51
2

In the function example, (read|write)DocumentsFromFile(...) having some function wrappers certainly seems to makes sense since everything in OSx and iOS seems to need three or four major classes instantiated and a bunch of properties, configured, linked, instantiated, and set, just to write "Hi" to a file, in 182 countries.

However, these examples aren't complete enough to use in a real program. The write function does not report any errors creating or writing to the file. On the read, I don't think it's a good idea to return an error that the file doesn't exist as the string that is supposed to contain the data that was read. You would want to know that it failed and why, through some notification mechanism, like an exception. Then, you can write some code that outputs what the problem is and allows the user to correct it, or "correctly" breaks the program at that point.

You would not want to just return a string with an "Error file does not exist" in it. Then, you would have to look for the error in the string from calling function each time and handle it there. You also possibly couldn't really tell if the error string was actually read from an actual file, or if it was produced from your code.

You can't even call the read like this in swift 2.2 and Xcode 7.3 because NSString(contentsOfFile...) throws an exception. It is a compile time error if you do not have any code to catch it and do something with it, like print it to stdout, or better, an error popup window, or stderr. I have heard that Apple is moving away from try catch and exceptions, but it's going to be a long move and it's not possible to write code without this. I don't know where the &error argument comes from, perhaps an older version, but NSString.writeTo[File|URL] does not currently have an NSError argument. They are defined like this in NSString.h :

public func writeToURL(url: NSURL, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws
public func writeToFile(path: String, atomically useAuxiliaryFile: Bool, encoding enc: UInt) throws
public convenience init(contentsOfURL url: NSURL, encoding enc: UInt) throws
public convenience init(contentsOfFile path: String, encoding enc: UInt) throws

Also, the file not existing is just one of a number of potential problems your program might have reading a file, such as a permissions problem, the file size, or numerous other issues that you would not even want to try to code a handler for each one of them. It's best to just assume it's all correct and catch and print, or handle, an exception if something goes amiss, besides, at this point, you don't really have a choice anyway.

Here are my rewrites :

func writeToDocumentsFile(fileName:String,value:String) {

    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString!
    let path = documentsPath.stringByAppendingPathComponent(fileName)

    do {
        try value.writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding)
    } catch let error as NSError {
        print("ERROR : writing to file \(path) : \(error.localizedDescription)")
    }

}

func readFromDocumentsFile(fileName:String) -> String {

    let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as NSString
    let path = documentsPath.stringByAppendingPathComponent(fileName)

    var readText : String = ""

    do {
        try readText = NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding) as String
    }
    catch let error as NSError {
        print("ERROR : reading from file \(fileName) : \(error.localizedDescription)")
    }
    return readText
}
Sam Allen
  • 19
  • 4
  • In many of your responses I think you're not grasping my point. ( or you may not care and that's ok ). However, to be clear, throwing an exception, and handling it somehow when you are looking for a file that's not there ( or has another issue such as a permission ) is far better than returning a string such as "ERROR: File [filename] does not exist" as the string you were supposed to actually read from the file. Then just printing that. If anything you should print the exception details, not the failed to be read string that now has an error in it.The program probably shouldn't just continue. – Sam Allen Oct 17 '17 at 14:32
2

For my txt file works this way:

let myFileURL = NSBundle.mainBundle().URLForResource("listacomuni", withExtension: "txt")!
let myText = try! String(contentsOfURL: myFileURL, encoding: NSISOLatin1StringEncoding)
print(String(myText))
Alessandro Mattiuzzi
  • 2,309
  • 2
  • 18
  • 24
2

Latest swift3 code
You can read data from text file just use bellow code This my text file

     {
"NumberOfSlices": "8",
"NrScenes": "5",
"Scenes": [{
           "dataType": "label1",
           "image":"http://is3.mzstatic.com/image/thumb/Purple19/v4/6e/81/31/6e8131cf-2092-3cd3-534c-28e129897ca9/mzl.syvaewyp.png/53x53bb-85.png",

           "value": "Hello",
           "color": "(UIColor.red)"
           }, {
           "dataType": "label2",
           "image":"http://is1.mzstatic.com/image/thumb/Purple71/v4/6c/4c/c1/6c4cc1bc-8f94-7b13-f3aa-84c41443caf3/mzl.hcqvmrix.png/53x53bb-85.png",
           "value": "Hi There",
           "color": "(UIColor.blue)"
           }, {
           "dataType": "label3",
           "image":"http://is1.mzstatic.com/image/thumb/Purple71/v4/6c/4c/c1/6c4cc1bc-8f94-7b13-f3aa-84c41443caf3/mzl.hcqvmrix.png/53x53bb-85.png",

           "value": "hi how r u ",
           "color": "(UIColor.green)"
           }, {
           "dataType": "label4",
           "image":"http://is1.mzstatic.com/image/thumb/Purple71/v4/6c/4c/c1/6c4cc1bc-8f94-7b13-f3aa-84c41443caf3/mzl.hcqvmrix.png/53x53bb-85.png",
           "value": "what are u doing  ",
           "color": "(UIColor.purple)"
           }, {
           "dataType": "label5",
          "image":"http://is1.mzstatic.com/image/thumb/Purple71/v4/6c/4c/c1/6c4cc1bc-8f94-7b13-f3aa-84c41443caf3/mzl.hcqvmrix.png/53x53bb-85.png",
           "value": "how many times ",
           "color": "(UIColor.white)"
           }, {
           "dataType": "label6",
           "image":"http://is1.mzstatic.com/image/thumb/Purple71/v4/5a/f3/06/5af306b0-7cac-1808-f440-bab7a0d18ec0/mzl.towjvmpm.png/53x53bb-85.png",
           "value": "hi how r u ",
           "color": "(UIColor.blue)"
           }, {
           "dataType": "label7",
           "image":"http://is5.mzstatic.com/image/thumb/Purple71/v4/a8/dc/eb/a8dceb29-6daf-ca0f-d037-df9f34cdc476/mzl.ukhhsxik.png/53x53bb-85.png",
           "value": "hi how r u ",
           "color": "(UIColor.gry)"
           }, {
           "dataType": "label8",
           "image":"http://is2.mzstatic.com/image/thumb/Purple71/v4/15/23/e0/1523e03c-fff2-291e-80a7-73f35d45c7e5/mzl.zejcvahm.png/53x53bb-85.png",
           "value": "hi how r u ",
           "color": "(UIColor.brown)"
           }]

}

You can use this code you get data from text json file in swift3

     let filePath = Bundle.main.path(forResource: "nameoftheyourjsonTextfile", ofType: "json")


    let contentData = FileManager.default.contents(atPath: filePath!)
    let content = NSString(data: contentData!, encoding: String.Encoding.utf8.rawValue) as? String

    print(content)
    let json = try! JSONSerialization.jsonObject(with: contentData!) as! NSDictionary
    print(json)
    let app = json.object(forKey: "Scenes") as! NSArray!
    let _ : NSDictionary
    for dict in app! {
        let colorNam = (dict as AnyObject).object(forKey: "color") as! String
        print("colors are \(colorNam)")

       // let colour = UIColor(hexString: colorNam) {
       // colorsArray.append(colour.cgColor)
       // colorsArray.append(colorNam  as! UIColor)

        let value = (dict as AnyObject).object(forKey: "value") as! String
        print("the values are \(value)")
        valuesArray.append(value)

        let images = (dict as AnyObject).object(forKey: "image") as! String
        let url = URL(string: images as String)
        let data = try? Data(contentsOf: url!)
        print(data)
        let image1 = UIImage(data: data!)! as UIImage
        imagesArray.append(image1)
         print(image1)
            }
2

It is recommended to read and write files asynchronously! and it's so easy to do in pure Swift,
here is the protocol:

protocol FileRepository {
    func read(from path: String) throws -> String
    func readAsync(from path: String, completion: @escaping (Result<String, Error>) -> Void)
    func write(_ string: String, to path: String) throws
    func writeAsync(_ string: String, to path: String, completion: @escaping (Result<Void, Error>) -> Void)
}

As you can see it allows you to read and write files synchronously or asynchronously.

Here is my implementation in Swift 5:

class DefaultFileRepository {
    
    // MARK: Properties
    
    let queue: DispatchQueue = .global()
    let fileManager: FileManager = .default
    lazy var baseURL: URL = {
        try! fileManager
            .url(for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
            .appendingPathComponent("MyFiles")
    }()
    
    
    // MARK: Private functions
    
    private func doRead(from path: String) throws -> String {
        let url = baseURL.appendingPathComponent(path)
        
        var isDir: ObjCBool = false
        guard fileManager.fileExists(atPath: url.path, isDirectory: &isDir) && !isDir.boolValue else {
            throw ReadWriteError.doesNotExist
        }
        
        let string: String
        do {
            string = try String(contentsOf: url)
        } catch {
            throw ReadWriteError.readFailed(error)
        }
        
        return string
    }
    
    private func doWrite(_ string: String, to path: String) throws {
        let url = baseURL.appendingPathComponent(path)
        let folderURL = url.deletingLastPathComponent()
        
        var isFolderDir: ObjCBool = false
        if fileManager.fileExists(atPath: folderURL.path, isDirectory: &isFolderDir) {
            if !isFolderDir.boolValue {
                throw ReadWriteError.canNotCreateFolder
            }
        } else {
            do {
                try fileManager.createDirectory(at: folderURL, withIntermediateDirectories: true)
            } catch {
                throw ReadWriteError.canNotCreateFolder
            }
        }
        
        var isDir: ObjCBool = false
        guard !fileManager.fileExists(atPath: url.path, isDirectory: &isDir) || !isDir.boolValue else {
            throw ReadWriteError.canNotCreateFile
        }
        
        guard let data = string.data(using: .utf8) else {
            throw ReadWriteError.encodingFailed
        }
        
        do {
            try data.write(to: url)
        } catch {
            throw ReadWriteError.writeFailed(error)
        }
    }
    
}


extension DefaultFileRepository: FileRepository {
    func read(from path: String) throws -> String {
        try queue.sync { try self.doRead(from: path) }
    }
    
    func readAsync(from path: String, completion: @escaping (Result<String, Error>) -> Void) {
        queue.async {
            do {
                let result = try self.doRead(from: path)
                completion(.success(result))
            } catch {
                completion(.failure(error))
            }
        }
    }
    
    func write(_ string: String, to path: String) throws {
        try queue.sync { try self.doWrite(string, to: path) }
    }
    
    func writeAsync(_ string: String, to path: String, completion: @escaping (Result<Void, Error>) -> Void) {
        queue.async {
            do {
                try self.doWrite(string, to: path)
                completion(.success(Void()))
            } catch {
                completion(.failure(error))
            }
        }
    }
    
}


enum ReadWriteError: LocalizedError {
    
    // MARK: Cases
    
    case doesNotExist
    case readFailed(Error)
    case canNotCreateFolder
    case canNotCreateFile
    case encodingFailed
    case writeFailed(Error)
}

Amir Khorsandi
  • 3,542
  • 1
  • 34
  • 38
1

write in ViewDidLoad

var error: NSError?
var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
var documentsDirectory = paths.first as String
var dataPath = documentsDirectory.stringByAppendingPathComponent("MyFolder")

if !NSFileManager.defaultManager().fileExistsAtPath(dataPath) {
    NSFileManager.defaultManager().createDirectoryAtPath(dataPath, withIntermediateDirectories: false, attributes: nil, error: &error)
} else {
    println("not creted or exist")
}

func listDocumentDirectoryfiles() -> [String] {
    if let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as? String {
        let myFilePath = documentDirectory.stringByAppendingPathComponent("MyFolder")
        return NSFileManager.defaultManager().contentsOfDirectoryAtPath(myFilePath, error: nil) as [String]
    }
    return []
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
NRV
  • 57
  • 1
1
 func writeToDocumentsFile(fileName:String,value:String) {
    let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
    let path = documentsPath.appendingPathComponent(fileName)
    do{
    try value.write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
}catch{
    }
    }

func readFromDocumentsFile(fileName:String) -> String {
    let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString
    let path = documentsPath.appendingPathComponent(fileName)
    let checkValidation = FileManager.default
    var file:String

    if checkValidation.fileExists(atPath: path) {
        do{
       try file = NSString(contentsOfFile: path, encoding: String.Encoding.utf8.rawValue) as String
        }catch{
            file = ""
        }
        } else {
        file = ""
    }

    return file
}
maroc
  • 21
  • 3
1

Earlier solutions answers question, but in my case deleting old content of file while writing was a problem.

So, I created piece of code for writing to file in documents directory without deleting previous content. You probably need better error handling, but I believe it's good starting point. Swift 4. Usuage:

    let filename = "test.txt"
    createOrOverwriteEmptyFileInDocuments(filename: filename)
    if let handle = getHandleForFileInDocuments(filename: filename) {
        writeString(string: "aaa", fileHandle: handle)
        writeString(string: "bbb", fileHandle: handle)
        writeString(string: "\n", fileHandle: handle)
        writeString(string: "ccc", fileHandle: handle)
    }

Helper methods:

func createOrOverwriteEmptyFileInDocuments(filename: String){
    guard let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
        debugPrint("ERROR IN createOrOverwriteEmptyFileInDocuments")
        return
    }
    let fileURL = dir.appendingPathComponent(filename)
    do {
        try "".write(to: fileURL, atomically: true, encoding: .utf8)
    }
    catch {
        debugPrint("ERROR WRITING STRING: " + error.localizedDescription)
    }
    debugPrint("FILE CREATED: " + fileURL.absoluteString)
}

private func writeString(string: String, fileHandle: FileHandle){
    let data = string.data(using: String.Encoding.utf8)
    guard let dataU = data else {
        debugPrint("ERROR WRITING STRING: " + string)
        return
    }
    fileHandle.seekToEndOfFile()
    fileHandle.write(dataU)
}

private func getHandleForFileInDocuments(filename: String)->FileHandle?{
    guard let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
        debugPrint("ERROR OPENING FILE")
        return nil
    }
    let fileURL = dir.appendingPathComponent(filename)
    do {
        let fileHandle: FileHandle? = try FileHandle(forWritingTo: fileURL)
        return fileHandle
    }
    catch {
        debugPrint("ERROR OPENING FILE: " + error.localizedDescription)
        return nil
    }
}
Szuwar_Jr
  • 683
  • 8
  • 11
1

Swift 3.x - 5.x

The Best Example is to Create a Local Logfile with an Extension .txt that can visible and show in the "Files App" with current date and Time as a File Name

just add this code in info.plist enable these two features

  UIFileSharingEnabled
  LSSupportsOpeningDocumentsInPlace

and this Function Below

var logfileName : String = ""
func getTodayString() -> String{

    let date = Date()
    let calender = Calendar.current
    let components = calender.dateComponents([.year,.month,.day,.hour,.minute,.second], from: date)

    let year = components.year
    let month = components.month
    let day = components.day
    let hour = components.hour
    let minute = components.minute
    let second = components.second

    let today_string = String(year!) + "-" + String(month!) + "-" + String(day!) + "-" + String(hour!)  + "" + String(minute!) + "" +  String(second!)+".txt"

    return today_string

}

func LogCreator(){
    logfileName = getTodayString()

    print("LogCreator: Logfile Generated Named: \(logfileName)")

    let file = logfileName //this is the file. we will write to and read from it

    let text = "some text" //just a text

    if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {

        let fileURL = dir.appendingPathComponent(file)
        let documentPath = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask, true)[0]
        print("LogCreator: The Logs are Stored at location \(documentPath)")


        //writing
        do {
            try text.write(to: fileURL, atomically: false, encoding: .utf8)
        }
        catch {/* error handling here */}

        //reading
        do {
            let text2 = try String(contentsOf: fileURL, encoding: .utf8)
            print("LogCreator: The Detail log are :-\(text2)")
        }
        catch {/* error handling here */}
    }
}


  [1]: https://i.stack.imgur.com/4eg12.png
Vishal Shelake
  • 572
  • 1
  • 8
  • 12
  • I tried this, but must have missed something. It saves my document and puts it in file:///var/mobile/Containers/Data/Application/E4BF1065-3B48-4E53-AC1D-0DC893CCB498/Documents/ but I cannot find it in files. – user3069232 Apr 29 '20 at 13:32
  • 1
    I missed THIS key... CFBundleDisplayName ${PRODUCT_NAME} worked in iOS 13, Swift 5 – user3069232 Apr 29 '20 at 13:47
0

Xcode 8.3.2 Swift 3.x. Using NSKeyedArchiver and NSKeyedUnarchiver

Reading file from documents

let documentsDirectoryPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let documentsDirectoryPath = NSURL(string: documentsDirectoryPathString)!
let jsonFilePath = documentsDirectoryPath.appendingPathComponent("Filename.json")

let fileManager = FileManager.default
var isDirectory: ObjCBool = false

if fileManager.fileExists(atPath: (jsonFilePath?.absoluteString)!, isDirectory: &isDirectory) {

let finalDataDict = NSKeyedUnarchiver.unarchiveObject(withFile: (jsonFilePath?.absoluteString)!) as! [String: Any]
}
else{
     print("File does not exists")
}

Write file to documents

NSKeyedArchiver.archiveRootObject(finalDataDict, toFile:(jsonFilePath?.absoluteString)!)
Prashant Gaikwad
  • 3,493
  • 1
  • 24
  • 26