Quinn, Jens, thanks for pointing me at the NSStream classes. For some reason, I discarded NSStream quickly after seeing the first paragraph of Apple's documentation on output streams <https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Streams/Articles/WritingOutputStreams.html> listing 5 relatively complex steps — without realizing that this API also has a synchronous interface.
Obviously, the NSStream API is too low level and thus difficult to use. Therefore, I would also like to see Apple build a great IO streaming API for Swift. I've implemented Quinn's example using NSInputStream and Swift's Generator design pattern. Except for mapping NSInputStream to a generator — which is really messy — everything else is straightforward and much more extensible and scalable than the standard Foundation approach. The code is below. == Matthias See https://gist.github.com/objecthub/ada1f852924b2c253653d6949fd3555d class ByteGenerator: GeneratorType { typealias Element = UInt8 let input: NSInputStream var buffer: [UInt8] var index: Int = 0 var eof: Bool = true init?(path: String, capacity: Int = 1024) { guard let input = NSInputStream(fileAtPath: path) else { return nil } self.buffer = [UInt8](count: capacity, repeatedValue: 0) input.open() if input.hasBytesAvailable { self.eof = input.read(&self.buffer, maxLength: self.buffer.count * sizeof(UInt8)) <= 0 } self.input = input } deinit { input.close() } func next() -> UInt8? { guard !self.eof else { return nil } if self.index >= self.buffer.count { self.index = 0 self.eof = !input.hasBytesAvailable || input.read(&self.buffer, maxLength: self.buffer.count * sizeof(UInt8)) <= 0 guard !self.eof else { return nil } } self.index += 1 return self.buffer[self.index - 1] } } struct CharacterGenerator<G: GeneratorType, U: UnicodeCodecType where G.Element == U.CodeUnit>: GeneratorType { typealias Element = Character var source: G var decoder: U mutating func next() -> Character? { guard case .Result(let scalar) = self.decoder.decode(&self.source) else { return nil } return Character(scalar) } } struct LineGenerator<G: GeneratorType where G.Element == Character>: GeneratorType { typealias Element = String var source: G mutating func next() -> String? { guard let fst = source.next() else { return nil } guard fst != "\n" else { return "" } var line = String(fst) while let ch = source.next() { if (ch == "\n") { return line } line.append(ch) } return line } } if let input = ByteGenerator(path: "/Users/username/filename.txt") { var generator = LineGenerator(source: CharacterGenerator(source: input, decoder: UTF8())) var i = 0 while let line = generator.next() { print("\(i): \(line)") i += 1 } } *Matthias Zenger* matth...@objecthub.net On Sun, Jun 5, 2016 at 8:36 PM, Jens Alfke <j...@mooseyard.com> wrote: > > On Jun 4, 2016, at 6:25 PM, Matthias Zenger via swift-users < > swift-users@swift.org> wrote: > > I wanted to use NSFileHandle (for a use case that requires streaming) and > realized that this is an API that cannot really be used in Swift because > it's based on Objective-C exceptions. > > > You’re right, NSFileHandle is a very archaic class (kind of a coelacanth) > and its I/O methods signal errors by throwing exceptions. It’s almost > unique in that regard; in general Cocoa APIs are only supposed to throw > exceptions for programmer errors like assertion failures. > > Are there any alternatives? > > > NSStream. > > —Jens >
_______________________________________________ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users