I want to covert the corresponding swift method into kotlin for kmm
func checkIfDirectoryExist(fileURL: URL) -> Bool {
var isDir: ObjCBool = false
if FileManager.default.fileExists(atPath: fileURL.path, isDirectory: &isDir)
{
if isDir.boolValue {
return true
} else {
return false
}
}
return false
}
However I am not sure hot to pass Boolean pointer in kotlin as there are no toCPointer<Boolean>()
method neither **
works
Working with Objective-C APIs in Kotlin/Native can be complicated. In many cases such as this one, you're actually interacting with elements of C such as pointers and memory allocation.
isDirectory
boolean in SwiftGiven the Swift code that you wrote:
var isDir: ObjCBool = false
if FileManager.default.fileExists(atPath: fileURL.path, isDirectory: &isDir) {
...
If we consider how this code is running it might help to understand how to write this in Kotlin/Native:
isDir
variable is defined as an ObjCBool
type.isDir
to the fileExists()
method, an &
is used in order to pass the variable by reference (read/write), not by value (read only).fileExists()
method not only returns the boolean indicator of whether the file exists or not, but it also populates the value of the isDir
variable with the result (often in Swift referred to as in/out
).isDirectory
argument is a pointer to a boolean (BOOL *
), as opposed to the return value of the method which is just a boolean (BOOL
).isDir
in K/N using memScoped
and alloc
In Kotlin/Native, we need to allocate memory for the isDir
variable to be populated, which relies on leveraging the kotlinx.cinterop
suite of methods.
Using memscoped
will allocate memory for use within the block passed as an argument and deallocate that memory when the scope closes. You can see in the declaration of the function that whatever type we return from the block (R
) will be the same value returned from the call to memScoped
(R
):
inline fun <R> memScoped(block: MemScope.() -> R): R
The MemScoped
object allows calling the alloc
method to allocate memory for use within this block, and we need to allocate memory for a BooleanVar
. Note that alloc
returns type T
which conforms to CVariable
.
memScoped {
val isDirectory = alloc<BooleanVar>()
...
}
isDirectory
When calling the fileExists()
method in K/N, if we check the K/N header declaration we can see what it expects to receive for arguments (formatted for readability). Note the isDirectory
parameter is a CPointer
pointing to a BooleanVar
. This is how we knew to allocate memory for a BooleanVar
and not some other kind of type:
@kotlinx.cinterop.ObjCMethod
public open external fun fileExistsAtPath(
path: kotlin.String,
isDirectory: kotlinx.cinterop.CPointer<kotlinx.cinterop.BooleanVar>?
): kotlin.Boolean
Therefore, with the memory that we allocated, which is of type CVariable
, we can pass the pointer to that memory (passing by reference) for the fileExistsAtPath
method to change the value of it:
val fileExists = NSFileManager.defaultManager.fileExistsAtPath(path, isDirectory.ptr)
^^^
Lastly, getting the value populated into the BooleanVar
CVariable
is accessed from the .value
property.
Together, the code might look something like:
return memScoped {
val isDirectory = alloc<BooleanVar>()
val fileExists = NSFileManager.defaultManager.fileExistsAtPath(path, isDirectory.ptr)
return@memScoped fileExists && isDirectory.value
}
As for converting the entire method, this is getting a bit into API generalization. You'll need to convert the URL
argument to a platform-independent type, such as a simple path (string). This appears to be unrelated to your original question, so although I'll leave this detail out, you might find this blog post helpful.
Kotlin/Native has some documentation on its interoperability with C, however admittedly, I find this documentation a bit difficult to understand with the provided example.