Binding iOS Framework to Kotlin Library

이태훈·2022년 8월 2일
0

Kotlin/Native에는 외부 라이브러리를 사용하기 위해 cinterop를 제공합니다.

오늘은 이 cinterop를 통해 Umberlla Framework를 Kotlin/Native로 바인딩을 해보겠습니다.

Firebase iOS Framework Binding

대표적인 예제로 Firebase를 Kotlin/Native로 바인딩해보겠습니다.

Retrieve iOS Framework with Carthage

먼저, Firebase의 Umberlla Framework를 받기 위해 Carthage를 사용합니다.

설치법 : brew install carthage

Cartfile에 바인딩하고싶은 Firebase Component를 작성해줍니다.

// Cartfile

binary "https://dl.google.com/dl/firebase/ios/carthage/FirebaseAnalyticsBinary.json" == 8.9.1

carthage update를 실행해주면 Carthage/Build폴더에 xcframework들이 생깁니다.

원하는 xcframework를 프로젝트에 src/nativeInterop/cinterop 폴더에 넣어줍니다. 폴더는 아무데나 넣어도 상관은 없습니다.

Create definition file

어떤 프레임워크를 바인딩할지 정하는 def file을 작성해줍니다.

마찬가지로 cinterop폴더 안에 원하는 이름으로 이름.def 파일을 생성해줍니다.

Firebase Analytics, Firebase Core 모듈을 바인딩하는 def file을 작성해보겠습니다.

// Firebase.def

language = Objective-C
package = kotlin.firebase
modules = FirebaseCore FirebaseAnalytics
compilerOpts = -framework FirebaseCore -framework FirebaseAnalytics
linkerOpts = -framework FirebaseCore -framework FirebaseAnalytics -lsqlite3

Configuration Targets

마지막으로 그래이들에서 ios target 설정해주면 끝입니다.

많은 예제가 있겠지만 간략하게 보기 위해 필요한 코드만 작성하겠습니다.

kotlin {
  ios {
	val target = if (konanTarget is IOS_X64 || konanTarget is IOS_SIMULATOR_ARM64) {
      "ios-arm64_i386_x86_64-simulator"
    } else {
      "ios-arm64_armv7"
    }
    val frameworkPaths = listOf(
      "FirebaseAnalytics", 
      "FirebaseCore"
    ).map {
      projectDir.resolve("src/nativeInterop/cinterop/Build/$it.xcframework/$target") 
    }
    
    binaries.all {
      linkerOpts(frameworkPaths.map { "-F$it" })
      linkerOpts("-ObjC")
    }

	compilations.getByName("main") {
   	  cinterops.create("Firebase") {
        compilerOpts(frameworkPaths.map { "-F$it" })
      }
    }
  }
}

간략히 설명드리겠습니다. Kotlin Multiplatform Gradle DSL 에 대한 자세한 설명은 링크를 참고해주세요.

val target = if (konanTarget is IOS_X64 || konanTarget is IOS_SIMULATOR_ARM64) {
  "ios-arm64_i386_x86_64-simulator"
} else {
  "ios-arm64_armv7"
}

먼저 어떤 플랫폼에 관해 빌드, 컴파일할지 target을 정해줍니다. Framework의 preset에 따라 변경해주시면 됩니다.

val frameworkPaths = listOf(
  "FirebaseAnalytics", 
  "FirebaseCore"
).map {
  projectDir.resolve("src/nativeInterop/cinterop/Build/$it.xcframework/$target") 
}

다음으로 프레임워크의 경로를 지정해줍니다. 상대 경로가 아닌 절대 경로를 지정해줘야 합니다.

compilations.getByName("main") {
  cinterops.create("Firebase") {
    compilerOpts(frameworkPaths.map { "-F$it" })
  }
}

마지막으로 cinterops를 통해 interopability를 수행합니다.

여기서는 위에서 정의해준 Firebase.def 파일을 통해 모듈을 컴파일하고 링킹합니다.

Kotlin/Native에서 interopability를 제공하는 언어들은 다음과 같습니다.

Swift는 제한적으로 지원을 해서 아직까지는 Kotlin/Native에 바인딩하기에 무리가 있어 보입니다.

각설하고 바인딩이 원하는 대로 잘 되는지 확인하기 위해 Sync Gradle을 수행해줍니다.

Conclusion

바인딩이 잘 됐는지 확인하기 위해 iOS SourceSets로 가서 우리가 만든 klib를 import 해봅니다. 앞서 definition filed에서 정의한 패키지를 import 해봅니다.

import kotlin.firebase.FIRApp
import kotlin.firebase.FIRAnalytics

actual fun configure() = FIRApp.configure()
actual fun logEvent(name: String, params: Map<Any?, *>) =
    FIRAnalytics.logEventWithName(name, params)

에러 없이 import가 잘 되는 것을 볼 수 있습니다.

이런 식으로 Umbrella Framework를 Kotlin/Native로 바인딩하면 플랫폼 종속적인 코드를 Kotlin Multiplatform으로 공유하고 한꺼번에 관리할 수 있습니다.

Firebase 같은 경우는 iOS Framework를 받기 위해 Carthage를 사용하고 플랫폼을 나누어 framework를 컴파일했습니다.

하지만 굳이 Carthage를 사용하지 않아도 됩니다. 아직까지는 Carthage를 미지원하는 라이브러리도 있을 뿐더러 뎁스가 추가되기 때문입니다.

한정적이긴 한데 Naver Login SDK와 같이 Objective-C로 이루어져있고 Framework를 제공하는 경우가 있습니다.

이와 같은 경우에서는 Framework를 바로 컴파일하여 Kotlin/Native로 바인딩하여 사용할 수 있습니다.

...

profile
https://www.linkedin.com/in/%ED%83%9C%ED%9B%88-%EC%9D%B4-7b9563237

0개의 댓글