Is this the correct way of reading a font with Jet...
# compose-ios
j
Is this the correct way of reading a font with Jetbrains compose resources in iOS?
Copy code
private val cache: MutableMap<String, Font> = mutableMapOf()

@Composable
actual fun font(name: String, res: String, weight: FontWeight, style: FontStyle): Font {
    return cache.getOrPut(res) {
        val byteArray = runBlocking {
            try {
                resource("$res.ttf").readBytes()
            } catch (e: Exception) {
                resource("$res.otf").readBytes()
            }
        }
        Font(res, byteArray, weight, style)
    }
}
I put all my files (font files with .ttf file extension that is) into
src/commonMain/resources/font
but its never found regardless if I am using
font("MyName", "font/myname", FontWeight.Bold, FontStyle.Normal)
or something else. I always getting resource not found from resources. I think could be something with my setup with cocoapods but not sure what I could have been missing. Do I need to link it in a certain way? I have static = true. Had similar issues in Android where the problem was I had to change to snake case and put into font folder, to let Android extract the resources into res folder.
1
The cocoapods setup is:
Copy code
cocoapods {
        version = "1.0"
        summary = "Some description for a Kotlin/Native module"
        homepage = "Link to a Kotlin/Native module homepage"
        name = "common"
        ios.deploymentTarget = "14.1"
        podfile = project.file("../iosApp/Podfile")
        framework {
            baseName = "common"
            isStatic = true
        }
        extraSpecAttributes["resources"] = "['src/commonMain/resources/**', 'src/iosMain/resources/**', 'src/commonMain/resources/font/**']"
    }
o
The setup looks fine. Does your
font(...)
function work if the
font.ttf
file is in src/commonMain/resources folder? It should work from resources/font directory as well, but I would check anyway. What's in the byteArray?
j
No doesnt work regardless folder inside resources. I think main issue using multiple cocoapods in multi moduke/feature project. So file need shared between feature modules, inside shared module and for iOS/Android apps itself. Feels like files not moved into bundle at all when look inside the iOS app binary and debug. As the file never found its error before bytes read.
Tried both Bard and ChatGPT and Google. No luck yet. But wonder if any bug, wrong setup or need modify .plist or podspec files include something?
o
Is the font.ttf file located in the resources/font directory of the "main" module (with the app entry point)? Ideally, it should work regardless of the module, but I'm just recalling we tried only within the "main" module in our samples.
j
I have app entry point like iOS in folder. That communicate with a presentation module (navigation/screen setup) and that include shared and feature modules and each feature modules also using shared. So a middle layer module between app entry point and shared module where I want all files inside ideally. I tested to include same files into all KMP modules but then duplication ambiguous or such.
It works if having basic app like template ones. But doing multi platform design system with custom fonts needed compose multiplatform not supporting platform font in Android or such
Also in samples only images.
o
Is your project opensource, so we can reproduce the problem?
j
Not open source sorry. Its internal company app. Not large project yet. Can probably share parts of it. Also modified a lot back and forth trying solve resource file issues along the road. Struggled with the images as well earlier. Mostly I think its cocoapods issue how copy files in build phases but not sure.
Also can add I wanted to use compose google fonts but google enforced Google play services so cant use in iOS yet. Thinking about doing my own framework for CDN and cache similar to Coil that dealing with binaries and such. To also support i18n files, design system specs etc. Its huge project I know 😁
o
Can probably share parts of it.
It would help a lot 🙂 If you manage to do it, please add it here https://github.com/JetBrains/compose-multiplatform/issues
It could be not related to compose at all, but we'll involve other team if needed (KMM, I think)
j
Yeah I also trying implement custom objc interop code to debug and check bundle binary. A little weird got Assets.car file as of example. Could probably be some appbundleid error config being wrong. Not easy tell Xcode what to do in combination with cocoapods and such.
@Oleksandr Karpovich [JB] https://github.com/JetBrains/compose-multiplatform/issues/3223 Tried to add as much context and code I could, but hard to share all my gradle build config files. If feel I can add more let me know 🙂
o
Thanks a lot!
j
Still investigating myself if I have missed something, hopefully AI can find the core issue soon 😄
Was able to use Asset Catalog Tinkerer to analyse Assets.car file, and only contained launcher icon. No font files or image files shared at all. So its not shipping the files at all I have added into cocoapods setup in each Gradle module. Thats good to know its probably not my own code thats wrong that is for reading files. I think its cocoapods and/or my gradle config setup not working or some multiplatform limitation.
o
Thanks! It’s helpful
Does your presentation module have a cocoapods setup as well? So then you call some function from you presentation module in Swift/ObjC code?
j
Yeah both presentation and shared module is cocoapods. Or well I am trying to only have one of them being that, but I am Android dev trying to understand how iOS world works at the same time. Whatever combination that works I prefer to have in my setup 🙂
And yeah in Swift code I only call code from presentation module. And from presentation module I call code from shared module, like reading fonts in my custom Theme / designsystem.
shared module contains all code for both method font expect/actual for Font as well as the resources folder/files. Presentation should only clone files from shared if thats needed to tell Swift iOS app to build. Not sure how the podspec files should be generated between each module. Also tricky as the presentation module also shared by Android application entry point.
Tried to add pod("shared") in cocoapods setup, but then Gradle 8.1 dont like implicit dependecies setup between shared and presentation it seems 😛
Android Studio also warns that my shared module contains multiple roots between commonMain and shared root folder for some reason. Start wondering if common or shared is reserved keywords somewhere 😄
o
I’m trying to investigate this. So far I did this: • Created a new project from a simple template • Added a new module “otherPod” with cocoapods setup and
Copy code
extraSpecAttributes["resources"] = "['src/commonMain/resources/fonts/**', 'src/commonMain/resources/**', 'src/iosMain/resources/**']"
• Added
pod 'otherPod', :path => '../otherPod'
in Podfile • Added
import otherPod
in ContentView.swift file (didn’t work without it for some reason…, maybe a glitch) • Added
otherPod/commonMain/resource/fonts/karla_regular.ttf
Checking what’s in the bundle:
Copy code
struct ComposeView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> UIViewController {
        let resourcePath = Bundle.main.resourcePath!
        do {
            let resourceFiles = try FileManager.default.contentsOfDirectory(atPath: resourcePath)
            print("Resources: \(resourceFiles)")
        } catch let error as NSError {
            print("Error: \(error)")
        }
        return Main_iosKt.MainViewController()
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
And the font file is printed:
Copy code
Resources: ["_CodeSignature", "karla_regular.ttf", "My application", "img123.xml", "compose-multiplatform.xml", "AppIcon60x60@2x.png", "Assets.car", "AppIcon76x76@2x~ipad.png", "fonts", "Info.plist", "PkgInfo"]
Now I’ll try to load it in the app code. Also I ran the cleanup.sh script before retrying to run the app
It works. I was able to load a font from a different module and use it. Here is a branch: https://github.com/JetBrains/compose-multiplatform-ios-android-template/tree/ok/use_fonts I hope it helps The last commit is the most interesting
j
Its not the otherPod not working for me (Named presentation in my case), its the code inside shared calling its own resources not working. Does it matter which code called where? Also thanks for setup this, very interesting!
@Oleksandr Karpovich [JB] Where did you change this and to what? Feels like potential thing I am missing?
o
It was changed automatically by tooling. There is a script cleanup.sh in the project. I ran that script and then I synced gradle. I think it caused the change
j
Because that part I dont have in build phase 😄 I will try clone the cleanup thing as well.
Copy code
xcodebuild: error: '/Users/myuser/StudioProjects/mobile-app/iosApp/iosApp.xcworkspace' does not exist.
lol Do I need to re-add that part somehow?
Nvm that, forgot run pod install 😄
But now getting back to my old problem instead: Unbound public symbol IrSimpleFunctionPublicSymbolImpl Complains I have:
Copy code
object MyTheme {
    val typography: MyTypography
        @Composable
        @ReadOnlyComposable
        get() = LocalMyTypography.current

