Swift String to unsigned char [](CUnsignedChar) - ios

Hello i'm trying to use an old legacy C library that uses buffers (unsigned char []) to transform the data. The main issue here is that I couldn't find a way to transform a String to a CUnsignedChar and then be able to alloc that buffer to a UnsafeMutablePointer

If you want to convert a Swift string to an immutable C string to pass to a C function, try this:
let s: String = "hello, world!"
s.nulTerminatedUTF8.withUnsafeBufferPointer { p -> Void in
puts(UnsafePointer<Int8>(p.baseAddress))
Void()
}
You may need to use UnsafePointer<UInt8> if your function takes an unsigned char *.

I turn String to NSData first ,then turn NSData to CUnsignedChar array
if let data = str.dataUsingEncoding(NSUTF8StringEncoding){
var rawData = [CUnsignedChar](count: data.length,repeatedValue: 0)
data.getBytes(&rawData, length: data.length)
}

Related

Swift: convert const char ** output parameter to String

I'm interacting with a C++ library (with the header in C) which uses const char ** as an output parameter.
After executing a method in that library, the value I need is written in that variable, for example:
CustomMethod(const char **output)
CustomMethod(&output)
// Using the `output` here
Normally, in Swift it's possible to pass just a standard Swift String as a parameter and it will be transparently transformed into the const char * (Interacting with C Pointers - Swift Blog).
For example, I already use the following construct a lot with the same library:
// C
BasicMethod(const char *input)
// Swift
let string = "test"
BasicMethod(string)
However, when it comes to working with const char **, I couldn't just pass a pointer to the Swift String, as I'd expected:
// C
CustomMethod(const char **output)
// Swift
var output: String?
CustomMethod(&output)
Getting an error:
Cannot convert value of type 'UnsafeMutablePointer<String?>' to
expected argument type 'UnsafeMutablePointer<UnsafePointer?>'
(aka 'UnsafeMutablePointer<Optional<UnsafePointer>>')
The only way I could make it work is by manipulating the pointers directly:
// C
CustomMethod(const char **output)
// Swift
var output: UnsafePointer<CChar>?
CustomMethod(&output)
let stringValue = String(cString: json)
Is there any way to use the automatic Swift string to const char ** conversion, or does it only work with const char *?
The bridged C function expects a mutable pointer to a CChar pointer, so you'll need to provide one, there's no automatic bridging here.
var characters: UnsafePointer<CChar>?
withUnsafeMutablePointer(to: &characters) {
CustomMethod($0)
}
if let characters = characters {
let receivedString = String(cString: characters)
print(receivedString)
}
Same code, but in a more FP manner:
var characters: UnsafePointer<CChar>?
withUnsafeMutablePointer(to: &characters, CustomMethod)
var receivedString = characters.map(String.init)
print(receivedString)

Swift convert Data to UnsafeMutablePointer<Int8>

