7

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.

udi
  • 303
  • 1
  • 6
  • 19
  • check this Answer https://stackoverflow.com/questions/39747190/unsafepointeruint8-initializer-in-swift-3 – Abdelahad Darwish May 11 '18 at 04:38
  • @AbdelahadDarwish I checked the answer. It says to use below code var ptr = data.bytes.assumingMemoryBound(to: UInt8.self). But the data.bytes is unavailable in swift 4. – udi May 11 '18 at 04:57

3 Answers3

8

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

            })
Abdelahad Darwish
  • 5,969
  • 1
  • 17
  • 35
  • 1
    I used your second code and changed UnsafeMutablePointer) to UnsafeMutablePointer). Now its working. Thank you very much for your help. – udi May 11 '18 at 05:23
  • 1
    I get Error: 'UnsafeMutablePointer' is not convertible to 'UnsafeMutablePointer<_>' – iosbegindevel Oct 18 '19 at 07:28
3

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)
2

Solution using NSData

let data = NSData(bytes: arrayOfUInt8, length: arrayOfUInt8.count)
let pointer: UnsafeMutablePointer<Int8> = data.bytes.assumingMemoryBound(to: UInt8.self)
Sentry.co
  • 5,355
  • 43
  • 38