Writing Data to an Nsoutputstream in Swift 3

Writing Data to an NSOutputStream in Swift 3

NSData had a bytes property to access the bytes.
The new Data value type in Swift 3 has a withUnsafeBytes()
method instead, which calls a closure with a pointer to the bytes.

So this is how you write Data to an NSOutputStream
(without casting to NSData):

let data = ... // a Data value
let bytesWritten = data.withUnsafeBytes { outputStream.write($0, maxLength: data.count) }

Remarks:
withUnsafeBytes() is a generic method:

/// Access the bytes in the data.
///
/// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.
public func withUnsafeBytes<ResultType, ContentType>(_ body: @noescape (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType

In the above call,
both ContentType and ResultType are automatically inferred by
the compiler (as UInt8 and Int), making additional
UnsafePointer() conversions unnecessary.

outputStream.write() returns the number of bytes actually written.
Generally, you should check that value. It can be -1 if
the write operation failed, or less than data.count when writing
to sockets, pipes, or other objects with a flow control.

Writing a String to an NSOutputStream in Swift

There are two issues here. The first is that you're passing data to outputStream.write() and not data.bytes (like you passed [data bytes] in your Objective-C code). The second issue is that data.bytes returns an UnsafePointer<Void>, but NSOutputStream.write() takes an UnsafePointer<UInt8>. Luckily, UnsafePointer has a way to convert between types:

/// Convert from a UnsafePointer of a different type.
///
/// This is a fundamentally unsafe conversion.
init<U>(_ from: UnsafePointer<U>)

Putting those things together makes your code look something like this:

let data: NSData = mystring.dataUsingEncoding(NSUTF8StringEncoding)!
outputStream.write(UnsafePointer<UInt8>(data.bytes), maxLength: data.length)

Use NSOutputstream and NSInputstream to write and read every interval

Looks like NSInputStream and NSOutputStream are not supposed to be re-used after a stream finishes. I fix the problem by changing the code to below:

func start(ipAddress ipAddress: String) {
self.conn = Connection(serverAddress: ipAddress)
self.sendTimer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "getTimeAndUpdateUI", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(self.sendTimer!, forMode: NSDefaultRunLoopMode)
}

func getTimeAndUpdateUI() {
self.conn!.connect()
let time = self.conn!.receive()
self.timeLabel.text = "Current time is \(time)"
self.conn!.disconnect()
}

Every time iOS poll the server for information, it recreates an instance of NSInputStream and NSOutputStream.

Any suggestion to improve is welcome. Thanks.

(NS)StreamDelegate - no error when writing to closed

You should always check the return value from write(), which is the number of bytes written to the stream, or -1 on error:

self.outputStream.close()
let bytesWritten = self.outputStream.write(newData, maxLength: newData.count)
if bytesWritten < 0 {
print("failed:", outputStream.streamError?.localizedDescription ?? "Unknown error")
}

Writing to the output stream after it has been closed is an error, and will be detected by that check.

The stream's streamError can provide additional information
in the case of an error, however it is nil in this particular situation.

How to send image data in NSOutputStream with large data

Your write routine has a number of issues:

  1. Where you have

    writeResult += [self.outputStream write:[completeData bytes]+bytesWritten maxLength:[completeData length]-bytesWritten];

    You, instead want:

    writeResult = [self.outputStream write:[completeData bytes]+bytesWritten maxLength:[completeData length]-bytesWritten];
  2. If writeResult is -1, you want to immediately return.

  3. Right now you're dismissing the picker if bytesWritten is equal to writeResult. That doesn't really make sense. I would take this dismissing of the picker out of the for loop altogether and make sure you dismiss it regardless.


As an aside, rather than getting a low resolution JPEG representation, I'd personally suggest retrieving the data for the asset directly. See https://stackoverflow.com/a/25322412/1271826.

This ensures that you have no loss of quality and/or change of file size. It is also far more efficient use of memory.

How to write Int32 to NSOutputStream in Swift

Your last approach is almost correct. value needs to be a variable parameter
so that you can use it as "in-out expression" &value, and data.bytes needs a cast:

func write4ByteField(var value: Int32) {
let data = NSData(bytes: &value, length: sizeof(Int32))
stream.write(UnsafePointer(data.bytes), maxLength: sizeof(Int32))
}

It can also be done without NSData, including the conversion to big-endian
byte order:

func write4ByteField(value: Int32) {
var valueBE = value.bigEndian
withUnsafePointer(&valueBE) {
self.stream.write(UnsafePointer($0), maxLength: sizeofValue(valueBE))
}
}

Writes to NSOutputStream after performing work on background thread don't work

In case anyone ever stumbles up on this and just happens to see the same thing I saw (and think the same thing is the problem):

The problem wasn't that the threading was causing issues - just writing that post made me realize how absurd I was being. The problem appears to have been that the underlying TCP sockets were timing out after waiting for so long to receive data. I solved the problem by sending out a heartbeat message once a second, which keeps the sockets alive until I'm ready to actually start sending data.

Error ( '()' is not identical to 'UInt8' ) writing NSData bytes to NSOutputStream using the write function in Swift

You can cast the pointer with UnsafePointer():

bytesWritten = self.downloadStream.write(UnsafePointer(data.bytes), maxLength: bytesLeftToWrite)

There is also a problem in your write loop, because you always write the
initial bytes of the data object to the output stream.

It should probably look similar to this (untested):

var bytes = UnsafePointer<UInt8>(data.bytes)
var bytesLeftToWrite: NSInteger = data.length

while bytesLeftToWrite > 0 {
let bytesWritten = self.downloadStream.write(bytes, maxLength: bytesLeftToWrite)
if bytesWritten == -1 {
break // Some error occurred ...
}

bytesLeftToWrite -= bytesWritten
bytes += bytesWritten // advance pointer

// ...
}


Related Topics



Leave a reply



Submit