I'm calling a function in an objective c class from swift.
-(char *)decrypt:(char *)crypt el:(int)el{}
when calling this function from swift, it asks for an UnsafeMutablePointer<Int8> as the value for the parameter 'crypt'
the value for the 'crypt' is comming from a server and it is a base64encoded string. So I decode that string and got a Data object.
let resultData = Data(base64Encoded: base64String)
Now I need to pass this data to the above mentioned function. I have tried to convert this Data object to a UnsafeMutablePointer<Int8>
resultData?.withUnsafeBytes { (u8Ptr: UnsafeMutablePointer<Int8>) in
let decBytes = tea?.decrypt(u8Ptr , el: el)}
But it is not compiling. Gives below error
'UnsafeMutablePointer' is not convertible to 'UnsafePointer<_>'
I don't know much about objective c. So could anyone help me to pass this parameter to objective c function.
you have to change UnsafeMutablePointer to UnsafePointer
UnsafePointer
resultData?.withUnsafeBytes {(bytes: UnsafePointer<CChar>)->Void in
//Use `bytes` inside this closure
}
UnsafeMutablePointer
var data2 = Data(capacity: 1024)
data2.withUnsafeMutableBytes({ (bytes: UnsafeMutablePointer<UInt8>) -> Void in
//Use `bytes` inside this closure
})
Edit, updated my answer for two things:
Not returning the pointer from withUnsafeBytes
Accounting for Swift 5' deprecation warning: 'withUnsafeBytes' is deprecated: use withUnsafeBytes<R>(_: (UnsafeRawBufferPointer) throws -> R) rethrows -> R instead
// buffer pointer captured is converted to byte pointer which is used in the block to decode the base64 encoded Data
encodedStringData.withUnsafeMutableBytes { (rawBufferPtr: UnsafeMutableRawBufferPointer) in
if let rawPtr = rawBufferPtr.baseAddress {
let decodedString = String(bytesNoCopy: rawPtr, length: rawBufferPtr.count, encoding: .utf8, freeWhenDone: false)
print(decodedString!)
}
}
someData.withUnsafeBytes { (bufferRawPtr: UnsafeRawBufferPointer) in
// For converting an UnsafeRawBufferPointer to its typed variant, in this case UnsafeBufferPointer<UInt8>
let bufferTypedPtr = bufferRawPtr.bindMemory(to: UInt8.self)
// Then, getting the typed UnsafePointer, in this case UnsafePointer<UInt8>
let unsafePointer = bufferTypedPtr.baseAddress!
}
Note: Swift 5 doesn't allow you to access encodedStringData from within the withUnsafeMutableBytes block! Read Swift 5 Exclusivity Enforcement for why.
Capturing the pointer outside of the block is apparently not recommended, it works but the behavior can get to be undefined in the future
Old answer:
This will help someone looking for getting to the underlying raw bytes (in a UnsafeMutablePointer<UInt8> representation) of a Data object as a variable for further use (instead of having to write all of the logic in the withUnsafeMutableBytes block).
var encodedStringData = Data(base64Encoded: "Vmlub2QgaXMgZ3JlYXQh")!
// byte pointer variable used later to decode the base64 encoded Data
let rawPtr: UnsafeMutablePointer<UInt8> = encodedStringData.withUnsafeMutableBytes { (bytePtr: UnsafeMutablePointer<UInt8>) in bytePtr }
let decodedString = String(bytesNoCopy: rawPtr, length: encodedStringData.count, encoding: .utf8, freeWhenDone: false)
print(decodedString, encodedStringData)
Solution using NSData
let data = NSData(bytes: arrayOfUInt8, length: arrayOfUInt8.count)
let pointer: UnsafeMutablePointer<Int8> = data.bytes.assumingMemoryBound(to: UInt8.self)

How to copy [UInt8] array to C pointer in Swift 3?

I'm trying to copy Swift [UInt8] buffer to a C pointer. I can't find the right solution, here is my code:
uploadBodyBytes = [UInt8]()
...
...
var data = crl.uploadBodyBytes[crl.bodyBytesUploaded..<crl.bodyBytesUploaded+actualLen]
_ = data.withUnsafeBytes({ (rawData /*provides UnsafeRawBufferPointer*/) -> UnsafeMutableRawPointer in
return memcpy(a, rawData /*expected UnsafeRawPointer*/, actualLen)
})
data.withUnsafeBytes gives me UnsafeRawBufferPointer but that seems to be incompatible with memcpy which expects UnsafeRawPointer.
Help appreciated.
You can access it with the baseAddress property, but you don't need to.

Swift String from imported unsigned char 2D array

