This is primarily an educational class which is also fully functional and can be used to base your own implementation upon. It shows how to read a file decoupled from the GUI. I.e. the data processing happens at the end of the run loop when all GUI related events have been processed.
Though the data processing happens at the end of the runloop, if the process itself takes a long time it could still have an impact on the GUI experience by delaying the reaction to GUI events. If that is an issue the processing itself should be put into another thread. (This is not covered by this example)
Use the class by creating a child class that override the “processStreamBuffer” method.
The public interface to the StreamingFileReader is as follows:
class StreamingFileReader: NSObject, NSStreamDelegate {
// The path of the file to be read
let fileUrl: NSURL
// Size of the stream buffer, also the initial size of the first block of data that is attempted to be read from the file.
let streamBufferSize: Int
/// The buffer to be processed by processStreamBuffer when it is called.
var streamBuffer: Array<UInt8> = []
/// The listener will not receive callbacks until the startReadingFromFile returns 'true'.
init(fileUrl: NSURL, streamBufferSize: Int) {}
/// This method starts file reading, it returns true if reading started, false if not.
func startReading() -> Bool {}
/// Override this method to read from the streambuffer. Return true to keep reading, return false to abort.
///
/// :param: nofBytes The number of bytes that were read or error information: -2 if the startAtOffset value is too big, -1 if a stream error occured, 0 when no more bytes are available. If an error is indicated, this will be the last callback. Even if true is returned.
///
/// :param: startAtOffset The location from which the bytes are stored in the streambuffer, resp the location from which to start filling the streambuffer. In case the numOfBytes indicate an error, this parameter will always be zero. If this parameter is >= streamBufferSize (set during initialization) there will be an immedate callback with nofBytes = -2. Note that this parameter is never changed by the methods in StreamingFileReader (except in error cases). It is provided as a service to the child class so it can choose to implement partial processing of the stream buffer. If the child class always completely consumes all of the data in the stream buffer then either never change this variable or always set it to zero.
///
/// :returns: Return true if reading should continue, false if not.
func processStreamBuffer(nofBytes: Int, inout startAtOffset: Int) -> Bool { return false }
}
Typical use would be as follows:
class MyClass: StreamingFileReader {
let STREAM_BUFFER_SIZE = 4*1024
init(fileUrl: NSURL) {
super.init(fileUrl: fileUrl, streamBufferSize: STREAM_BUFFER_SIZE)
}
override func processStreamBuffer(nofBytes: Int, inout startAtOffset: Int) -> Bool {
switch nofBytes {
case -2: return errorEnd("Oeps, programming error, offset is too big")
case -1: return errorEnd("Error reading from file, see application log for details")
case 0: return false
case 1 ... STREAM_BUFFER_SIZE: return processData(nofBytes, offset: &startAtOffset)
default: return errorEnd("Oeps, programming error, illegal number of bytes read \(nofBytes)")
}
}
private func processData(nofBytes: Int, inout offset: Int) -> Bool {
...
}
private errorEnd(message: String) -> Bool {
...
return false
}
}