Search code examples
androidiosswiftkotlinkotlin-multiplatform

Kotlin Mutliplatform : Maping Swift/Objc code to Kotlin in iOSMain module?


I am learning KMM. I am now designing a common Location fetching in iOSMain and Android main

My problem is , I don't know to map Swift to Kotlin in iOSMain

For example,

The Swift code for , getting location is

var locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
var currentLoc: CLLocation!
if(CLLocationManager.authorizationStatus() == .authorizedWhenInUse ||
CLLocationManager.authorizationStatus() == .authorizedAlways) {
   currentLoc = locationManager.location
   print(currentLoc.coordinate.latitude)
   print(currentLoc.coordinate.longitude)
} 

Kotlin side implementation:

kotlin

In the above code:

  1. How to use the Swift's .authorizedWhenInUse and .authorizedAlways in Kotlin Code ?

  2. And the in currentLoc.coordinate.longitude , the longitude and latitude is not resolving . Why ?

Please help me


Solution

  • Mapping Swift (Objective-C) to Kotlin

    1. How to use the Swift's .authorizedWhenInUse and .authorizedAlways in Kotlin Code ?

    According to Kotlin's documentation on interoperability, Kotlin/Native provides bidirectional interoperability with Objective-C, not Swift, so my first recommendation would be to reference Apple's Objective-C documentation over the Swift documentation.

    If you pull up the Swift documentation for .authorizedWhenInUse, you'll see you can switch the language to Objective-C:

    authorizedWhenInUse Swift reference documentation


    Switch this to the Objective-C documentation to see how to reference this in Objective-C:

    authorizedWhenInUse Objective-C reference documentation


    Given this, you should be able to use kCLAuthorizationStatusAuthorizedWhenInUse in your Kotlin code.

    Referencing iOS Frameworks

    Since you already have some reference code, you could also simply Command+Click (or Command+B) one of the objects (for example, CLLocationManager) which should open up the compiled Kotlin code.

    Manually you can also access all iOS frameworks from the "Project" View of Android Studio → "External Libraries" and then search for the iOS framework that you are searching for.

    enter image description here

    Here, you can dig through the frameworks to find what you're looking for. Not knowing the equivalent Objective-C API, you could just search for "authorizedWhenInUse" and can find it:

    Kotlin CoreLocation framework reference code

    Dealing with C-structs

    1. currentLoc.coordinate.longitude , the longitude and latitude is not resolving

    This is more complicated...

    The location property is of type CLLocationCoordinate2D and (the important part!) is that it is contained within a CValue:

    @kotlinx.cinterop.ExternalObjCClass public open class CLLocation : platform.darwin.NSObject, platform.Foundation.NSCopyingProtocol, platform.Foundation.NSSecureCodingProtocol {
    
        ...
    
        public final val coordinate: kotlinx.cinterop.CValue<platform.CoreLocation.CLLocationCoordinate2D> /* compiled code */
    
    

    Note that in Objective-C, CLLocationCoordinate2D is a C struct:

    typedef struct CLLocationCoordinate2D {
        ...
    } CLLocationCoordinate2D;
    

    The Kotlin documentation here is thin, but it shows the methods available for CValue includes the .useContents() method.

    CValue.useContents() method definition

    Therefore, your code could be written as follows (compiled and confirmed that this runs and generates a location on a physical device):

    val locationManager = CLLocationManager()
    
    val currentLoc: CLLocation?
    if (locationManager.authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse ||
        locationManager.authorizationStatus == kCLAuthorizationStatusAuthorizedAlways) {
        currentLoc = locationManager.location
    
        currentLoc?.coordinate?.useContents {
            println("latitude = ${latitude}")
            println("longitude = ${longitude}")
        }
    }
    

    [Update September 2022] If you want to dig deeper, I also published a blog post on writing iOS-platform-dependent code using Kotlin with KMM's expect/actual: artandscienceofcoding.com/science/avoid-this-kmm-technique