Make InputStream and OutputStream methods safe and Swifty

Hi,

Currently, We must pass unsafe pointers to InputStream.read() and OutputStream.write() methods, which is not suitable when calling them from Swift.

Personally I use these convenience methods which wrap them to make Streams accessible in Swift.

extension InputStream {
    func readData(maxLength length: Int) throws -> Data {
        var buffer = [UInt8](repeating: 0, count: length)
        let result = self.read(&buffer, maxLength: buffer.count)
        if result < 0 {
            throw self.streamError ?? POSIXError(.EIO)
        } else {
            return Data(buffer.prefix(result))
        }
    }
}

extension OutputStream {
    func write<DataType: DataProtocol>(_ data: DataType) throws -> Int {
        var buffer = Array(data)
        let result = self.write(&buffer, maxLength: buffer.count)
        if result < 0 {
            throw self.streamError ?? POSIXError(.EIO)
        } else {
            return result
        }
    }
}
2 Likes

I think there are a couple of challenges with InputStream API. One is clearly this, which I think actually could use some of the newer Data methods we've added (@itaiferber?). Also, these are inherently tied to run loop, which can surprise people.

Another challenge is that InputStream and OutputStream are class cluster types, and have those unsafe pointer APIs as their base functionality for subclasses to implement. I don't think we have a great way to fix those without deprecating the entire class hierarchy. Formatter has a similar problem since it's required function is an error String instead of Error (it predates NSError).

1 Like

Indeed I intended to just show a demo. Of course it is open for optimization.

Thus in this case we should declare these helper functions as final?

byte buffers are easy to deal with in C, but a little odd in Swift. I only propose to make Swift codes much more readable when dealing with these two frequently used methods of Stream class.

I find myself needing this right now. Did anything come of it? Might make sense as a Data initializer:

let data = Data(from: stream)