swiftlibssh2unsafe-pointers

'UnsafePointer<Int8>' is not convertible to 'UnsafePointer<_>


I am trying to implement to write a wrapper around libssh2 using Swift. The following code is for removing a file via SFTP.

func removeFile(_ path: String) {
    let data = path.data(using: String.Encoding.utf8)!
    let result = data.withUnsafeBytes { (pointer: UnsafePointer<Int8>) -> Int in
        return libssh2_sftp_unlink_ex(sftpHandle, pointer, data.count)
    }
}

For pointer: UnsafePointer<Int8> I am getting the following error message:

'UnsafePointer<Int8>' is not convertible to 'UnsafePointer<_>

I found this thread about a similar Problem with UInt8. I tried removing the cast but was just getting the next error:

'Swift.UnsafePointer<_>' is not convertible to 'Swift.UnsafePointer<_>'

Running the libssh2_sftp_unlink_ex(sftpHandle, pointer, data.count) outside the closure with a dummy pointer works.

I also found this answer on converting a String to UInt8, the problem is that I wasn't able to port it to Int8. Any ideas on how to correctly convert the pointer?


Solution

  • data.withUnsafeBytes calls the closure with an UnsafeRawBufferPointer, this has to be “bound” to an UnsafePointer<Int8>. Also data.count must be converted to an UInt32 (aka CUnsignedInt) because that is how the C type unsigned integer is imported to Swift:

    func removeFile(_ path: String) {
        let data = path.data(using: String.Encoding.utf8)!
        let result = data.withUnsafeBytes {
            libssh2_sftp_unlink_ex(sftpHandle,
                                   $0.bindMemory(to: Int8.self).baseAddress,
                                   UInt32(data.count))
        }
    }
    

    Alternatively, use the withCString() method of String:

    func removeFile(_ path: String) {
        let result = path.withCString {
            libssh2_sftp_unlink_ex(sftpHandle, $0, UInt32(strlen($0)))
        }
    }
    

    Even simpler: use the variant with needs only a C string and not an explicit string length. Here the compiler automatically creates the code to convert the Swift string to a temporary C string:

    func removeFile(_ path: String) {
        let result = libssh2_sftp_unlink(sftpHandle, path)
    }
    

    (Does not work because libssh2_sftp_unlink is a macro and not imported to Swift.)