Swift convert Data to UnsafeMutablePointer<Int8> - ios

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)

Related

Encode String/NSString to Code Page 850 Format

I need to encode a regular string (String or NSString) to a Code Page 850 format.
There's an external String enconding who supports this format (It's called dosLatin1 in the CFStringEncoding enum). I don't know if it's can really do the work, but it's the only reference that I found to Code Page 850 in the whole iOS documentation.
How can I use the CFStringEnconding to convert a "regular" string to a string at a CP850 format? Is it the best way to do it?
If you can get by with CP 1252 which is the "modern" replacement for 850, then you can use Swift String's built in conversion. Otherwise, you can try using Core Foundation's conversion method.
let swiftString = "This is a string"
// Easy way -- use Swift String plus "modern" CP 1252 eoncding to get a block of data. Note: does not include BOM
if let data = swiftString.data(using: .windowsCP1252, allowLossyConversion: true) {
print(data) // Do something with the resulting data
}
// The more thorough way to use CP 850 (enum value = 1040) -- use Core Foundation. This will produce a BOM if necessary.
let coreFoundationString = swiftString as CFString
let count = CFStringGetLength(coreFoundationString) * 2
var buffer = [UInt8](repeating: 0, count: count)
let resultCount = CFStringGetBytes(coreFoundationString as CFString, CFRangeMake(0, CFStringGetLength(coreFoundationString)), 1040, 0x41, true, &buffer, count, nil)
print(buffer)

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.

Casting UnsafeMutablePointers to UnsafeMutableRawPointers

I have a couple of issues updating to swift 3.0. I have the following code:
// Retrieve the Device GUID
let device = UIDevice.current
let uuid = device.identifierForVendor
let mutableData = NSMutableData(length: 16)
(uuid! as NSUUID).getBytes(UnsafeMutablePointer(mutableData!.mutableBytes))
// Verify the hash
var hash = Array<UInt8>(repeating: 0, count: 20)
var ctx = SHA_CTX()
SHA1_Init(&ctx)
SHA1_Update(&ctx, mutableData!.bytes, mutableData!.length)
SHA1_Update(&ctx, (opaqueData1! as NSData).bytes, opaqueData1!.count)
SHA1_Update(&ctx, (bundleIdData1! as NSData).bytes, bundleIdData1!.count)
SHA1_Final(&hash, &ctx)
let computedHashData1 = Data(bytes: UnsafePointer(&hash), count: 20)
My first issue is with the line of code:
(uuid! as NSUUID).getBytes(UnsafeMutablePointer(mutableData!.mutableBytes))
mutableData!.mutableBytes now returns an UnsafeMutableRawPointer and the compiler complains that "cannot invoke initializer for type 'UnsafeMutablePointer<_> with an argument of type '(UnsafeMutableRawPointer)'" Now I have been trying to cast them to the same types but have had no success.
My second issue is with the line:
let computedHashData1 = Data(bytes: UnsafePointer(&hash), count: 20)
This line causes a compiler error "Ambiguous use of 'init'"
Your first issue, you can write something like this:
(uuid! as NSUUID).getBytes(mutableData!.mutableBytes.assumingMemoryBound(to: UInt8.self))
But if you can accept Data having the same raw UUID bytes, you can write it as:
var uuidBytes = uuid!.uuid
let data = Data(bytes: &uuidBytes, count: MemoryLayout.size(ofValue: uuidBytes))
Your second issue, in Data.init(bytes:count:), the type of the first parameter is UnsafeRawPointer, to which you can pass arbitrary type of Unsafe(Mutable)Pointers.
Using Swift with Cocoa and Objective-C (Swift 3)
Check the Constant Pointers part of Pointers.
When a function is declared as taking an UnsafeRawPointer argument,
it can accept the same operands as UnsafePointer<Type> for any type
Type.
You have no need to cast pointer types.
let computedHashData1 = Data(bytes: &hash, count: 20)

Converting C pointers to Swift 3

I have the code:
let data = Data(bytes: UnsafePointer<UInt8>(audioBuffer.mData), count: Int(bufferSize))
and
let u16 = UnsafePointer<Int32>(audioBuffer.mData).pointee
Both of which work in Swift 2.3 but not in Swift 3. How do I convert them so they act equivalently? (and why?)
To read 16-bit audio samples from Audio Unit callback buffers in Swift 3, I use:
let bufferPointer = UnsafeMutableRawPointer(mBuffers.mData)
if var bptr = bufferPointer {
for i in 0..<(Int(frameCount)) {
let oneSampleI16 = bptr.assumingMemoryBound(to: Int16.self).pointee
// do something with the audio sample
bptr += 1
}
}
The rest of the Audio Session and Audio Unit code is in this gist: https://gist.github.com/hotpaw2/630a466cc830e3d129b9
I can't say I understand this well, nor have I read the document, but it looks like swift3 pointer casts are scoped to avoid or limit aliasing, so you can't (easily) have two different views of the same piece of memory, or at least not for very long. This means you must either copy the cast data out or do whatever you need to do within a cast callback.
Why eliminate aliasing? I guess it makes for happier compilers.
For Data:
// [NS]Data. probably copying the data
Data(bytes: audioBuffer.mData!, count: Int(audioBuffer.mDataByteSize))
For numeric arrays:
// cast the data to Int32s & (optionally) copy the data out
let umpInt32 = audioBuffer.mData!.assumingMemoryBound(to: Int32.self)
let frameCount = Int(audioBuffer.mDataByteSize/4)
var u32 = [Int32](repeating: 0, count: frameCount)
// copy data from buffer
u32.withUnsafeMutableBufferPointer {
$0.baseAddress!.initialize(from: umpInt32, count: frameCount)
}
p.s. there's some confusion in your code. is u16 supposed to be an array of Int32s? Or UInt16s? Or something else?
Check the latest reference of Data.init(bytes:count:).
The type of the parameter bytes is UnsafeRawPointer, which accepts UnsafeMutableRawPointer. And the type of AudioBuffer.mData is UnsafeMutableRawPointer?. You have no need to convert using initializer.
let data = Data(bytes: audioBuffer.mData!, count: Int(bufferSize))
(You just need to explicitly unwrap mData, as it is imported as nullable type, UnsafeMutableRawPointer?, but you need to pass non-nil UnsafeRawPointer (or UnsafeMutableRawPointer).
The second example, you'd better check what sort of methods are available for UnsafeMutableRawPointer. You can find load(fromByteOffset:as:) method, and can use it like this.
let i32 = audioBuffer.mData!.load(as: Int32.self)
`load(

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