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

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?

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

Related

Issue Using Swift's withUnsafeMutableBytes Wrapper

I'm trying to write an extension to Data that allows me to extract it in various casts.
I'm running into a strange problem that I can't quite figure out.
Before I get into generics, I'm trying out fixed data types, and I created this method:
func intValueFromData(_ inData: Data) -> Int64? {
var number = Int64(0)
let len = Swift.min(MemoryLayout<Int64>.size, inData.count)
_ = withUnsafeMutableBytes(of: &number) {
inData.copyBytes(to: $0, from: 0..<len)
}
return number
}
That works. If I do this:
var int_64 = Int64(12345)
var data = Data(bytes: &int_64, count: MemoryLayout<Int64>.size)
let fetched = intValueFromData(data)
fetched becomes "12345" as an Int64.
However, when I try to embed the same method into the Data type, like so:
extension Data {
mutating func intValueFromData() -> Int64? {
var number = Int64(0)
let len = Swift.min(MemoryLayout<Int64>.size, self.count)
_ = withUnsafeMutableBytes(of: &number) {
self.copyBytes(to: $0, from: 0..<len)
}
return number
}
}
I get a compile-time error that says that "withUnsafeMutableBytes(of: &number)" is not supposed to have an argument.
The last time that I encountered something like this, it turned out that Apple explicitly blocked a functionality, but neglected to tell us in a straightforward manner.
I am not an expert at this kind of thing, but I am wondering if anyone could shed any light on why withUnsafeMutableBytes(of: &number) behaves differently inside the extension.
In the context of Data, withUnsafeMutableBytes refers to Data.withUnsafeMutableBytes(_:).
To disambiguate this and refer to the global function, explicitly prefix the module name: _ = Swift.withUnsafeMutableBytes(of: &number) {
Just to complete the set, here's the generic function I created:
/* ###################################################################################################################################### */
// MARK: - Data Extension -
/* ###################################################################################################################################### */
/**
This extension adds the ability to extract data fron a Data instance, cast into various types.
*/
public extension Data {
/* ################################################################## */
/**
This method allows a Data instance to be cast into various standard types.
- parameter inValue: This is an inout parameter, and the type will be used to determine the cast.
- returns: the cast value (the parameter will also be set to the cast value). Can be ignored.
*/
#discardableResult
mutating func castInto<T>(_ inValue: inout T) -> T {
// Makes sure that we don't try to read past the end of the data.
let len = Swift.min(MemoryLayout<T>.size, self.count)
_ = Swift.withUnsafeMutableBytes(of: &inValue) {
self.copyBytes(to: $0, from: 0..<len)
}
return inValue
}
}

Convert Swift Encodable class typed as Any to dictionary

In connection with my previous questions, I decided to subclass NSArrayController in order to achieve the desired behavior.
class NSPresetArrayController: NSArrayController {
override func addObject(_ object: Any) {
if let preset = object as? Preset {
super.addObject(["name": preset.name, "value": preset.value])
} else {
super.addObject(object)
}
}
}
This works, but what if I wanted something that works for any Encodable class, and not just one with two properties called name and value?
Basically, the problem is creating a dictionary from a class, where the keys are the property names, and the values are the values of these properties.
I tried writing something like this:
class NSPresetArrayController: NSArrayController {
override func addObject(_ object: Any) {
if let encodableObject = object as? Encodable {
let data = try! PropertyListEncoder().encode(encodableObject)
let any = try! PropertyListSerialization.propertyList(from: data, options: [], format: nil)
super.addObject(any)
}
}
}
However, I get a compile error:
Cannot invoke 'encode' with an argument list of type '(Encodable)'
1. Expected an argument list of type '(Value)'
How do I fix this so it compiles?
The problem is that protocols don't always conform to themselves. PropertyListEncoder's encode(_:) method expects a Value : Encodable argument:
func encode<Value : Encodable>(_ value: Value) throws -> Data
However the Encodable type itself is currently unable to satisfy this constraint (but it might well do in a future version of the language).
As explored in the linked Q&A (and also here), one way to work around this limitation is to open the Encodable value in order to dig out the underlying concrete type, which we can substitute for Value. We can do this with a protocol extension, and use a wrapper type in order to encapsulate it:
extension Encodable {
fileprivate func openedEncode(to container: inout SingleValueEncodingContainer) throws {
try container.encode(self)
}
}
struct AnyEncodable : Encodable {
var value: Encodable
init(_ value: Encodable) {
self.value = value
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try value.openedEncode(to: &container)
}
}
Applied to your example:
class NSPresetArrayController : NSArrayController {
override func addObject(_ object: Any) {
guard let object = object as? Encodable else {
// Not encodable, maybe do some error handling.
return
}
do {
let encoded = try PropertyListEncoder().encode(AnyEncodable(object))
let cocoaPropertyList = try PropertyListSerialization.propertyList(from: encoded, format: nil)
super.addObject(cocoaPropertyList)
} catch {
// Couldn't encode. Do some error handling.
}
}
}
The type of the value that you pass to encode(_:) has to be a concrete type that implements Encodable. This means you need to recover the object's real type from the Any that you have. In order to cast, you must have a statically-specified type to which you are casting. You can't say object as! type(of: object), in other words; you have to say object as? MyClass (or in a generic context you can say object as? T).
Therefore, I believe that the only way to get around this is to statically enumerate the types you are working with, like so:
import Foundation
struct S : Encodable {
let i: Int
}
struct T : Encodable {
let f: Float
}
struct U : Encodable {
let b: Bool
}
func plistObject(from encodable: Any) -> Any? {
let encoded: Data?
switch encodable {
case let s as S:
encoded = try? PropertyListEncoder().encode(s)
case let t as T:
encoded = try? PropertyListEncoder().encode(t)
case let u as U:
encoded = try? PropertyListEncoder().encode(u)
default:
encoded = nil
}
guard let data = encoded else { return nil }
return try? PropertyListSerialization.propertyList(from: data,
options: [],
format: nil)
}
Needless to say, this is rather gross. It's inflexible, repetitive boilerplate. I'm not sure I can actually recommend its use. It's an answer to the literal question, not necessarily a solution to the problem.

A swiftier way to convert String to UnsafePointer<xmlChar> in Swift 3 (libxml2)

I'm working on a Swift 3 wrapper for the libxml2 C-library.
There are two convenience methods to convert String to UnsafePointer<xmlChar> and vice versa. In libxml2 xmlChar is declared as unsigned char.
UnsafePointer<xmlChar> to String is uncomplicated
func stringFrom(xmlchar: UnsafePointer<xmlChar>) -> String {
let string = xmlchar.withMemoryRebound(to: CChar.self, capacity: 1) {
return String(validatingUTF8: $0)
}
return string ?? ""
}
For String to UnsafePointer<xmlChar> I tried many things for example
let bytes = string.utf8CString.map{ xmlChar($0) }
return UnsafePointer<xmlChar>(bytes)
but this doesn't work, the only working solution I figured out is
func xmlCharFrom(string: String) -> UnsafePointer<xmlChar> {
let pointer = (string as NSString).utf8String
return unsafeBitCast(pointer, to: UnsafePointer<xmlChar>.self)
}
Is there a better, swiftier way without the bridge cast to NSString and unsafeBitCast?
Swiftiest way I can think of is to just use the bitPattern: initializer:
let xmlstr = str.utf8CString.map { xmlChar(bitPattern: $0) }
This will give you an Array of xmlChars. Hang onto that, and use Array's withUnsafeBufferPointer method when you need to pass an UnsafePointer to something:
xmlstr.withUnsafeBufferPointer { someAPIThatWantsAPointer($0.baseAddress!) }
Don't let the UnsafePointer escape from the closure, as it won't be valid outside it.
EDIT: How's this for a compromise? Instead of having your function return a pointer, have it take a closure.
func withXmlString<T>(from string: String, handler: (UnsafePointer<xmlChar>) throws -> T) rethrows -> T {
let xmlstr = string.utf8CString.map { xmlChar(bitPattern: $0) }
return try xmlstr.withUnsafeBufferPointer { try handler($0.baseAddress!) }
}
Or, as an extension on String:
extension String {
func withXmlString<T>(handler: (UnsafePointer<xmlChar>) throws -> T) rethrows -> T {
let xmlstr = self.utf8CString.map { xmlChar(bitPattern: $0) }
return try xmlstr.withUnsafeBufferPointer { try handler($0.baseAddress!) }
}
}
I'm working on a Swift 3 wrapper for the libxml2 C-library.
Condolences.
[...] String to UnsafePointer [is complicated]
Agree. It is complicated because it is unclear who owns the xmlChar array.
[...] the only working solution I figured out is
let pointer = (string as NSString).utf8String
This works because of the ownership semantics of -[NSString utf8String]:
Apple docs:
This C string is a pointer to a structure inside the string object, which may have a lifetime shorter than the string object and will certainly not have a longer lifetime.
So the lifetime is probably something like the current autorelease pool or even shorter, depending on the compiler's ARC optimisations and the implementation of utf8String. Definitely not safe to keep around.
Is there a better, swiftier way [...]?
Well, that depends on the use case. There's no way to handle this without thinking about the ownership of the created xmlChar buffer.
It should be clear from the API how the functions are using the passed string (even though I know that libxml2's documentation is terrible).
For situations where a string is just used during a function call it might be nice to have a scoped access function:
extension String {
func withXmlChar(block: (UnsafePointer<xmlChar>) -> ()) { ... }
}
If the function keeps the pointer around you must guarantee for the lifetime of the pointee. Probably something like a container object that keeps a Data and pointer around for some ARC maintained lifetime...
It might be worthwile to go through one of Mike Ash's recent articles which is about managing ownership of objects beyond ARC.
String has a
public init(cString: UnsafePointer<UInt8>)
initializer, therefore the conversion from an XML string to a Swift string can be simplified to
let xmlString: UnsafePointer<xmlChar> = ...
let s = String(cString: xmlString)
Ill-formed UTF-8 sequences are replaced by the Unicode replacement
character U+FFFD.
For the conversion from a Swift string to an XML string I would suggest
a similar approach as Charles Srstka, but using the
existing String.withCString method instead of creating an intermediate
array:
extension String {
func withXmlString<T>(handler: (UnsafePointer<xmlChar>) throws -> T) rethrows -> T {
return try self.withCString { try handler(UnsafeRawPointer($0).assumingMemoryBound(to: UInt8.self)) }
}
}
If the throwing option is not needed, it simplifies to
extension String {
func withXmlString<T>(handler: (UnsafePointer<xmlChar>) -> T) -> T {
return self.withCString { handler(UnsafeRawPointer($0).assumingMemoryBound(to: UInt8.self)) }
}
}

Delegating to another initializer from a closure

I'm looking at ways to create a DispatchData instance out of a Data instance. I started with a static function:
static func dispatchData(fromData data: Data) -> DispatchData {
return data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) -> DispatchData in
let bufferPtr = UnsafeRawBufferPointer(start: UnsafeRawPointer(typedPtr), count: data.count)
return DispatchData(bytes: bufferPtr)
}
}
This works fine (and is coincidentally very similar to this answer). I then decided I'd like to add this as an initializer to DispatchData in an extension and wrote:
private extension DispatchData {
init(data: Data) {
data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) in // 1
let bufferPtr = UnsafeRawBufferPointer(start: UnsafeRawPointer(typedPtr), count: data.count)
self.init(bytes: bufferPtr)
}
}
}
At this, the compiler balked on the line marked // 1:
Variable 'self.__wrapped' captured by a closure before being initialized
It makes sense — the compiler doesn't want self to be captured before I've delegated to init(bytes: UnsafeRawBufferPointer), since stored variables — like __wrapped, in this case — aren't yet initialized. But I can't see another way to get the UnsafeRawBufferPointer; I can't return it from data.withUnsafeBytes, per the documentation:
Warning
The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.
I know I can just go back to using a static function to achieve what I want, but I'd prefer it if there were a way to add this initializer. Is there a way to make this initializer work as intended?
The problem with your extension is that although the compiler knows you're passing the closure as non-escaping argument to withUnsafeBytes(_:); it doesn't have any guarantee that it will be called once and only once (which is required for initialisation).
One simple solution is to, rather than chain to another initialiser, just construct a new instance of DispatchData yourself, and then assign this to self, thus completing initialisation:
import Foundation
private extension DispatchData {
init(data: Data) {
self = data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) in
DispatchData(bytes:
UnsafeRawBufferPointer(start: typedPtr, count: data.count)
)
}
}
}
Note also that there's an implicit conversion from UnsafePointer<UInt8> to UnsafeRawPointer? when passing it as an argument; so we don't need to wrap it in an initialiser call.
And in Swift 5, withUnsafeBytes gives you a UnsafeRawBufferPointer directly:
private extension DispatchData {
init(data: Data) {
self = data.withUnsafeBytes(DispatchData.init)
}
}

Generic method causes "cannot pass immutable value of type AnyObject? as inout argument"

I'm working on a Swift version of a Keychain wrapper class. I'm a bit puzzled why this works:
private func executeFetch(query: KeyStoreObject) throws -> AnyObject? {
var result: AnyObject?
try executeQuery(query) { SecItemCopyMatching(query.data, &result) }
return result
}
And this doesn't:
private func executeFetch<T: AnyObject>(query: KeyStoreObject) throws -> T? {
var result: T?
try executeQuery(query) { SecItemCopyMatching(query.data, &result) }
return result
}
I believe that the error is that SecItemCopyMatching may attempt to assign anything of type AnyObject (i.e. anything at all) to result. However, in the second example result is not necessarily of type AnyObject; it is some particular type T that is a subclass of AnyObject. Thus, SecItemCopyMatching may be unable to correctly set result. For instance, what if T is Int, but SecItemCopyMatching wants to set result to a String? When result is of type AnyObject this is no longer an issue.