Reputation: 23224
When I using NSOutputStream
's write
method
func write(_ buffer: UnsafePointer<UInt8>, maxLength length: Int) -> Int
I don't know how to convert String
to UnsafePointer<UInt8>
and length
How can I do this in swift?
Upvotes: 32
Views: 30731
Reputation: 3469
I've created a Swift extension that includes this feature among many others for generating pointers from Strings.
It includes a full test suite and it supports:
myString.stackPointer() -> UnsafePointer<Int8>?
myString.mutableStackPointer() -> UnsafeMutablePointer<Int8>?
myString.withUnsignedStackPointer { (ptr: UnsafePointer<UInt8>?) in
myString.withUnsignedMutableStackPointer { (ptr: UnsafeMutablePointer<UInt8>?) in
myString.heapPointer() -> UnsafePointer<Int8>?
myString.mutableHeapPointer() -> UnsafeMutablePointer<Int8>?
myString.unsignedHeapPointer() -> UnsafePointer<UInt8>?
myString.unsignedMutableHeapPointer() -> UnsafeMutablePointer<UInt8>?
https://gist.github.com/nathan-fiscaletti/892e074dc14e6707603414cd2d80c287
If you want to test it out you should be able to directly paste it into a Swift Playground.
Upvotes: 1
Reputation: 3591
By far the easiest way (in Swift 5):
let s = "hello, world"
let pointer = UnsafePointer(Array(s.utf8CString))
Not sure how backwards compatible this is.
Upvotes: 4
Reputation: 189
Swift 4,
Convert String to NSString, then use NSString's methods.
let text = "Hello"
let pointer: UnsafePointer<Int8>? = NSString(string: text).utf8String
let length = NSString(string: text).length
Upvotes: 9
Reputation: 1548
I see there are other answers, and an accepted answer, so it seems you've gotten what you need. I came here because I noticed Swift 5's deprecation warnings for withUnsafeMutableBytes
et al, and started testing @abdullahselek's answer, but I noticed in Swift 5 (haven't yet verified if it worked in previous versions) that String
is convertible to UnsafePointer<UInt8>
in-line, so you can use it in place where an UnsafePointer<UInt8>
is expected. In case it helps to see another example, here's our old and updated function, posted here:
OLD
let derivationStatus = localDerivedKeyData.withUnsafeMutableBytes { derivedKeyBytes in
salt.withUnsafeBytes { saltBytes in
CCKeyDerivationPBKDF(
CCPBKDFAlgorithm(kCCPBKDF2),
password,
passwordData.count,
saltBytes,
salt.count,
algorithm,
UInt32(rounds),
derivedKeyBytes,
derivedKeyData.count
)
}
}
NEW
let derivationStatus = localDerivedKeyData.withUnsafeMutableBytes { (outputBytes: UnsafeMutableRawBufferPointer) -> Int32 in
let status = CCKeyDerivationPBKDF(
CCPBKDFAlgorithm(kCCPBKDF2),
password, // a String
passwordData.count, // just the password String converted to Data
String(data: salt, encoding: .utf8), // converts salt (Data) to String
salt.count,
algorithm,
UInt32(rounds),
outputBytes.baseAddress?.assumingMemoryBound(to: UInt8.self),
derivedKeyData.count
)
return status
}
With that said, you could use a similar approach to get your stream as follows:
let stream = OutputStream(toBuffer: UnsafeMutablePointer(mutating: someString), capacity: someString.data(using: .utf8)!.count)
(the ! is used to silence the compiler error, but you should avoid force-unwrapping where possible).
Upvotes: 1
Reputation: 8435
Here is a string extension for Swift 5 that you can convert a string to UnsafePointer<UInt8>
and UnsafeMutablePointer<Int8>
extension String {
func toUnsafePointer() -> UnsafePointer<UInt8>? {
guard let data = self.data(using: .utf8) else {
return nil
}
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
let stream = OutputStream(toBuffer: buffer, capacity: data.count)
stream.open()
let value = data.withUnsafeBytes {
$0.baseAddress?.assumingMemoryBound(to: UInt8.self)
}
guard let val = value else {
return nil
}
stream.write(val, maxLength: data.count)
stream.close()
return UnsafePointer<UInt8>(buffer)
}
func toUnsafeMutablePointer() -> UnsafeMutablePointer<Int8>? {
return strdup(self)
}
}
To convert UnsafeMutablePointer<Int8>
to String
guard let mutablePointer = "test".toUnsafeMutablePointer() else {
return
}
let str = String(cString: mutablePointer)
Upvotes: 5
Reputation: 48075
Here is how to do it in Swift 3. Run fine in Swift 4 too
extension String {
func toPointer() -> UnsafePointer<UInt8>? {
guard let data = self.data(using: String.Encoding.utf8) else { return nil }
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
let stream = OutputStream(toBuffer: buffer, capacity: data.count)
stream.open()
data.withUnsafeBytes({ (p: UnsafePointer<UInt8>) -> Void in
stream.write(p, maxLength: data.count)
})
stream.close()
return UnsafePointer<UInt8>(buffer)
}
}
To convert from String
to UnsafeMutablePointer<Int8>
let cString = strdup("Hello") // UnsafeMutablePointer<Int8>
To convert from UnsafeMutablePointer<Int8>
to String
let string = String(cString: cString!) // String
Upvotes: 17
Reputation: 61
An answer for people working in Swift 4 now. You can no longer get bytes from a Data object, you have to copy them into an UnsafeMutablePointer
let helloWorld = "Hello World!"
let data = helloWorld.data(using: String.Encoding.utf8, allowLossyConversion: false)!
var dataMutablePointer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
//Copies the bytes to the Mutable Pointer
data.copyBytes(to: dataMutablePointer, count: data.count)
//Cast to regular UnsafePointer
let dataPointer = UnsafePointer<UInt8>(dataMutablePointer)
//Your stream
oStream.write(dataPointer, maxLength: data.count)
Upvotes: 6
Reputation: 539685
You have to convert the string to UTF-8 data first
let string = "foo bar"
let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
and then write it to the output stream
let outputStream: NSOutputStream = ... // the stream that you want to write to
let bytesWritten = outputStream.write(UnsafePointer(data.bytes), maxLength: data.length)
The UnsafePointer()
cast is necessary because data.bytes
has the type UnsafePointer<Void>
, and not UnsafePointer<UInt8>
as expected by the write()
method.
Update for Swift 3:
let string = "foo bar"
// Conversion to UTF-8 data (cannot fail):
let data = string.data(using: String.Encoding.utf8)!
// Write to output stream:
let outputStream: NSOutputStream = ... // the stream that you want to write to
let bytesWritten = data.withUnsafeBytes { outputStream.write($0, maxLength: data.count) }
Upvotes: 24
Reputation: 5597
You can also let Swift do it for you!
import Foundation
// Example function:
func printUTF8Vals(_ ptr: UnsafePointer<UInt8>, _ len: Int) {
for i in 0..<len {
print(ptr[i])
}
}
// Call it:
let str = "Hello"
printUTF8Vals(str, str.lengthOfBytes(using: String.Encoding.utf8))
// Prints:
// 72
// 101
// 108
// 108
// 111
Upvotes: 6