· 6 min read Posted by Kevin Galligan, Russell Wolf

KMMBridge Quick Start - CocoaPods

Publishing the KMMBridge Quick Start template project to CocoaPods

Quick Start Continued

This is a followup post for publishing the KMMBridge Quick Start template, specifically for CocoaPods. Since SPM works almost out-of-the-box, and CocoaPods has multiple steps, and points of failure, we decided to make it an optional part 2. However, you must do all of the setup in part 1 before attempting to publish to CocoaPods!

CocoaPods Setup

Publishing to CocoaPods requires a few extra steps. In summary, CocoaPods keeps it’s dependency info in a separate git repo. That means you will need to:

  1. Create (or have access to) a GitHub repo dedicated to storing CocoaPods version information.
  2. Configure CI with SSH key info to be able to push to that repo.
  3. Configure your clients to be able to access that repo (if not public).
CocoaPods publishes podspec config info to a separate repo. This repo only holds config information. It does not host your binaries. The XCFramework binary files are pushed to the same place as they were in part 1. Whether you are publishing for SPM, CocoaPods, or both, the XCFramework binary zip file that KMMBridge uploads is the same file, in the same place. Only the config info for SPM and CocoaPods differs.

Add a CocoaPods Podspec repo

CocoaPods keeps it’s config info, called “podspecs”, in a separate git repo. In our case, this means you’ll want to create a new empty repo on GitHub.

Create a repo in your org. We’ll call ours “Podspecs”. It can be public or private.

Important! Add at least one commit to this repo. The easiest option is to create a README. The CocoaPods process won’t push without an existing commit.

Next, you’ll need to enable access between these repos and our CI build workflow. Head over to the CocoaPods configuration section of the KMMBridge docs for details.

You must complete the steps described in the KMMBridge docs, and it is relatively easy to do something wrong in that config, so be very careful.

After you configure your keys, before you can publish, you’ll need to update the Framework config in allshared/build.gradle.kts.

Update the Gradle Build

Our Kotlin Framework publication config in allshared/build.gradle.kts needs a little work before we can use KMMBridge to publish with CocoaPods.

In a KMP project that builds Xcode Frameworks, you need to create the targets you want to build, and tell them to make Frameworks. You can do this explicitly, which is how the default template is configured:

listOf(
    iosX64(),
    iosArm64(),
    iosSimulatorArm64()
).forEach {
    it.binaries.framework {
        export(project(":analytics"))
        isStatic = true
    }
}

To use that in an Xcode project, you need to connect it in some way. In the case of our test app, we use Direct Integration with embedAndSignAppleFrameworkForXcode.

This config works for publishing to SPM.

For CocoaPods, we need more config info to construct the podspec and publish. We’ll need to add the Kotlin CocoaPods plugin and it’s associated configuration.

There are some important distinctions here. How we configure local development, and how we publish binaries, aren’t directly related. You could use CocoaPods for local development, but only publish to SPM. Alternatively, you can use Direct Integration and publish to CocoaPods, which is what we’ll be doing here. However, we need to add the local CocoaPods configuration because KMMBridge uses that to collect the necessary information for CocoaPods publication.
  1. Add the Kotlin CocoaPods Gradle plugin:
plugins {
    kotlin("multiplatform")
    id("co.touchlab.kmmbridge")
    id("co.touchlab.skie")
    kotlin("native.cocoapods") // <- Add this
    `maven-publish`
}
  1. Replace the target and framework config with the following:
    iosX64()
    iosArm64()
    iosSimulatorArm64()

    cocoapods {
        summary = "KMMBridgeSKIETemplate"
        homepage = "https://www.touchlab.co"
        ios.deploymentTarget = "13.5"
        extraSpecAttributes["libraries"] = "'c++', 'sqlite3'"
        license = "BSD"
        extraSpecAttributes.put("swift_version", "\"5.0\"") // <- SKIE Needs this!
        framework {
            export(project(":analytics"))
            isStatic = true
        }
    }
  1. Add the CocoaPods config to the kmmbridge block:
kmmbridge {
    mavenPublishArtifacts()
    spm()
    cocoapods("git@github.com:[your org]/[your podspec repo].git")
}
If you would prefer to copy/paste, a copy of the updated build.gradle.kts file can be found at part2/build.gradle.kts

More Info About the Kotlin CocoaPods Plugin

How the Kotlin CocoaPods plugin configures Frameworks

The Kotlin CocoaPods Gradle plugin is added with the following:

plugins {
    kotlin("native.cocoapods")
}

This plugin is designed to enable local CocoaPods development. It does not support any kind of publication outside of your development machine. When discussing KMP integration with new teams, as SPM has increased in popularity, there is an automatic aversion to using CocoaPods for anything.

The local dev configuration has very little to do with how your projects are published.

I have to say that often, but it is a confusing situation.

Until very recently, local CocoaPods development was what I would recommend for everybody. While not perfect, the Kotlin CocoaPods plugin did a good job of automating builds. However, I’ve recently come to appreciate Direct Integration with embedAndSignAppleFrameworkForXcode. In general we’ll be using that more often going forward.

To publish CocoaPods with KMMBridge, you need to use the Kotlin CocoaPods Gradle plugin, but only to get access to the config block. We’ll still use embedAndSignAppleFrameworkForXcode for local test app development.

The part of Framework configuration that is sometimes confusing and the source of errors is that the Kotlin CocoaPods Gradle plugin automatically configures Apple/Darwin targets to build an Xcode Framework. It does this silently and automatically.

The reason our target config went from this:

listOf(
    iosX64(),
    iosArm64(),
    iosSimulatorArm64()
).forEach {
    it.binaries.framework {
        export(project(":analytics"))
        isStatic = true
    }
}

to this:

iosX64()
iosArm64()
iosSimulatorArm64()

is because the Kotlin CocoaPods plugin automatically adds the framework { /* Etc */ } config.

One common source of errors is actually the extra Framework config. People see tutorials with the explicit framework { /* Etc */ } config and add that, then also add kotlin("native.cocoapods"). Doing so will create duplicate Framework outputs for each target. For KMMBridge specifically, when you try to assemble the XCFramework, it will fail.

See KMMBridge - Troubleshooting for details.

Update GitHub Actions Workflows

Then, assuming you added the key secret in your Kotlin repo and called it PODSPEC_SSH_KEY, open .github/workflows/All-publish.yml and .github/workflows/KMMBridge-publish.yml and uncomment the secret PODSPEC_SSH_KEY param:

jobs:
  call-kmmbridge-publish:
    permissions:
      contents: write
      packages: write
    uses: touchlab/KMMBridgeGithubWorkflow/.github/workflows/faktorybuildautoversion.yml@autoversion
    with:
      jvmVersion: 17
      versionBaseProperty: LIBRARY_VERSION
      publishTask: # etc
    secrets:
      PODSPEC_SSH_KEY: ${{ secrets.PODSPEC_SSH_KEY }}

Setting up the CocoaPods podspec has a lot of parts, so it is more likely that things will fail here. If you get issues, make sure to come back and double-check everything in this section.

Publish

Head over to your GitHub repo in your browser, find “Actions”, and run one of the build scripts. CocoaPods is very precise about deployment. There are multiple points that could fail, and if anything does, the overall deployment will fail. Once it is set up, you should rarely have issues, if ever, but debugging your initial setup can be tricky.

If you run into issued with deploy, reach out.

CocoaPods Client Setup

You’ll need to add the podsepc source repo, then include the project. Assuming your local GitHub access is configured, you should just need to edit your Podfile.

See this doc for more detail.

platform :ios, '13.5'

source 'https://github.com/[your org]/KotlinPodspecs.git'

target '[Your iOS app target]' do
  pod 'allshared', '~> 0.1'
end

Run pod install, then open the .xcworkspace file in Xcode.