· 6 min read Posted by Kevin Schildhorn

Local or Remote? How you add your KMP framework matters

Do you actually need to use SPM or Cocoapods to include KMP? You don't, and it might not be the right answer
Photo by Tim Mossholder on Unsplash (https://unsplash.com/photos/shallow-focus-photo-of-thank-you-for-shopping-signage-qvWnGmoTbik)
Credit: Photo by Tim Mossholder on Unsplash (https://unsplash.com/photos/shallow-focus-photo-of-thank-you-for-shopping-signage-qvWnGmoTbik)

When you create an iOS framework using Kotlin Multiplatform your first instinct might be to use Swift Package Manager, or SPM, to link your framework, but is that the best approach?

There’s a few ways you can add a KMP framework to your XCode project. You could use a dependency manager like SPM or CocoaPods or just directly embed it locally.

You may just want to take the easy route and add it to your existing dependency manager, but really think it over:

Should you really use a dependency manager? What are the pros and cons?

iOS developers usually use SPM for framework linking as it’s made by Apple and it’s the industry standard, but that’s not always the best approach for a locally developed KMP framework.

While it’s convenient to just use the dependency manager there’s actually a lot of benefits to directly integrating with a local instance of the framework. In this article we’ll go over those benefits, and times when you may need to resort to SPM.

For local KMP development, Touchlab’s recommendation is to use direct linking when possible. Xcode and the Kotlin compiler will automatically configure the correct build settings for you. Most iOS teams use SPM for dependencies, but local KMP development with SPM requires manual config and build steps that are best avoided.

Get Started

If you’re convinced that you’d like to try direct linking, follow our tutorial here.

Why Local?

You may already be using CocoaPods or SPM in your project, which would make them a tempting option to use. Before you get ahead of yourself, there are some basic questions you should be asking yourself:

  • What is the benefit of using a remote dependency manager?
  • Are you publishing any binaries?
  • Do you need the framework to be accessible remotely?

While you may want to use a dependency manager, if you’re not publishing any binaries then you probably don’t need it. For local Kotlin builds, CocoaPods or SPM are only being used to build and link the local framework.

KMP has official support for using CocoaPods for local builds. If you are using CocoaPods for all of your iOS dependencies, local KMP builds with CocoaPods does make sense.

SPM configuration is less flexible. There is no “official” KMP support for local builds, largely because SPM doesn’t support the needed extension points.

For local KMP builds, if you’re not already using CocoaPods in your app, we recommend direct linking. Publishing dependencies for other developers and projects to use is generally what dependency managers are for.

Dependency Managers are meant for external use, to share a library with many projects that anyone can just grab.

When a library developer adds their library to a manager, like SwiftyJSON in CocoaPods, that’s a library that’s meant to be used by many projects. Your shared code framework is most likely just an extension of your apps logic. It’s only used in your project and is tightly coupled to it, so it doesn’t really make sense to have it be remotely distributed unless it’s used in multiple projects.

That being said if you are planning on publishing a Kotlin Multiplatform library that’s meant to be used by various projects, such as a common networking layer, then it makes sense to use a dependency manager.

Benefits of Direct Integration

Simplicity

While adding a depedency to CocoaPods may feel more simple to us developers, there’s actually more going on in gradle to build for CocoaPods. With CocoaPods there are some gradle tasks that need to be run before opening XCode. Additionally if we directly integrate then we don’t need to include the CocoaPods library and setup for cocoapods in gradle, so the gradle script is cleaner.

Granularity

With a dependency manager you reference your framework, maybe set a version, and that’s about it. While this is easier for developers that means there’s no granularity in what you’re building. There’s a lot of different architectures involved in iOS development, so when you build with CocoaPods you’re creating an artifact that’s meant to be universal. This is good for a large library like Alamofire but for your framework that is only used by your XCode project you want something specific to what you’re building.

We just did this in droidcon, changing from CocoaPods to direct integration for this reason. With this change we are calling ./gradlew :ios:embedAndSignAppleFrameworkForXcode which automatically builds the correct architecture and build type for our app. From the Jetbrains blog:

[embedAndSignAppleFrameworkForXcode] uses the configuration of the iOS application project to define the build mode (debug or release, device or simulator target) and provides the appropriate framework version to the specified location. The Xcode project is configured so that the task executes upon each build to provide the latest version of the framework for the iOS application.

With this change there is no mental overhead of learning what tasks to run and when. Gradle takes care of all the details and we can simply have this called on in a build script.

CI

CI can already be a tricky thing to setup, and adding remote dependencies can make it harder. The official docs mention how you have to be specific with your pod file when using git, and all the possible issues you may face with Cocoapods. These can be avoided by just using a direct integration. By referencing locally you avoid the risks of something going wrong during the setup and pod calls, and make the CI process more stable.

Support

As of this writing Swift Package Manager is not officially supported by Kotlin Multiplatform. It is possible to add your framework using SPM, but not having official support makes it less reliable of an option. Kotlin Multiplatform does officially support CocoaPods, but that could change in the future. With local dependencies you don’t have to worry about which methods are supported by KMP, or updating with newer versions.

What if I need Remote Access?

If you need remote access then of course use CocoaPods for your framework. As mentioned earlier sometimes this is needed if you have a large team with many modules, or need to share code across many projects.

What about Kotlin Multiplatform and SPM? Well we don’t recommend going this route. SPM is not officially supported by KMP, however if you need to use SPM for remote access there is one tool available.

KMMBridge

KMMBridge is a set of Gradle tooling that facilitates publishing and consuming pre-built Kotlin Multiplatform Xcode Framework binaries. It’s something we’ve worked on at Touchlab to support publishing KMP libraries that can be consumed by either CocoaPods or Swift Package Manager. You can read about it more on the official site but in short it published the framework to an online storage which can then be referenced by remote dependency managers.

So while KMMBridge is a great tool for remote access to frameworks, our recommendation still stands that most frameworks for single projects should be added locally. As mentioned above it helps simplify CI builds, has support for building efficiently, and doesn’t require any overhead when adding it to your project.