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 additionalUnsafePointer()
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:
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];
If
writeResult
is -1, you want to immediatelyreturn
.Right now you're dismissing the picker if
bytesWritten
is equal towriteResult
. That doesn't really make sense. I would take this dismissing of the picker out of thefor
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
Parameters After Opening Bracket
How to Convert Between Related Types Through a Common Initializer
Argument Labels Do Not Match Any Available Overloads
Swift Error: Missing Return in a Function Expected to Return 'String'
Autolayout Contraints for a View from Xib
Skphysicsbody Avoid Collision Swift/Spritekit
Does a Mutating Struct Function in Swift Create a New Copy of Self
Firestore Order by Time But Sort by Id
Initialize Lazy Instance Variable with Value That Depends on Other Instance Variables
Why am I Allowed Method Access Less Restrictive Than Class Access
How to Integrate Uiactivityviewcontroller with Swiftui's Scrollview
I Won't Be Able to Return a Value with Alamofire in Swift
Localizewithformat and Variadic Arguments in Swift
Is This a Good Way to Display Asynchronous Data
How to Write a Function That Will Unwrap a Generic Property in Swift Assuming It Is an Optional Type
Process Array in Parallel Using Gcd