예전에 해커톤에서 만들었던 기능 중 화면 하나를 Compose로 만들어 보았습니다. (사실 귀차니즘으로 일부는 하드코딩 되어 있습니다)
그렇습니다. iOS에서의 UI도 Compose iOS로 구현되었습니다.
UI는 컴포즈. API는 Ktor. 비동기는 Coroutine.
iOS에서는 KMM에서 생성된 ComposeView를 draw 해준 것 외 작성된 swift 코드는 전혀 없습니다. (와!)
import SwiftUI
import shared
struct ComposeView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
IosKt.MainViewController()
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
struct ContentView: View {
var body: some View {
ComposeView()
.ignoresSafeArea(.keyboard)
}
}
iOS에서 CIO 사용 불가능
iOS에서 HttpClient를 사용 시에는 ClientEngline을 CIO가 아닌 Darwin을 사용해야 합니다.
desktop과 web 구현 당시 모두 CIO를 사용했던 걸로 기억하여 당연히 이번에도 사용하면 되겠지 생각했는데 안되더군요...
그렇기에 exepct로 HttpClient를 만들고 iOS와 Android 각각에서 actual로 ClientEngine을 CIO, Drawin를 사용하도록 구현해야 합니다.
// common
expect object KtorClient {
val client: HttpClient
}
// Android
actual object KtorClient {
actual val client: HttpClient = HttpClient(CIO) {
install(ContentNegotiation) {
json()
}
}
}
// iOS
actual object KtorClient {
actual val client: HttpClient = HttpClient(Darwin) {
install(ContentNegotiation) {
json()
}
}
}
iOS 빌드 시 composable 오류
공통 compose UI를 사용하기 위해 commom module에서 composable을 사용하는 경우 Android는 문제 없지만, iOS 빌드 시 오류가 발생하기에 internal로 사용해야합니다.
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)
하지만 이렇게 되면 Android에서는 해당 composable을 접근할 수 없기에 component와 presentation을 multi-module로 분리해야하는데 귀찮아서 이건 다음 기회에 하기로...
로컬 이미지 사용 불가능...
로컬 이미지의 경우에도 리소스에 접근할 수 없어 moko-resource 라이브러리를 활용해야하는데 MR.images까지는 접근되지만 이후 사용해야할 리소스가 generate되지 않아 가져오지 못하고 있습니다 엉엉
서버(웹) 이미지 사용 시 Coil 사용 불가능하기에 우회 개발
서버를 통해 이미지를 불러오는 경우에도 coil 라이브러리는 Android 종속이기에 사용할 수 가 없습니다.
그렇기에 iOS에서는 ktor를 통해 ByteArray로 가져오고 이를 ComposableImageBitmap으로 치환하여 사용하였습니다.
suspend fun loadPicture(client: HttpClient, url: String): ImageBitmap {
val image = client.use { client ->
client.get(url).body<ByteArray>()
}
return Image.makeFromEncoded(image).toComposeImageBitmap()
}
그리고 사용 시에는 다음과 같이 사용할 수 있습니다.
val banner by produceState<ImageBitmap?>(null) {
value = loadPicture(httpClient, "https://haeyum.dev/kmm/banner1.png")
}
다만 매번 produce하고 null 체크 후 이미지에 넣어 사용하기 귀찮기에 KtorImage라는 composable 함수를 만들고 이를 사용하는 방식으로 대체하였습니다.
// KtorImage.kt
@Composable
internal fun KtorImage(
client: HttpClient,
url: String,
contentDescription: String?,
modifier: Modifier = Modifier,
contentScale: ContentScale = ContentScale.Fit,
placeholder: @Composable (Composable.() -> Unit)? = null
) {
val image by produceState<ImageBitmap?>(null) {
value = loadPicture(client, url)
}
image?.let {
Image(
bitmap = it,
contentDescription = contentDescription,
modifier = modifier,
contentScale = contentScale
)
} ?: placeholder
}
// 사용 예시
@Composable
fun JustTestKtorImage() {
KtorImage(
client = client,
url = "https://haeyum.dev/kmm/banner1.png",
contentDescription = "Banner Image",
modifier = Modifier.aspectRatio(2f).fillMaxWidth().drawWithContent {
drawContent()
drawRect(
color = Color(0x66000000),
topLeft = Offset.Zero,
size = size
)
},
contentScale = ContentScale.FillWidth
)
}
iOS Platform API 사용이 잘 되는지 궁금하네요
추가적으로 holix에 공유하니 iOS Platform API도 사용 가능하냐는 질문에 급하게 swift torch 검색하여 kotlin으로 구현해보았습니다.
다만 에뮬레이터에서 카메라를 사용할 수 없기에 정상적으로 돌아가는지 테스트할 수 없어 매우 아쉽네요...
actual fun toggleTorch() {
val device = AVCaptureDevice.defaultDeviceWithDeviceType(AVMediaTypeVideo, null, position = 0)
?: return println("No device found")
if (device.hasTorch) {
device.setTorchMode(if (device.torchMode.toInt() == 0) 1 else 0)
device.unlockForConfiguration()
println("Torch toggled: ${device.torchMode}")
} else {
println("Torch not available")
}
}
--- 05. 01 추가 ---
iOS Platform API를 활용한 로직에서 첨언을 하자면, device를 가져오는 경우 defaultDeviceWithDeviceType이 아닌 defaultDeviceWithMediaType을 사용하고 전후로 Configuration을 lock/unlock해주면 Torch를 정상적으로 사용할 수 있음을 직접 확인하였습니다!
일단 오늘까지 연구를 바탕으로 컴포즈 iOS를 구현은 할 수 있다! 다만 여러 이슈들이 존재한다... 이를 하나하나 해결하고 Wrapping 하여 템플릿을 만든다면 언젠가는 편하게 공통 UI를 구현할 수 있지 않을까... 싶읍니댜... 이만 총총...
'개발 > 개발 일기' 카테고리의 다른 글
아이폰에서 모아키 쓰고 싶다 = 직접 만들자 (7) | 2023.11.16 |
---|---|
Google Solution Challenge Top 100 (0) | 2023.08.07 |
Compose iOS 첫 성공 (0) | 2023.03.27 |
Compose Web - Skiko (0) | 2023.02.23 |
NamingFox - 변수, 함수 이름 짓는 플러그인 개발기3 (0) | 2023.02.13 |
상상하는 것을 소프트웨어로 구현하는 것을 좋아하는 청년
게시글이 마음에 드시나요? [ 공감❤️ ] 눌러주시면 큰 힘이 됩니다!