Simon BRUNOU
05/10/2021, 12:25 PMMap
to an objective-c CFDictionary
using CFDictionaryCreate
. The main issue is using the pointers to go back and forth between Kotlin and Objective-C. Here’s my code so far (not generic for now will be later)
fun Map<CFStringRef?, Any?>.toCFDictionary(): CFDictionaryRef? {
return CFDictionaryCreate(
kCFAllocatorDefault,
keys,
values,
size.toLong(),
kCFTypeDictionaryKeyCallBacks,
kCFTypeDictionaryValueCallBacks
)
}
There is a type mismatch for the second, third, fifth and sixth arguments.
Second and third arguments should be CValuesRef<COpaquePointerVar>? but are currently Set<CPointer<__CFString>?> and Collection<Any?>.
The last two arguments should be CValuesRef<CFDictionaryKeyCallBacks>? and CValuesRef<CFDictionaryValueCallBacks>? but are CFDictionaryKeyCallBacks and CFDictionaryValueCallBacks.
I do not know how to convert those values to the expected ones, can anyone help me plase ?
Thank you for your time.Sam
05/10/2021, 1:03 PMSimon BRUNOU
05/10/2021, 1:10 PMAny?
to CValuesRef<*>?
. Is it because I’m not supposed to do that ? The update code is now
fun Map<CFStringRef?, Any?>.toCFDictionary(): CFDictionaryRef? {
memScoped {
val mutableResult = CFDictionaryCreateMutable(
kCFAllocatorDefault,
size.toLong(),
null,
null
)
entries.forEach { entry ->
CFDictionaryAddValue(
mutableResult,
entry.key,
entry.value
)
}
return mutableResult
}
}
entry.value
is the only error left.Sam
05/10/2021, 2:34 PM/**
* Converts a mutable map into a CFDictionaryRef. This function should only be called within a memScoped block.
*/
private fun MutableMap<CFStringRef, Any>.toCFDictionary() = CFDictionaryCreateMutable(null, this.keys.size.toLong(), null, null).also { cfd ->
this.entries.forEach { entry ->
when(val value = entry.value) {
is String -> CFDictionaryAddValue(cfd, entry.key, CFStringCreateWithCString(null, value, kCFStringEncodingUTF8))
is Int -> CFDictionaryAddValue(cfd, entry.key, CFBridgingRetain(NSNumber(int = value)))
is Long -> CFDictionaryAddValue(cfd, entry.key, CFBridgingRetain(NSNumber(long = value)))
is Float -> CFDictionaryAddValue(cfd, entry.key, CFBridgingRetain(NSNumber(float = value)))
is Double -> CFDictionaryAddValue(cfd, entry.key, CFBridgingRetain(NSNumber(double = value)))
is CPointer<*> -> CFDictionaryAddValue(cfd, entry.key, value)
}
}
}
The reason for the NSNumber conversions is that you can’t store primitive values in a CFDictionary, it only takes objects. IIRC, it interprets the numbers as pointers and then all hell breaks loose.Simon BRUNOU
05/10/2021, 2:38 PMrusshwolf
05/10/2021, 3:33 PMcfRetain()
function takes a list of arbitrary values and gives you a block where they've been passed through CFBridgingRetain()
to get `CFTypeRef`s, and then calls CFBridgingRelease
on them all for you after the block runs. The cfDictionaryOf()
function converts a Map<CFStringRef?, CFTypeRef?>
to a CFDictionaryRef
. I probably got a little too cute with the API given that it's all private stuff in one file, but it's maybe a helpful pattern for wrapping some of the uglier pointer handling so your calling API can be more KotlinySimon BRUNOU
05/10/2021, 3:36 PM