16

I am trying to append a string into text file. I am using the following code.

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

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

    //reading
    let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
    println(text2) //prints some text
}

this does not append the string to file. Even if I call this function repeatedly.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Atif Farrukh
  • 2,219
  • 2
  • 25
  • 47

5 Answers5

28

If you want to be able to control whether to append or not, consider using OutputStream. For example:

do {
    let fileURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
        .appendingPathComponent("votes.txt")
    
    guard let outputStream = OutputStream(url: fileURL, append: true) else {
        print("Unable to open file")
        return
    }

    outputStream.open()
    let text = "some text\n"
    try outputStream.write(text)
    outputStream.close()
} catch {
    print(error)
}

By the way, this is an extension that lets you easily write a String (or Data) to an OutputStream:

extension OutputStream {
    enum OutputStreamError: Error {
        case stringConversionFailure
        case bufferFailure
        case writeFailure
    }

    /// Write `String` to `OutputStream`
    ///
    /// - parameter string:                The `String` to write.
    /// - parameter encoding:              The `String.Encoding` to use when writing the string. This will default to `.utf8`.
    /// - parameter allowLossyConversion:  Whether to permit lossy conversion when writing the string. Defaults to `false`.

    func write(_ string: String, encoding: String.Encoding = .utf8, allowLossyConversion: Bool = false) throws {
        guard let data = string.data(using: encoding, allowLossyConversion: allowLossyConversion) else {
            throw OutputStreamError.stringConversionFailure
        }
        try write(data)
    }

    /// Write `Data` to `OutputStream`
    ///
    /// - parameter data:                  The `Data` to write.

    func write(_ data: Data) throws {
        try data.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) throws in
            guard var pointer = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
                throw OutputStreamError.bufferFailure
            }

            var bytesRemaining = buffer.count

            while bytesRemaining > 0 {
                let bytesWritten = write(pointer, maxLength: bytesRemaining)
                if bytesWritten < 0 {
                    throw OutputStreamError.writeFailure
                }

                bytesRemaining -= bytesWritten
                pointer += bytesWritten
            }
        }
    }
}

For Swift 2 rendition, see previous revision of this answer.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • when I try to read the file it gives nil using `let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil) println(text2)` – Atif Farrukh Nov 18 '14 at 11:10
  • Did you definitely `open` the `NSOutputStream` before trying to `write` to it? The `write` will fail if you don't. Also, did you definitely `close` the `NSOutputStream` before trying to read the file in again? You have to close the file before you try to use it again. If you're still having problems, try checking the return value of the `write` function and see if that succeeded or not. – Rob Nov 18 '14 at 11:12
  • the `write` function ask me to enter `maxLength`. what should I put there? – Atif Farrukh Nov 18 '14 at 11:14
  • yes I have opened and closed the `NSOutputStream ` before writing and reading respectively – Atif Farrukh Nov 18 '14 at 11:15
  • You shouldn't have a `maxLength` parameter. That means you're calling the wrong rendition of `write` function. Did you include the `extension` I supplied in my answer? – Rob Nov 18 '14 at 11:24
  • Yes I was using the wrong one, now included your extension, but still getting `nil` – Atif Farrukh Nov 18 '14 at 11:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/65136/discussion-between-rob-and-atif-farrukh). – Rob Nov 18 '14 at 11:30
  • OMG, need swift 2 version of this!!1 – Quadrivium Oct 19 '15 at 20:10
  • @Quadrivium - The extension is Swift 2 ready. The only thing that's different is the process of building the path. See revised answer. – Rob Oct 19 '15 at 20:59
  • I was using a filename format string with '/' in it... oops change them to '-' – Quadrivium Oct 19 '15 at 21:22
  • Can anyone update for Swift 3? This was a useful extension – JohnV Oct 04 '16 at 22:42
  • @JohnV - Added Swift 3 rendition. – Rob Oct 04 '16 at 23:06
  • maybe some idea how to apply this code with swift 3 ? – Sirop4ik Jan 24 '17 at 13:25
  • The code in his answer _is_ Swift 3. I added Swift 2 rendition at the end for 2.3 users, but the answer is otherwise Swift 3. – Rob Jan 24 '17 at 16:44
4

You can also use FileHandle to append String to your text file. If you just want to append your string the end of your text file just call seekToEndOfFile method, write your string data and just close it when you are done:


FileHandle usage Swift 3 or Later

let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

// create a new text file at your documents directory or use an existing text file resource url
let fileURL = documentsDirectory.appendingPathComponent("simpleText.txt")
do {
    try Data("Hello World\n".utf8).write(to: fileURL)
} catch {
    print(error) 
}
// open your text file and set the file pointer at the end of it
do {
    let fileHandle = try FileHandle(forWritingTo: fileURL)
    fileHandle.seekToEndOfFile()
    // convert your string to data or load it from another resource
    let str = "Line 1\nLine 2\n"
    let textData = Data(str.utf8)
    // append your text to your text file
    fileHandle.write(textData)
    // close it when done
    fileHandle.closeFile()
    // testing/reading the file edited
    if let text = try? String(contentsOf: fileURL, encoding: .utf8) {
        print(text)  // "Hello World\nLine 1\nLine 2\n\n"
    }
} catch {
    print(error)
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
2

Please check the below code as its working for me. Just Add the code as it is:

let theDocumetFolderSavingFiles = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let filePath = "/theUserData.txt"
let thePathToFile = theDocumetFolderSavingFiles.stringByAppendingString(filePath)
let theFileManager = NSFileManager.defaultManager()

if(theFileManager.fileExistsAtPath(thePathToFile)){

        do {

            let stringToStore = "Hello working fine"
            try stringToStore.writeToFile(thePathToFile, atomically: true, encoding: NSUTF8StringEncoding)

        }catch let error as NSError {
            print("we are geting exception\(error.domain)")
        }

        do{
            let fetchResult = try NSString(contentsOfFile: thePathToFile, encoding: NSUTF8StringEncoding)
            print("The Result is:-- \(fetchResult)")
        }catch let errorFound as NSError{
            print("\(errorFound)")
        }

    }else
    {
        // Code to Delete file if existing
        do{
            try theFileManager.removeItemAtPath(thePathToFile)
        }catch let erorFound as NSError{
            print(erorFound)
        }
    }
bourvill
  • 152
  • 1
  • 13
sharma_kunal
  • 2,152
  • 1
  • 28
  • 28
2

A simple solution that works for me. UPDATE, it looks like I must have gotten this from here, so credit where credit is due: Append text or data to text file in Swift

Usage:

"Hello, world".appendToURL(fileURL: url)

Code:

extension String {
    func appendToURL(fileURL: URL) throws {
        let data = self.data(using: String.Encoding.utf8)!
        try data.append(fileURL: fileURL)
    }
}

extension Data {
    func append(fileURL: URL) throws {
        if let fileHandle = FileHandle(forWritingAtPath: fileURL.path) {
            defer {
                fileHandle.closeFile()
            }
            fileHandle.seekToEndOfFile()
            fileHandle.write(self)
        }
        else {
            try write(to: fileURL, options: .atomic)
        }
    }
}    
biomiker
  • 3,108
  • 1
  • 29
  • 26
1

Check the reading part.

The method cotentsOfFile: is a method of NSString class. And you have use it wrong way.

So replace this line

let text2 = String(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)

Here you have to use NSString instead of String class.

let text2 = NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding, error: nil)
Zoe
  • 27,060
  • 21
  • 118
  • 148
Kampai
  • 22,848
  • 21
  • 95
  • 95