    val colorTheme: MyColorTheme
        @Composable
        @ReadOnlyComposable
        get() = LocalMyColorTheme.current
}
But I cant change the vals to being internal, as I cant then calling that code anymore. This only happening when adding both pod shared and presentation pod at same time into PodFile. Not sure why 😄
There was something how internal vs public in iOS was needed if multiple cocoapods, I think pending issue ticket for that as well. But ticket said solved in compose 1.4.0 which I use, but still happening 😄
o
For unbound symbol for getter we already have a fix - https://github.com/JetBrains/compose-multiplatform/issues/3216 It will be included automatically in the next release. Although it can be used now as well, please the issue last comment
j
Right can I somehow fix the by avoiding object for now, or compose compiler being set to something else? JUst to try if font thing works?
o
if you don't want to use compose compiler 1.4.7-rc01, The alternative workaround is to use either top level properties, or to use functions instead of properties, or to use a top-level extension property to your object: object MyTheme val MyTheme.typography... - it would require adding new imports in usage places
j
I dont mind using it, but then compose multiplatform complains I cant use Kotlin 1.8.21 instead 😛
I moved the vals outside the object. And now, I be damn the fonts working 😮
So running clean script was the only thing I needed and make sure both pods in PodFile and run pod install .... 😂 A lot of junk from XCode that never added the podspec changes into xc Apple config files for build phases.
o
Great! Glad it works
j
Ccurious did your sample works in Android btw with that? Or only tested in iOS with the font resource loading? I did notice you had name fonts instead of font. Would be interesting to see if thats required for Android. Maybe out of scope, but interesting how to apply resource patterns between the platforms.
Thanks for help!
o
For 1.4.7-rc01 to work, that line should be added in all build.gradle.kts modules with compose
j
Ah ok, thats the issue I had needed to add in all modules.
o
I only tested in ios
j
So cool now having identical compose code for both iOS and Android, as well as working shared resources the right way for both images and fonts. Glad to know it does work having pods refer to each other 🙂
I closed the ticket now with description 🙂