I am using a 3rd party C library in my iOS application, which I am in the process of converting from Objective-C to Swift. I hit an obstacle when attempting to read one of the structs returned by the C library in Swift.
The struct looks similar to this:
typedef unsigned int LibUint;
typedef unsigned char LibUint8;
typedef struct RequestConfiguration_ {
LibUint8 names[30][128];
LibUint numberNames;
LibUint currentName;
} RequestConfiguration;
Which is imported into Swift as a Tuple containing 30 Tuples of 128 LibUint8 values. After a long time of trial and error using nested withUnsafePointer calls, I eventually began searching for solutions to iterating a Tuple in Swift.
What I ended up using is the following functions:
/**
* Perform iterator on every children of the type using reflection
*/
func iterateChildren<T>(reflectable: T, #noescape iterator: (String?, Any) -> Void) {
let mirror = Mirror(reflecting: reflectable)
for i in mirror.children {
iterator(i.label, i.value)
}
}
/**
* Returns a String containing the characters within the Tuple
*/
func libUint8TupleToString<T>(tuple: T) -> String {
var result = [CChar]()
let mirror = Mirror(reflecting: tuple)
for child in mirror.children {
let char = CChar(child.value as! LibUint8)
result.append(char)
// Null reached, skip the rest.
if char == 0 {
break;
}
}
// Always null terminate; faster than checking if last is null.
result.append(CChar(0))
return String.fromCString(result) ?? ""
}
/**
* Returns an array of Strings by decoding characters within the Tuple
*/
func libUint8StringsInTuple<T>(tuple: T, length: Int = 0) -> [String] {
var idx = 0
var strings = [String]()
iterateChildren(tuple) { (label, value) in
guard length > 0 && idx < length else { return }
let str = libUint8TupleToString(value)
strings.append(str)
idx++
}
return strings
}
Usage
func handleConfiguration(config: RequestConfiguration) {
// Declaration types are added for clarity
let names: [String] = libUint8StringsInTuple(config.names, config.numberNames)
let currentName: String = names[config.currentName]
}
My solution uses reflection to iterate the first Tuple, and reflection to iterate the second, because I was getting incorrect strings when using withUnsafePointer for the nested Tuples, which I assume is due to signage. Surely there must be a way to read the C strings in the array, using an UnsafePointer alike withUsafePointer(&struct.cstring) { String.fromCString(UnsafePointer($0)) }.
To be clear, I'm looking for the fastest way to read these C strings in Swift, even if that involves using Reflection.
Here is a possible solution:
func handleConfiguration(var config: RequestConfiguration) {
let numStrings = Int(config.numberNames)
let lenStrings = sizeofValue(config.names.0)
let names = (0 ..< numStrings).map { idx in
withUnsafePointer(&config.names) {
String.fromCString(UnsafePointer<CChar>($0) + idx * lenStrings) ?? ""
}
}
let currentName = names[Int(config.currentName)]
print(names, currentName)
}
It uses the fact that
LibUint8 names[30][128];
are 30*128 contiguous bytes in memory. withUnsafePointer(&config.names)
calls the closure with $0 as a pointer to the start of that
memory location, and
UnsafePointer<CChar>($0) + idx * lenStrings
is a pointer to the start of the idx-th subarray. The above code requires
that each subarray contains a NUL-terminated UTF-8 string.
The solution suggested by Martin R looks good to me and, as far as I can see from my limited testing, does work. However, as Martin pointed out, it requires that the strings be NUL-terminated UTF-8. Here are two more possible approaches. These follow the principle of handling the complexity of C data structures in C instead of dealing with it in Swift. Which of these approaches you choose depends on what specifically you are doing with RequestConfiguration in your app. If you are not comfortable programming in C, then a pure Swift approach, like the one suggested by Martin, might be a better choice.
For the purposes of this discussion, we will assume that the 3rd party C library has the following function for retrieving RequestConfiguration:
const RequestConfiguration * getConfig();
Approach 1: Make the RequestConfiguration object available to your Swift code, but extract names from it using the following C helper function:
const unsigned char * getNameFromConfig(const RequestConfiguration * rc, unsigned int nameIdx)
{
return rc->names[nameIdx];
}
Both this function's signature and the RequestConfiguration type must be available to the Swift code via the bridging header. You can then do something like this in Swift:
var cfg : UnsafePointer<RequestConfiguration> = getConfig()
if let s = String.fromCString(UnsafePointer<CChar>(getNameFromConfig(cfg, cfg.memory.currentName)))
{
print(s)
}
This approach is nice if you need the RequestConfiguration object available to Swift in order to check the number of names in multiple places, for example.
Approach 2: You just need to be able to get the name at a given position. In this case the RequestConfiguration type does not even need to be visible to Swift. You can write a helper C function like this:
const unsigned char * getNameFromConfig1(unsigned int idx)
{
const RequestConfiguration * p = getConfig();
return p->names[idx];
}
and use it in Swift as follows:
if let s = String.fromCString(UnsafePointer<CChar>(getNameFromConfig1(2)))
{
print(s)
}
This will print the name at position 2 (counting from 0). Of course, with this approach you might also want to have C helpers that return the count of names as well as the current name index.
Again, with these 2 approaches it is assumed the strings are NUL-terminated UTF-8. There are other approaches possible, these are just examples.
Also please note that the above assumes that you access RequestConfiguration as read-only. If you also want to modify it and make the changes visible to the 3rd party library C code, then it's a different ballgame.

