Writing Data to an OutputStream with Swift 5+
The trick is to use bindMemory
function:
func write(_ data: Data) -> Int {
return data.withUnsafeBytes({ (rawBufferPointer: UnsafeRawBufferPointer) -> Int in
let bufferPointer = rawBufferPointer.bindMemory(to: UInt8.self)
return self.write(bufferPointer.baseAddress!, maxLength: data.count)
})
}
While this works with Swift 5.0, there are apparently some issues; see a related forum discussion.
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.
How to check for success when writing with OutputStream?
Assuming you know that the sendMessage(_:)
method is getting called and all you're looking to do is check whether or not the write was successful, this can be done in a straight forward way by looking at the return value of the write method (documentation) Here's a quick example.
@IBAction func sendMessage(sender: AnyObject!) {
guard let outputStream = outputStream else {
print("Connection not create yet ! =====> Return")
return
}
guard let text = textfield?.text,
data: NSData = text.data(using: String.Encoding.utf8) else {
print("no data")
return
}
print("\(outputStream) ==> Pass Data : \(text)")
outputStream.open()
defer {
outputStream.close()
}
let result = outputStream.write(UnsafePointer<UInt8>(data.bytes), maxLength: data.length)
if result == 0 {
print("Stream at capacity")
} else if result == -1 {
print("Operation failed: \(outputStream.streamError)")
} else {
print("The number of bytes written is \(result)")
}
}
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)
How to send NSData over an OutputStream
Pretty sure you want to replace this:
let length = self.inputStream!.read(&buffer, maxLength: buffer.count)
let data = NSData.init(bytes: buffer, length: buffer.count)
With
let length = self.inputStream!.read(&buffer, maxLength: buffer.count)
let data = NSData.init(bytes: buffer, length: length)
Also, I am not 100% sure that the random blocks of data will always be ok to use to make the audio buffers. You might need to collect up the data first into a bigger block of NSData.
Right now, since you always pass in blocks of 8192 (even if you read less), the buffer creation probably always succeeds. It might not now.
Sending bytes on outputstream with Swift
I don't see anything wrong with your code but the conversion to Data
is not needed:
bytes.withUnsafeBytes {
guard let pointer = $0.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return
}
outputStream.write(pointer, maxLength: bytes.count)
}
since [UInt8]
conforms to ContiguousBytes
.
Actually, the following is also possible:
bytes.withUnsafeBufferPointer {
guard let baseAddress = $0.baseAddress else { return }
outputStream.write(baseAddress, maxLength: bytes.count)
}
Related Topics
How to Prompt for Accessibility Features in a MACos App (From the Appdelegate)
Guard Let Error: Initializer for Conditional Binding Must Have Optional Type Not 'String'
Swift Lazy Stored Property Versus Regular Stored Property When Using Closure
How to Wait for Http Requests to Finish
How to Place Uisearchcontroller to the Navigationtitle and How to Enable and Disable It by Button
Comparing Non-Optional Any to Nil Is Always False
For-In Loops Multiple Conditions
How to Initialize a Mlmultiarray in Coreml
Opening Import File for Module 'Swift': Not a Directory
Swift: Get 30 Days Before 'Specific Date'
Cannot Call Value of Non-Function Type 'Ciimage'
Enum's Rawvalue Property Not Recognized
Swiftui View Property Willset & Didset Property Observers Not Working
Inline Kvo of a Property in Another View Controller
Why Upload Alamofire Background Request Don't Executes in Background