Hi :wave: quick question, In my Kotlin Multiplatfo...
# multiplatform
q
Hi đź‘‹ quick question, In my Kotlin Multiplatform project for iOS, I've implemented multi-channel builds using xcconfig files and multiple project configurations. However, every time Gradle performs a refresh operation, it resets Xcode's project configuration back to the default
Pods-iosApp.xxx.xcconfig
file, forcing me to manually restore my custom xcconfig. How can I prevent Gradle from modifying the active xcconfig configuration?
t
Gradle should not reset XCode config unless you are using some 3rd party plugin to manage XCode configuration within Gradle
q
My Gradle plugin declarations are as follows, and I don't appear to be using any third-party plugins: ### libs.versions.toml
Copy code
[versions]
agp = "8.10.1"
kotlin = "2.1.21"
compose = "1.8.2"

[plugins]
androidLibrary = { id = "com.android.library", version.ref = "agp" }
androidApplication = { id = "com.android.application", version.ref = "agp" }

kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlinCocoapods = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

compose = { id = "org.jetbrains.compose", version.ref = "compose" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
### app/build.gradle.kts (excerpt)
Copy code
plugins {
    alias(libs.plugins.androidApplication)
    
    alias(libs.plugins.kotlinCocoapods)
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.kotlinSerialization)
    
    alias(libs.plugins.compose)
    alias(libs.plugins.compose.compiler)
}

kotlin {
    androidTarget {
        @OptIn(ExperimentalKotlinGradlePluginApi::class)
        compilerOptions {
            jvmTarget.set(JvmTarget.JVM_21)
        }
    }
    
    iosX64()
    iosArm64()
    iosSimulatorArm64()
    
    targets.configureEach {
        compilations.configureEach {
            compileTaskProvider.get().compilerOptions {
                freeCompilerArgs.add("-Xexpect-actual-classes")
            }
        }
    }
    
    cocoapods {
        version = "1.0"
        summary = "App"
        homepage = "<https://myproduct.com>"
        
        podfile = project.file("../iosApp/Podfile")
        
        ios.deploymentTarget = "15.4"
       
        framework {
            baseName = "composeApp"
            binaryOption("bundleId", "com.test")
        }
        xcodeConfigurationToNativeBuildType["productODebug"] = NativeBuildType.DEBUG
        xcodeConfigurationToNativeBuildType["productORelease"] = NativeBuildType.RELEASE
        xcodeConfigurationToNativeBuildType["productTRelease"] = NativeBuildType.RELEASE
    }
}
t
@Andrey Yastrebov do you have any ideas?
a
Need to verify it
q
@Andrey Yastrebov Any updates on this? Should I submit this issue to YouTrack?
a
No updates yet, you can submit an issue
q
a
Ok, I have figured it out. The problem is not related to KMP or Gradle setup. This is how cocoapods works, they generate they own
.xcconfig
files and assign them to the target. Each time when run
pod install
cocoapods sets these configurations. What you need to do is to include Pods
xcconfig
into your custom
xcconfig
, and this approach should be persistent. You can refer to this guide: https://orjpap.github.io/xcode/ios/swift/cocoapods/2020/04/20/iOS-build-schemes.html
In my test project I've created two custom configs:
AppDebug.xcconfig
and
AppRelease.xcconfig
Add this to Podfile
project 'iosApp', 'AppRelease' => :release, 'AppDebug' => :debug
Then include in AppDebug and AppRelease
#include "Pods/Target Support Files/Pods-iosApp/Pods-iosApp.appdebug.xcconfig"
#include "Pods/Target Support Files/Pods-iosApp/Pods-iosApp.apprelease.xcconfig"
q
wow, I'm really sorry for asking a silly question. Since I've only been an Android engineer before and recently got involved with iOS development due to company requirements and the KMM framework, I apologize for taking up your time.
a
No worries, cocoapods may be complicated even for experienced iOS developers.
q
For other users who encounter this same issue later, and also as a memo for myself - here's an alternative solution: I categorized configurations by product channels (
Configuration/[productName]/[Debug|Release].xcconfig
), for example: •
Configuration/product1/Debug.xcconfig
•
Configuration/product1/Release.xcconfig
•
Configuration/product2/Release.xcconfig
Each product+build type combination exactly matches build names in the Xcode project configurations (if there's a config file
Configuration/product1/Debug.xcconfig
, there must be a configuration named
product1Debug
in Xcode project settings). Since I couldn't map generated configs to custom ones in different directories using previous solutions, I found another approach in a comment thread - modifying generated config files via hook functions to include custom configurations. Next, add this hook to your Podfile:
Copy code
post_install do |installer|
    config_pattern = /^(.+)(Debug|Release)$/
    missing_configs = []

    # If you have existing post_install scripts, place them here to merge, like:
    # Resolve version warnings
    # installer.pods_project.targets.each do |target|
    #     target.build_configurations.each do |config|
    #         config.build_settings['CONFIGURATION_BUILD_DIR'] = '$PODS_CONFIGURATION_BUILD_DIR'
    #         if config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f < 12.0
    #             config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
    #         end
    #     end
    # end

    # Include custom configurations
    installer.generated_projects.each do |project|
        project.build_configurations.each do |config|
            if config.name =~ config_pattern
                project_name = $1
                config_type = $2
                puts "config #{config}"
                pods_xcconfig_path = "Pods/Target Support Files/Pods-iosApp/Pods-iosApp.#{config.name.downcase}.xcconfig"
                custom_xcconfig_path = "Configuration/#{project_name}/#{config_type}.xcconfig"
                if File.exist?(custom_xcconfig_path) && File.exist?(pods_xcconfig_path)
                    xcconfig = File.read(pods_xcconfig_path)
                    xcconfig = "#include \"../../../#{custom_xcconfig_path}\"\n\n#{xcconfig}"
                    File.open(pods_xcconfig_path, "w") { |file| file << xcconfig }
                    puts "âś… Fixed config #{pods_xcconfig_path} => #include \"Configuration/#{project_name}/#{config_type}.xcconfig\""
                else
                    missing_configs << "Custom Xcconfig: #{custom_xcconfig_path}\n   Pods Xcconfig:   #{pods_xcconfig_path}"
                end
            else
                puts "⏩ Skipping unmatched config: #{config.name}"
            end
        end
    end

    # Handle missing config files
    unless missing_configs.empty?
        header = "🚨 Found #{missing_configs.count} config file issues. Please verify these files exist:"
        raise Pod::Informative, [header, *missing_configs].join("\n\n - ")
    end
end
Then remove any generated config includes from your own configs (if present):
Copy code
#include "Pods/Target Support Files/Pods-iosApp/Pods-iosApp.[productName][release|debug].xcconfig"
Now run
pod install
again. If your custom config names don't match Xcode configuration names, the install will fail with detailed error messages showing exactly which files are problematic.