How do I convert an NSString to an integer using Swift?

I need to convert an NSString to an integer in Swift: Here's the current code I'm using; it doesn't work:
var variable = (NSString(data:data, encoding:NSUTF8StringEncoding))
exampeStruct.otherVariable = (variable).intValue
Variable is a normal varable, and exampleStruct is a struct elsewhere in the code with a subvariable otherVariable.
I expect it to set exampleStruct.otherVariable to an int value of the NSString, but I get the following error:
"Cannot convert the expression's type () to type Float"
How do I convert an NSString to int in Swift?
It looks to me like the problem might not be the Int conversion, but rather that the exampleStruct is expecting a Float.
If that's not the issue however (and granted, Xcode errors for Swift often seem to be more about the line number rather than about the actual problem) then something like this should work for you?
var ns:NSString = "1234"
if let i = (ns as String).toInt() {
exampleStruct.otherVariable = i
}
I know you already got your answer, but I just want to explain what (I think) might not be trivial
First, we have some NSData we want to convert to NSString, because no one guaranties the data is a valid UTF8 buffer, it return an optional
var variable = NSString(data:data, encoding:NSUTF8StringEncoding)
Which means variable: NSString?
Usually NSString is bridged to swift's String, but in this case, we use an NSString constructor - you can think about it more as a "Foundation"-like syntax that wasn't directly imported to swift (as there's no bridge for NSData)
we can still use the 'Foundation' way with NSString
if let unwrappedVariable = variable {
var number = unwrappedVariable.intValue
}
if number is a Float, but the string is a string representation of an integer
if let unwrappedVariable = variable {
var number: Float = Float(unwrappedVariable.intValue)
}
if both number and the string (representation of) are floats:
if let unwrappedVariable = variable {
var number:Float = unwrappedVariable.floatValue
}
Anyway, there's a small problem with using Foundation. For these types of conversions it has no concept of an optional value (for int, float). It will return 0 if it cannot parse a string as and integer or float. That's why it's better to use swift native String:
if let variable: String = NSString(data: data, encoding: NSUTF8StringEncoding) {
if let integer = variable.toInt() {
var integerNumber = integer
var floatNumber = Float(integer)
}
}
edit/update:
No need to use NSString when coding with Swift. You can use Swift native String(data:) initializer and then convert the string to Int:
if let variable = String(data: data, encoding: .utf8),
let integer = Int(variable) {
exampeStruct.otherVariable = integer
}
If other variable is a Float type:
if let variable = String(data: data, encoding: .utf8),
let integer = Float(variable) {
exampeStruct.otherVariable = integer
}

Resources