Skip to content

Commit

Permalink
feat: new ios integration section (#4382)
Browse files Browse the repository at this point in the history
  • Loading branch information
danil-pavlov authored Sep 23, 2024
1 parent 439b3c9 commit 291b4a9
Show file tree
Hide file tree
Showing 25 changed files with 373 additions and 12 deletions.
25 changes: 25 additions & 0 deletions docs/images/ios-integration/direct-integration-scheme.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions docs/images/ios-integration/ios-integration-scheme.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/multiplatform/xcode-edit-schemes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/multiplatform/xcode-spm-usage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 13 additions & 8 deletions docs/kr.tree
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,18 @@
<toc-element id="multiplatform-android-dependencies.md" toc-title="For Android target platforms"/>
<toc-element id="multiplatform-ios-dependencies.md" toc-title="For iOS target platforms"/>
</toc-element>
<toc-element toc-title="Set up iOS integration">
<toc-element id="multiplatform-ios-integration-overview.md" toc-title="Choose integration method"/>
<toc-element id="multiplatform-direct-integration.md"/>
<toc-element toc-title="Remote SPM export" id="native-spm.md"/>
<toc-element toc-title="CocoaPods integration">
<toc-element id="native-cocoapods.md"/>
<toc-element id="native-cocoapods-libraries.md"/>
<toc-element id="native-cocoapods-xcode.md"/>
<toc-element id="native-cocoapods-dsl-reference.md"/>
</toc-element>
<toc-element id="multiplatform-spm-local-integration.md" toc-title="Local integration with SPM"/>
</toc-element>
<toc-element toc-title="Compile artifacts">
<toc-element id="multiplatform-configure-compilations.md" accepts-web-file-names="mpp-configure-compilations.html"/>
<toc-element toc-title="[Experimental DSL] Build final native binaries" id="multiplatform-native-artifacts.md" hidden="true"/>
Expand Down Expand Up @@ -225,19 +237,12 @@
<toc-element id="native-objc-interop.md"/>
<toc-element id="apple-framework.md"/>
</toc-element>
<toc-element toc-title="CocoaPods integration">
<toc-element id="native-cocoapods.md"/>
<toc-element id="native-cocoapods-libraries.md"/>
<toc-element id="native-cocoapods-xcode.md"/>
<toc-element id="native-cocoapods-dsl-reference.md"/>
</toc-element>
<toc-element id="native-spm.md"/>
<toc-element id="native-libraries.md"/>
<toc-element id="native-platform-libs.md"/>
<toc-element id="native-dynamic-libraries.md"/>
<toc-element toc-title="Memory manager">
<toc-element id="native-memory-manager.md"/>
<toc-element id="native-ios-integration.md" toc-title="Integration with ARC"/>
<toc-element id="native-arc-integration.md" toc-title="Integration with ARC" accepts-web-file-names="native-ios-integration.html"/>
<toc-element id="native-migration-guide.md" toc-title="Migration guide"/>
</toc-element>
<toc-element id="native-debugging.md"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,8 @@ When you declare XCFrameworks, Kotlin Gradle plugin will register three Gradle t
* `assembleDebugXCFramework` (additionally debug artifact that contains [dSYMs](native-ios-symbolication.md))
* `assembleReleaseXCFramework`

<anchor name="build-frameworks"></anchor>

If you're using [CocoaPods integration](native-cocoapods.md) in your projects, you can build XCFrameworks with the Kotlin
CocoaPods Gradle plugin. It includes the following tasks that build XCFrameworks with all the registered targets and
generate podspec files:
Expand Down
Empty file.
88 changes: 88 additions & 0 deletions docs/topics/multiplatform/multiplatform-direct-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
[//]: # (title: Direct integration)

If you want to develop your Kotlin Multiplatform project and an iOS project simultaneously by sharing code between them,
you can set up direct integration using a special script.

This script automates the process of connecting the Kotlin framework to iOS projects in Xcode:

![Direct integration diagram](direct-integration-scheme.svg){width="700"}

The script uses the `embedAndSignAppleFrameworkForXcode` Gradle task designed specifically for the Xcode environment.
During the setup, you add it to the run script phase of the iOS app build. After that, the Kotlin artifact
is built and included in the derived data before running the iOS app build.

In general, the script:

* Copies the compiled Kotlin framework into the correct directory within the iOS project structure.
* Handles the code signing process of the embedded framework.
* Ensures that code changes in the Kotlin framework are reflected in the iOS app in Xcode.

## How to set up

If you're currently using the CocoaPods plugin to connect your Kotlin framework, migrate first.

### Migrate from CocoaPods plugin to direct integration {initial-collapse-state="collapsed"}

To migrate from the CocoaPods plugin:

1. In Xcode, clean build directories using **Product** | **Clean Build Folder** or with the
<shortcut>Cmd + Shift + K</shortcut> shortcut.
2. In the directory with the `Podfile` file, run the following command:

```none
pod deintegrate
```

3. Remove the `cocoapods {}` block from your `build.gradle(.kts)` files.
4. Delete the `.podspec` and `Podfile` files.

### Connect the framework to your project

To connect the Kotlin framework generated from the multiplatform project to your Xcode project:

1. The `embedAndSignAppleFrameworkForXcode` task only registers if the `binaries.framework` configuration option is
declared. In your Kotlin Multiplatform project, check the iOS target declaration in the `build.gradle.kts` file.
2. In Xcode, open the iOS project settings by double-clicking the project name.
3. On the **Build Phases** tab of the project settings, click **+** and select **New Run Script Phase**.

![Add run script phase](xcode-run-script-phase-1.png){width=700}

4. Adjust the following script and copy the result to the run script phase:

```bash
cd "<Path to the root of the multiplatform project>"
./gradlew :<Shared module name>:embedAndSignAppleFrameworkForXcode
```

* In the `cd` command, specify the path to the root of your Kotlin Multiplatform project, for example, `$SRCROOT/..`.
* In the `./gradlew` command, specify the name of the shared module, for example, `:shared` or `:composeApp`.

![Add the script](xcode-run-script-phase-2.png){width=700}

5. Drag the **Run Script** phase before the **Compile Sources** phase.

![Drag the Run Script phase](xcode-run-script-phase-3.png){width=700}

6. On the **Build Settings** tab, disable the **User Script Sandboxing** option under **Build Options**:

![User Script Sandboxing](disable-sandboxing-in-xcode-project-settings.png){width=700}

> This may require restarting your Gradle daemon, if you built the iOS project without disabling sandboxing first.
> Stop the Gradle daemon process that might have been sandboxed:
> ```shell
> ./gradlew --stop
> ```
>
> {type="tip"}
7. Build the project in Xcode. If everything is set up correctly, the project will successfully build.
> If you have a custom build configuration different from the default `Debug` or `Release`, on the **Build Settings**
> tab, add the `KOTLIN_FRAMEWORK_BUILD_TYPE` setting under **User-Defined** and set it to `Debug` or `Release`.
>
{type="note"}
## What's next?
You can also take advantage of local integration when working with the Swift package manager. [Learn how to add a
dependency on a Kotlin framework in a local package](multiplatform-spm-local-integration.md).
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
[//]: # (title: iOS integration methods)

You can integrate a Kotlin Multiplatform shared module into your iOS app. For that, you generate an [iOS framework](https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WhatAreFrameworks.html)
from the shared module and then add it as a dependency to the iOS project:

![iOS integration scheme](ios-integration-scheme.svg)

It's possible to consume this framework as a local or remote dependency. Choose local integration if you want to have
full control over the entire codebase and get instant updates to final applications when the common code changes.

If you want to explicitly separate the code base of your final application from the common code base, set up remote
integration. In this case, the shared code will be integrated into final applications like a regular third-party
dependency.

## Local integration

In a local setup, there are two main integration options. You can use direct integration through a special script, which
makes the Kotlin build a part of the iOS build. If you have Pod dependencies in your Kotlin Multiplatform project,
take the CocoaPods integration approach.

### Direct integration

You can connect the iOS framework directly from the Kotlin Multiplatform project by adding a special script to your Xcode
project. The script is integrated into the build phase of your project's build settings.

This integration method can work for you if you do **not** import CocoaPods dependencies in your Kotlin Multiplatform
project.

If you create a project in Android Studio, choose the **Regular framework** option to have this setup generated
automatically. If you use the [Kotlin Multiplatform web wizard](https://kmp.jetbrains.com/), direct integration
is applied by default.

For more information, see [Direct integration](multiplatform-direct-integration.md).

### CocoaPods integration with a local podspec

You can connect the iOS framework from the Kotlin Multiplatform project through [CocoaPods](https://cocoapods.org/),
a popular dependency manager for Swift and Objective-C projects.

This integration method works for you if:

* You have a mono repository setup with an iOS project that uses CocoaPods
* You import CocoaPods dependencies in your Kotlin Multiplatform project

To set up a workflow with a local CocoaPods dependency, you can either edit the scripts manually or generate the project
using a wizard in Android Studio.

For more information, see [CocoaPods overview and setup](native-cocoapods.md).

## Remote integration

For remote integration, your project might use the Swift Package Manager (SPM) or the CocoaPods dependency manager to
connect the iOS framework from a Kotlin Multiplatform project.

### Swift package manager with XCFrameworks

You can set up a Swift package manager (SPM) dependency using XCFrameworks to connect the iOS framework from the Kotlin
Multiplatform project.

For more information, see [Swift package export setup](native-spm.md).

### CocoaPods integration with XCFrameworks

You can build XCFrameworks with the Kotlin CocoaPods Gradle plugin and then distribute shared parts of your project
separately from mobile apps through CocoaPods.

For more information, see [Build final native binaries](multiplatform-build-native-binaries.md#build-frameworks).
149 changes: 149 additions & 0 deletions docs/topics/multiplatform/multiplatform-spm-local-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
[//]: # (title: Using Kotlin from local Swift packages)

In this tutorial, you'll learn how to integrate a Kotlin framework from a Kotlin Multiplatform project into a local
package using the Swift package manager (SPM).

This is a local integration method that can work for you if:

* You have an iOS app with local SPM modules.
* Your existing iOS project has a static linking type.
* You've already set up a Kotlin Multiplatform project targeting iOS on your local machine.

![Direct integration diagram](direct-integration-scheme.svg){width=700}

To set up the integration, you'll add a special script that uses the `embedAndSignAppleFrameworkForXcode` Gradle task
as a pre-action in your project's build settings. To see the changes made in common code reflected in your Xcode project,
you'll only need to rebuild the Kotlin Multiplatform project.

This way, you can easily use Kotlin code in local Swift packages, compared to a regular direct integration method,
that adds the script to the build phase and requires rebuilding both the Kotlin Multiplatform and the iOS project to get
the changes from the common code.

> If you aren't familiar with Kotlin Multiplatform, learn how to [set up the environment](https://www.jetbrains.com/help/kotlin-multiplatform-dev/multiplatform-setup.html)
> and [create a cross-platform application from scratch](https://www.jetbrains.com/help/kotlin-multiplatform-dev/multiplatform-create-first-app.html) first.
>
{type="tip"}

## Set up the project

The feature is available starting with Kotlin 2.0.0.

> To check the Kotlin version, navigate to the `build.gradle(.kts)` file in the root of your Kotlin Multiplatform project.
> You'll see the current version in the `plugins {}` block at the top of the file.
>
> Alternatively, check the version catalog in the `gradle/libs.versions.toml` file.
>
{type="tip"}

The tutorial assumes that your project is using [direct integration](multiplatform-direct-integration.md)
approach with the `embedAndSignAppleFrameworkForXcode` task in the project's build phase. If you're connecting a Kotlin
framework through CocoaPods plugin or through Swift package with `binaryTarget`, migrate first.

### Migrate from SPM binaryTarget integration to local direct integration {initial-collapse-state="collapsed"}

To migrate from the SPM integration with `binaryTarget`:

1. In Xcode, clean build directories using **Product** | **Clean Build Folder** or with the
<shortcut>Cmd + Shift + K</shortcut> shortcut.
2. In every `Package.swift` file, remove both dependencies to the package with a Kotlin framework inside and target
dependencies to the products.

### Migrate from CocoaPods plugin to direct integration {initial-collapse-state="collapsed"}

> If you have dependencies on other Pods in the `cocoapods {}` block, you have to resort to the CocoaPods integration approach.
> Currently, it's impossible to both have dependencies on Pods and on the Kotlin framework in a multimodal SPM project.
>
{type="warning"}

To migrate from the CocoaPods plugin:

1. In Xcode, clean build directories using **Product** | **Clean Build Folder** or with the
<shortcut>Cmd + Shift + K</shortcut> shortcut.
2. In the directory with `Podfile`, run the following command:

```none
pod deintegrate
```

3. Remove the `cocoapods {}` block from your `build.gradle(.kts)` files.
4. Delete the `.podspec` and `Podfile` files.

## Connect the framework to your project

> The integration into `swift build` is currently not supported.
>
{type="note"}

To be able to use Kotlin code in a local Swift package, connect the Kotlin framework generated from the multiplatform
project to your Xcode project:

1. In Xcode, go to **Product** | **Scheme** | **Edit scheme** or click the schemes icon in the top bar and select **Edit scheme**:

![Edit scheme](xcode-edit-schemes.png){width=700}

2. Select the **Build** | **Pre-actions** item, then click **+** | **New Run Script Action**:

![New run script action](xcode-new-run-script-action.png){width=700}

3. Adjust the following script and add it as an action:

```bash
cd "<Path to the root of the multiplatform project>"
./gradlew :<Shared module name>:embedAndSignAppleFrameworkForXcode
```

* In the `cd` command, specify the path to the root of your Kotlin Multiplatform project, for example, `$SRCROOT/..`.
* In the `./gradlew` command, specify the name of the shared module, for example, `:shared` or `:composeApp`.

4. Choose your app's target in the **Provide build settings from** section:

![Filled run script action](xcode-filled-run-script-action.png){width=700}

5. You can now import the shared module into your local Swift package and use Kotlin code. For example, define the following function:

```Swift
import Shared

public func greetingsFromSpmLocalPackage() -> String {
return Greeting.greet()
}
```

![SPM usage](xcode-spm-usage.png){width=700}

6. In the `ContentView.swift` file of your iOS project, you can now use this function by importing the local package:

```Swift
import SwiftUI
import SpmLocalPackage

struct ContentView: View {
var body: some View {
Vstack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text(greetingsFromSpmLocalPackage())
}
.padding()
}
}

#Preview {
ContentView()
}
```

7. Build the project in Xcode. If everything is set up correctly, the project build will be successful.

There are a couple more factors worth considering:

* If you have a custom build configuration that is different from the default `Debug` or `Release`, on the **Build Settings**
tab, add the `KOTLIN_FRAMEWORK_BUILD_TYPE` setting under **User-Defined** and set it to `Debug` or `Release`.
* If you encounter an error with script sandboxing, open the iOS project settings by double-clicking the project name,
then on the **Build Settings** tab, disable the **User Script Sandboxing** under **Build Options**.

## What's next

* [Choose your integration method](multiplatform-ios-integration-overview.md)
* [Learn how to set up Swift package export](native-spm.md)
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion docs/topics/native/native-memory-manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,4 +206,4 @@ Then, compile the test binary with the `-e testlauncher.mainBackground` compiler
## What's next

* [Migrate from the legacy memory manager](native-migration-guide.md)
* [Configure integration with iOS](native-ios-integration.md)
* [Check the specifics of integration with Swift/Objective-C ARC](native-arc-integration.md)
4 changes: 2 additions & 2 deletions docs/topics/native/native-migration-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,5 @@ To support the new memory manager, remove usages of the affected API:

## What's next

* [Learn about the new memory manager](native-memory-manager.md)
* [Configure integration with iOS](native-ios-integration.md)
* [Learn more about the new memory manager](native-memory-manager.md)
* [Check the specifics of integration with Swift/Objective-C ARC](native-arc-integration.md)
3 changes: 2 additions & 1 deletion docs/topics/native/native-objc-interop.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ Some other resources you might find useful:

* The [Kotlin-Swift interopedia](https://github.com/kotlin-hands-on/kotlin-swift-interopedia), a collection of examples
on how to use Kotlin declarations in Swift code.
* The [iOS integration](native-ios-integration.md) section, covering specifics of memory management between iOS and Kotlin.
* The [Integration with Swift/Objective-C ARC](native-arc-integration.md) section, covering the details of integration
between Kotlin's tracing GC and Objective-C's ARC.

## Usage

Expand Down

0 comments on commit 291b4a9

Please sign in to comment.