본문으로 바로가기
반응형

Photo by unsplash

Mutilple OS에 공용 library를 delivery하기 위해서 Kotlin MultiPlatform을 사용합니다. 일반적인 목적, 구조에 대한 설명들은  이미 많은 블로그나 정리된 문서들에서 다루고 있으니, 여기서는 shared에 위치한 MacOS 코드를 bulid하는  절차를 간단하게 정리해 봅니다.

최종 목적은 Flutter의 Mac OS Desktop에 KMP에서 빌드된 라이브러리를 적용하기 위한 첫번째 작업입니다.

또 다른 Multi platform인 Flutter에서 KMP Library를 import 해서 사용한다는 생각이 무리라고 여겨 질 수는 있으나, 상황에 따라 KMP로 만든 공용 library를 AOS, iOS native에 제공하고 flutter에 까지 제공 가능하다면 활용성 범위에서 매력적일 수 있습니다.

호출할 함수의 정의

KMP 프로젝트를 생성하면  하기와 같은 구조로 생성됩니다.

  • androidMain: android에서 전용으로 사용 할 소스코드를 포함
  • commonMain: 각 Platform에서 공용으로 들어갈 소스 코드를 포함
  • iosMain: ios에서 전용으로 사용 할 소스코드를 포함
  • jvmMain: jvm으로 빌드

iOS와 MacOS를 기본적을 동일한 구조를 갖는다고는 하지만 macOS에서만 따로 호출할 수 있는 코드를 하나 정의 하겠습니다.

먼저 src에 macosMain 폴더를 위와 같은 구조로 만듭니다.

jvmMain이나 iosMain도 동일한 구조를 가지고 있습니다.

'macosMain/kotlin/com.example.test.sample'

Platform.macos.kt는 공통으로 적용해야 하는 함수가 있기 때문에 jvmMain이나, iosMain등에서 복사해와서 적절하게 변경해 줍니다.

//Platform.macos.kt

class MACOSPlatform: Platform {
    override val name: String = "Mac OS!"
}
actual fun getPlatform(): Platform = MACOSPlatform()

getPlatform() 함수는 commonMain에서 expect로 설정된 함수이기 때문에 반드시 구현해야 합니다.

다음으로 테스트를 위해 외부에서 호출할 함수 하나를 정의해 줍니다. 위 캡쳐된 구조에서 MyLibInterface를 아래와 같이 구성하겠습니다.

//MyLibInterface.kt

class MyLibInterface {
    fun connectTest(): String {
        println("Mac OS Connected")
        return "Mac OS Connected"
    }
}

호출시 단순히 Mac OS Connected를 찍습니다.

다만 KMP에서 만든 함수를 라이브러리로 만들어 외부에서 호출되도록 하는경우 빌드가 extern C로 되어야 하며, Top-level function을 권고하고 있습니다. MacOS와 Windows 모두에게서 호출되는 함수를 선언하려면 아래와 같이 함수를 선언해야 합니다.

@CName은 외부에서 함수 호출시 package명부터 시작하여 함수 이름까지 전체의 경로로 호출하지 않고 정해진 이름으로 호출할 수 있도록 도와 줍니다. (단! 동일한 이름이 존재해서는 안됩니다.)

instance를 생성하고 나서 호출해야 하는 방법과 Top-level function으로 선언했을때의 함수를 호출하는 방법은 다음 글에서 상세하게 다룹니다. 다만, Top-level function@CName을 사용하는 방법을 권고하며, 위 connectTest()처럼 instance를 생성하여 호출하는 방법은 지양해야 합니다.

XCFramework 빌드

이번에는 shared계위의 build.gradle.kts에 빌드를 설정 합니다.

kotlin {
    androidTarget {
       ...
    }

////////// 이 부분을 변경 - START //////////////
    val xcFramework = XCFramework()

    listOf(
//        iosX64(),
//        iosArm64(),
//        iosSimulatorArm64(),
        macosX64(),
        macosArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "Shared"
            xcFramework.add(this)
        }
    }
////////// 이 부분을 변경 - END //////////////

    jvm()
    ...

외부에서 import하기위해서 XCFramework()으로 한번에 만들어 빌드하도록 합니다. 그렇지 않으면 각 os별로 따로 빌드되어 Xcode에서 다시 xcxFramework으로 합치는 작업을 해야 합니다.

XCfFramework()을 생성하여 macosX64()와  macosArm64()를 scfFramework으로 묶습니다. 

여기서 baseName은("Shared") 외부에서 import할 대상이 됩니다.

iosXXX() 빌드는 일단 주석처리해 놓았습니다. 주석처리가 되지 않으면 모두 빌드되어 xcFramework으로 묶이는데, 시간이 오래 걸립니다.

XCFramework()을 추가한후 gradle을 sync하면 오른쪽 gradle창에 "assembleSCFramework"이 생긴걸 볼수 있습니다.

 

이 명령어를 더블클릭해도 되고, terminal에서 하기 명령어를 수행해도 됩니다.

./gradlew assembleXCFramework

빌드가 잘 되었다면 하기 경로에서 빌드된 framework을 확인할 수 있습니다.

해당 폴더아래에는 debug와 release가 존재하고 각각 "shared.xcframework"을 가지고 있습니다.

Cocoapods를 이용한 빌드

KMP 설치 당시 cocoapods[1]를 설정했다면 이를 통해서도 빌드를 할수 있습니다. Android studio Gradle 설정창을 보면 아래와 같이 빌드를 할수 있는 명령어 셋이 보입니다.

debug나 release만 또는 두개모두를 빌드할수 있는 명령어셋이 존재하여 클릭시 'shared/build/cocoapods' 폴더에서 빌드된 결과를 얻을 수 있습니다.

"shared.xcframework"을 가지고 flutter에 import하는 작업은 다음글에 이어 집니다.

 

References

[1] https://kotlinlang.org/docs/native-cocoapods.html

반응형