icerockdev / moko-resources

Resources access for mobile (android & ios) Kotlin Multiplatform development
https://moko.icerock.dev/
Apache License 2.0
1.11k stars 121 forks source link

compose-jb ios support #395

Closed serbelga closed 1 year ago

serbelga commented 1 year ago
fun main() {
    defaultUIKitMain("ToDometer", Application("ToDometer") {
        ToDometerAppTheme {
            // ...
            Text(text = MR.strings.app_name.desc().localized())

Jetbrains Compose version: 1.3.0-beta01

Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   ???                                    0x102e4e9a8 ???
1   <translation info unavailable>         0x1053b7e84 ???
2   libsystem_pthread.dylib                0x10c6a7f7b pthread_kill + 263
3   libsystem_c.dylib                      0x10bac1fe0 abort + 130
4   ToDometer                              0x1012107c9 konan::abort() + 9
5   ToDometer                              0x10123adde (anonymous namespace)::terminateWithUnhandledException(ObjHeader*)::$_1::operator()() const + 14
6   ToDometer                              0x10123acaf void (anonymous namespace)::$_0::operator()<(anonymous namespace)::terminateWithUnhandledException(ObjHeader*)::$_1>((anonymous namespace)::terminateWithUnhandledException(ObjHeader*)::$_1) + 47
7   ToDometer                              0x10123aac9 (anonymous namespace)::terminateWithUnhandledException(ObjHeader*) + 9
8   ToDometer                              0x10123aa87 (anonymous namespace)::processUnhandledException(ObjHeader*) + 55
9   ToDometer                              0x10078be16 kfun:kotlinx.coroutines#handleCoroutineExceptionImpl(kotlin.coroutines.CoroutineContext;kotlin.Throwable){} + 86
10  ToDometer                              0x1006f170d kfun:kotlinx.coroutines#handleCoroutineException(kotlin.coroutines.CoroutineContext;kotlin.Throwable){} + 1021
11  ToDometer                              0x1006e12be kfun:kotlinx.coroutines.StandaloneCoroutine.handleJobException#internal + 206
12  ToDometer                              0x100700aa8 kfun:kotlinx.coroutines.JobSupport.finalizeFinishingState#internal + 1720
13  ToDometer                              0x10070b8a3 kfun:kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath#internal + 1667
14  ToDometer                              0x10070b157 kfun:kotlinx.coroutines.JobSupport.tryMakeCompleting#internal + 535
15  ToDometer                              0x10070ad08 kfun:kotlinx.coroutines.JobSupport#makeCompletingOnce(kotlin.Any?){}kotlin.Any? + 488
16  ToDometer                              0x1006decd4 kfun:kotlinx.coroutines.AbstractCoroutine#resumeWith(kotlin.Result<1:0>){} + 324
17  ToDometer                              0x1005cb41a kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 1594
18  ToDometer                              0x1007673a5 kfun:kotlinx.coroutines.DispatchedTask#run(){} + 3333
19  ToDometer                              0x10096f5a6 kfun:org.jetbrains.skiko.NsQueueDispatcher.dispatch$lambda$0#internal + 118
20  ToDometer                              0x10096f88e kfun:org.jetbrains.skiko.NsQueueDispatcher.$dispatch$lambda$0$FUNCTION_REFERENCE$1368.invoke#internal + 78
21  ToDometer                              0x10096f96e kfun:org.jetbrains.skiko.NsQueueDispatcher.$dispatch$lambda$0$FUNCTION_REFERENCE$1368.$<bridge-UNN>invoke(){}#internal + 78
22  ToDometer                              0x100970bad _6f72672e6a6574627261696e732e736b696b6f3a736b696b6f_knbridge662 + 253
23  libdispatch.dylib                      0x10bc3f7fb _dispatch_call_block_and_release + 12
24  libdispatch.dylib                      0x10bc40a3a _dispatch_client_callout + 8
25  libdispatch.dylib                      0x10bc5032c _dispatch_main_queue_drain + 1338
26  libdispatch.dylib                      0x10bc4fde4 _dispatch_main_queue_callback_4CF + 31
27  CoreFoundation                         0x10df5bb6f __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
28  CoreFoundation                         0x10df56486 __CFRunLoopRun + 2482
29  CoreFoundation                         0x10df556f7 CFRunLoopRunSpecific + 560
30  GraphicsServices                       0x11208c28a GSEventRunModal + 139
31  UIKitCore                              0x11b79662b -[UIApplication _run] + 994
32  UIKitCore                              0x11b79b547 UIApplicationMain + 123
33  ToDometer                              0x100c5e0d3 kfun:androidx.compose.ui.main#defaultUIKitMain(kotlin.String;platform.UIKit.UIViewController){} + 2099
34  ToDometer                              0x100fba599 kfun:dev.sergiobelda.todometer.ios#main(){} + 217
35  ToDometer                              0x100fc2158 Konan_start + 152
36  ToDometer                              0x10126b2ae Init_and_run_start + 94
37  dyld_sim                               0x10b4872bf start_sim + 10
38  dyld                                   0x202f28310 start + 2432

Link to repo: https://github.com/serbelga/ToDometerKotlinMultiplatform

Alex009 commented 1 year ago

https://github.com/serbelga/ToDometerKotlinMultiplatform/pull/164 here configuration for your project.

while compose-jb ios integration is experimental api - we will not add this code to moko-resources plugin. but for all who want use Compose for iOS with resources at now - use this workardound in your ios-app build.gradle:

// copy .bundle from all .klib to .kexe
tasks.withType<KotlinNativeLink>()
    .configureEach {
        val linkTask: KotlinNativeLink = this
        val outputDir: File = this.outputFile.get().parentFile

        @Suppress("ObjectLiteralToLambda") // lambda broke up-to-date
        val action = object : Action<Task> {
            override fun execute(t: Task) {
                (linkTask.libraries + linkTask.sources)
                    .filter { library -> library.extension == "klib" }
                    .filter(File::exists)
                    .forEach { inputFile ->
                        val klibKonan = org.jetbrains.kotlin.konan.file.File(inputFile.path)
                        val klib = org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl(
                            klib = klibKonan,
                            component = "default"
                        )
                        val layout = klib.extractingToTemp

                        // extracting bundles
                        layout
                            .resourcesDir
                            .absolutePath
                            .let(::File)
                            .listFiles { file: File -> file.extension == "bundle" }
                            // copying bundles to app
                            ?.forEach {
                                logger.info("${it.absolutePath} copying to $outputDir")
                                it.copyRecursively(
                                    target = File(outputDir, it.name),
                                    overwrite = true
                                )
                            }
                    }
            }
        }
        doLast(action)
    }

// copy .bundle from .kexe to .app
tasks.withType<ExperimentalPackComposeApplicationForXCodeTask>()
    .configureEach {
        val packTask: ExperimentalPackComposeApplicationForXCodeTask = this

        val kclass = ExperimentalPackComposeApplicationForXCodeTask::class
        val kotlinBinaryField =
            kclass.declaredMemberProperties.single { it.name == "kotlinBinary" }
        val destinationDirField =
            kclass.declaredMemberProperties.single { it.name == "destinationDir" }
        val executablePathField =
            kclass.declaredMemberProperties.single { it.name == "executablePath" }

        @Suppress("ObjectLiteralToLambda") // lambda broke up-to-date
        val action = object : Action<Task> {
            override fun execute(t: Task) {
                val kotlinBinary: RegularFile =
                    (kotlinBinaryField.get(packTask) as RegularFileProperty).get()
                val destinationDir: Directory =
                    (destinationDirField.get(packTask) as DirectoryProperty).get()
                val executablePath: String =
                    (executablePathField.get(packTask) as Provider<String>).get()

                val outputDir: File = File(destinationDir.asFile, executablePath).parentFile

                val bundleSearchDir: File = kotlinBinary.asFile.parentFile
                bundleSearchDir
                    .listFiles { file: File -> file.extension == "bundle" }
                    ?.forEach { file ->
                        file.copyRecursively(File(outputDir, file.name), true)
                    }
            }
        }
        doLast(action)
    }

later, when api in compose-jb will be stable - we add integration inside moko-resources gradle plugin

serbelga commented 1 year ago

Ok, nice! I have an implementation of the stringResource function for compose ios on this branch https://github.com/icerockdev/moko-resources/compare/master...serbelga:moko-resources:resources_compose_ios for when compose-jb is stable. I can create a WIP PR if you want

Alex009 commented 1 year ago

compose for ios support released in 0.21.0