Hi guys, I am using Kotlin 2.2.0-RC2 and compose ...
# compose-ios
b
Hi guys, I am using Kotlin 2.2.0-RC2 and compose multiplatform 1.8.1. I use coil 3.2.0 to show an image in the composable.
Copy code
@Composable
fun Feck() {
    AsyncImage(
        modifier = Modifier
            .size(96.dp)
            .clickable {

            },
        onLoading = { successState ->
            println("Image loading...")
        },
        onSuccess = { successState ->
            println("Image loaded successfully!")
        },
        onError = { errorState ->
            println("Image loading failed: ${errorState.result.throwable.message}")
        },
        model = Res.getUri("drawable/settings_icon_filled.svg"),
        colorFilter = ColorFilter.tint(color = Color.Red),
        contentDescription = null
    )
}
This code works on my sample Android app and iOS app within the kmp project. I wanted to use the module in another existing ios app. So, I have exported an XCFramework with resources and verified that the resources are bundled.
Copy code
fun FeckUIViewController() = ComposeUIViewController {
    Feck()
}
Copy code
import UIKit
import SwiftUI
import Foundation
import common
import chatui

struct ComposeView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> UIViewController {
        
        let fileManager = FileManager.default

                print("--- Listing Main Bundle Contents ---")
                // Corrected line: Access bundlePath directly as it's a String, not an Optional
                let bundlePath = Bundle.main.bundlePath
                print("Main Bundle Path: \(bundlePath)")
                do {
                    let bundleContents = try fileManager.contentsOfDirectory(atPath: bundlePath)
                    print("Contents of Main Bundle:")
                    for item in bundleContents {
                        print("- \(item)")
                    }
                } catch {
                    print("Error listing main bundle contents: \(error.localizedDescription)")
                }

                print("\n--- Checking for compose-resources directly in main bundle ---")
                if let composeResourcesURL = Bundle.main.url(forResource: "compose-resources", withExtension: nil) {
                    print("Found compose-resources directory at URL: \(composeResourcesURL.path)")
                    // Optionally, list contents of compose-resources if found
                    do {
                        let resourcesContents = try fileManager.contentsOfDirectory(atPath: composeResourcesURL.path)
                        print("Contents of compose-resources:")
                        for item in resourcesContents {
                            print("- \(item)")
                        }
                    } catch {
                        print("Error listing compose-resources contents: \(error.localizedDescription)")
                    }

                } else {
                    print("compose-resources directory NOT found in main bundle.")
                }

                print("\n--- Checking for composeResources directly in main bundle (alternative casing) ---")
                if let composeResourcesURL = Bundle.main.url(forResource: "composeResources", withExtension: nil) {
                    print("Found composeResources directory at URL: \(composeResourcesURL.path)")
                } else {
                    print("composeResources directory NOT found in main bundle.")
                }

        
        return FeckViewControllerKt.FeckUIViewController()
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}

struct ContentView: View {
    var body: some View {
        ComposeView()
                .ignoresSafeArea(.keyboard)
    }
}
But coil fails to find the resource file. --- Listing Main Bundle Contents --- Main Bundle Path: /Users/binishmathew/Library/Developer/CoreSimulator/Devices/AB7ABA72-71AC-4578-89E9-79B6A72AB285/data/Containers/Bundle/Application/4C921F2E-698A-4B4F-A07B-03064D8BD661/ios.app Contents of Main Bundle: - _CodeSignature - ios.debug.dylib - __preview.dylib - ios - Frameworks - Info.plist - PkgInfo --- Checking for compose-resources directly in main bundle --- compose-resources directory NOT found in main bundle. --- Checking for composeResources directly in main bundle (alternative casing) --- composeResources directory NOT found in main bundle. Image loading... Image loading failed: No such file or directory Is there any way to fix this ?
🧵 6
b
@Konstantin Tskhovrebov Thank you for your response but I didn't understand what you said. Are you telling me to put the svg file in files dir ? How is it different from having it in the drawable dir ? I would face the same issue with the exported framework. Am I missing something here ?
@Konstantin Tskhovrebov, the problem I'm facing is that I generated an XCFamework and copied it into the dependencies of an existing iOS app. The resources are bundled with the framework. When I run the app, the compose code tries to get the resources from the iOS resources. But the resources are in the framework. Is there anyway to copy the resources to the iOS main bundle ? I tried setting up a build phase run script and and copy file phase as well. Nothing worked. Gemini and I are missing something, maybe.
k
oh! sorry, I missed it. you are right. it should work. Does the xcfrawework contain the resources inside? What Xcode version do you use?
the resources will be available not in the main bundle.
b
Oh. I found the problem. I have two frameworks. common.xcframework and chatui.xcframework. based on the code below, it would pick the first framework and check there. Since common does not have any compose resouces, it will fallback to the default dir. https://github.com/JetBrains/compose-multiplatform/blob/cf0ff4feb298a07968d4867eab[…]in/kotlin/org/jetbrains/compose/resources/ResourceReader.ios.kt
Copy code
/**
     * Determines the path to the compose resources directory.
     * It first searches for a "/Frameworks/'*'.framework/composeResources" directory in the main bundle directory.
     * If no such directory exists, it defaults to a directory named "compose-resources" in the main bundle directory.
     *
     * @return The path to the compose resources directory as a string.
     */
    private fun findComposeResourcesPath(): String {
        val mainBundle = NSBundle.mainBundle
        val fm = NSFileManager.defaultManager()
        val frameworkDirs = fm.findSubDirs(mainBundle.resourcePath + "/Frameworks") { it.endsWith(".framework") }
        val frameworkResourcesDir = frameworkDirs.firstOrNull { frameworkDir ->
            fm.findSubDirs(frameworkDir) { it.endsWith("composeResources") }.isNotEmpty()
        }
        val defaultDir = mainBundle.resourcePath + "/compose-resources"
        return frameworkResourcesDir ?: defaultDir
    }
@Konstantin Tskhovrebov I removed the common framework and the app started taking the icon from chatui framework. Now, I have a doubt. If I have two or more frameworks with compose resources, what would be the behaviour ? If I add the framework to a KMP ios app, based on the statement below,
Copy code
return frameworkResourcesDir ?: defaultDir
What would be the behaviour ?
k
We don't support a scenario with more than one framework with resources. Merge it together.
b
What if I have some 3rd party frameworks as well? I won't be having a control over that.
k
3rd party frameworks with CMP inside? don't think it is a good idea in general. CMP is supposed to be used as a single ios framework. it should be merged on the kotlin side first.
b
I have an existing iOS app and I have a 3rd party framework with CMP. I generated a framework with CMP as well. I can't merge these two.
s
@Binish Mathew have you tried XCTFramework? so in the KMP projects there has been build scripts that generate XCTFrameworks so my high level thought is that if you build the XCTFramework from your CMP project and a swift package pointing to that xct package then you can add that to your iOS app ideally via swiftpackage manager, here is a KMP public repo I’ve worked on in the past. https://github.com/hmrc/tax-kalculator/blob/main/build_xcframework.sh but in the iOS app we would directly consume that package so I dont see why this would fail in CMP but I haven’t done it, interested to know your findings.
k
I have a 3rd party framework with CMP
ask to provide a KMP library instead.
☝️ 1
b
Yeah. Will try that.
@sweetclimusic thanks for the example. I will try this one.