Read a text file line by line in Swift? Read a text file line by line in Swift? ios ios

Read a text file line by line in Swift?


Swift 3.0

if let path = Bundle.main.path(forResource: "TextFile", ofType: "txt") {    do {        let data = try String(contentsOfFile: path, encoding: .utf8)        let myStrings = data.components(separatedBy: .newlines)        TextView.text = myStrings.joined(separator: ", ")    } catch {        print(error)    }}

The variable myStrings should be each line of the data.

The code used is from:Reading file line by line in iOS SDK written in Obj-C and using NSString

Check edit history for previous versions of Swift.


Swift 5.2

The solution below shows how to read one line at a time. This is quite different from reading the entire contents into memory. Reading line-by-line scales well if you have a large file to read. Putting an entire file into memory does not scale well for large files.

The example below uses a while loop that quits when there are no more lines, but you can choose a different number of lines to read if you wish.

The code works as follows:

  1. create a URL that tells where the file is located
  2. make sure the file exists
  3. open the file for reading
  4. set up some initial variables for reading
  5. read each line using getLine()
  6. close the file when done

You could make the code less verbose if you wish; I have included comments to explain what the variables' purposes are.

Swift 5.2

import Cocoa// get URL to the the documents directory in the sandboxlet home = FileManager.default.homeDirectoryForCurrentUser// add a filenamelet fileUrl = home    .appendingPathComponent("Documents")    .appendingPathComponent("my_file")    .appendingPathExtension("txt")// make sure the file existsguard FileManager.default.fileExists(atPath: fileUrl.path) else {    preconditionFailure("file expected at \(fileUrl.absoluteString) is missing")}// open the file for reading// note: user should be prompted the first time to allow reading from this locationguard let filePointer:UnsafeMutablePointer<FILE> = fopen(fileUrl.path,"r") else {    preconditionFailure("Could not open file at \(fileUrl.absoluteString)")}// a pointer to a null-terminated, UTF-8 encoded sequence of bytesvar lineByteArrayPointer: UnsafeMutablePointer<CChar>? = nil// the smallest multiple of 16 that will fit the byte array for this linevar lineCap: Int = 0// initial iterationvar bytesRead = getline(&lineByteArrayPointer, &lineCap, filePointer)defer {    // remember to close the file when done    fclose(filePointer)}while (bytesRead > 0) {        // note: this translates the sequence of bytes to a string using UTF-8 interpretation    let lineAsString = String.init(cString:lineByteArrayPointer!)        // do whatever you need to do with this single line of text    // for debugging, can print it    print(lineAsString)        // updates number of bytes read, for the next iteration    bytesRead = getline(&lineByteArrayPointer, &lineCap, filePointer)}


This is not pretty, but I believe it works (on Swift 5). This uses the underlying POSIX getline command for iteration and file reading.

typealias LineState = (  // pointer to a C string representing a line  linePtr:UnsafeMutablePointer<CChar>?,  linecap:Int,  filePtr:UnsafeMutablePointer<FILE>?)/// Returns a sequence which iterates through all lines of the the file at the URL.////// - Parameter url: file URL of a file to read/// - Returns: a Sequence which lazily iterates through lines of the file////// - warning: the caller of this function **must** iterate through all lines of the file, since aborting iteration midway will leak memory and a file pointer/// - precondition: the file must be UTF8-encoded (which includes, ASCII-encoded)func lines(ofFile url:URL) -> UnfoldSequence<String,LineState>{  let initialState:LineState = (linePtr:nil, linecap:0, filePtr:fopen(fileURL.path,"r"))  return sequence(state: initialState, next: { (state) -> String? in    if getline(&state.linePtr, &state.linecap, state.filePtr) > 0,      let theLine = state.linePtr  {      return String.init(cString:theLine)    }    else {      if let actualLine = state.linePtr  { free(actualLine) }      fclose(state.filePtr)      return nil    }  })}

Here is how you might use it:

for line in lines(ofFile:myFileURL) {  print(line)}