개발/Compose

Compose iOS: Undefined symbols for architecture arm64

귀염둥이 팡무 2023. 5. 1. 17:09


Compose Multiplatform을 통해 Compose iOS를 구현하는 경우 매우 높은 확률로 아래의 컴파일 오류를 만날 것 이다.

물론 아래의 시나리오 외에도 발생할 수 있지만 가장 크게 만날 수 있는 오류이다.

Undefined symbols for architecture arm64:
"_kfun:com.haeyum.crosscomposemobile#HomeScreen(){}", referenced from:
_objc2kotlin_kfun:com.haeyum.crosscomposemobile#HomeScreen(){} in shared(result.o)
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

시나리오

해당 이슈는 다른 플랫폼(Desktop, Android 등) 빌드 시 문제가 발생하지 않지만 iOS를 빌드하는 경우 발생한다.
즉 iOS를 빌드하지 않고 다른 플랫폼으로만 개발하다 마지막에 iOS를 빌드하게 되는 경우 엄청난 지옥을 발견할 수 있다.

이슈 분석

처음에는 gradle에서 iOS를 설정하는 과정에서 문제가 발생한 줄 알고 clean project부터 새 프로젝트 생성까지 다양한 시도를 하였으나 같은 문제가 발생하여 기능을 단계별로 도입하면서 컴파일을 하였고, 그 결과 Composable이 문제라는 사실을 알게 되었다.

iOS 모듈 안에서 Composable을 구현 시에는 문제없이 빌드도 되고 실행이 되지만 common 모듈에서의 Composable을 사용하게 되면 IDE상에서는 문제가 없지만 빌드 시 위의 컴파일 오류를 만나볼 수 있다.

해결 방법

본인이 파악한 방법으로는 크게 2가지가 존재하기에 선호하는 방식으로 사용하는 것을 추천하지만, 개인적으로는 Module 분리를 주로 사용할 것 같다.

Internal

이를 해결하기 위해서는 Composable에 internal을 사용해줌으로써 common모듈에서도 Composable을 공용으로 사용할 수 있게 구현할 수 있다. 하지만 internal을 사용하게 되면 iOS에서 사용이 가능하지만 android에서는 접근이 불가능하므로 commonMain에서 공통 Composable을 구현 후 androidMain과 iosMain에 각각 해당 Composable을 Wrapping 해주면 된다.

Module 분리

단순 같은 Composable임에도 iOS만을 위해 Platform별 분기하여 Wrapping하는 행위는 일종의 보일러플레이트처럼 느껴질 수 도 있으며 귀차니즘을 느낄 것 이다. 이를 common-composable 모듈과 같이 분리하여 관리하면 Wrapping을 하지 않고도 바로 각 플랫폼에서 사용할 수 있을 것 이다.