Skip to content

Commit 3920fa1

Browse files
update: SPM export doc made better
Updated structure, highlighted several common issues. --------- Co-authored-by: Aleksey.Zamulla <[email protected]>
1 parent 7f6be02 commit 3920fa1

File tree

2 files changed

+92
-47
lines changed

2 files changed

+92
-47
lines changed
355 KB
Loading

docs/topics/native/native-spm.md

Lines changed: 92 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,50 @@ Consider a Kotlin Multiplatform project that has an iOS target. You may want to
66
as a dependency to iOS developers working on native Swift projects. Using Kotlin Multiplatform tooling, you can provide
77
an artifact that would seamlessly integrate with their Xcode projects.
88

9-
This guide shows how to do this by building [XCFrameworks](multiplatform-build-native-binaries.md#build-xcframeworks)
9+
This tutorial shows how to do this by building [XCFrameworks](multiplatform-build-native-binaries.md#build-xcframeworks)
1010
with the Kotlin Gradle plugin.
1111

12-
## Prepare file locations
12+
## Set up remote integration
1313

14-
To make your framework consumable, you need to upload two files:
15-
* A ZIP archive of the XCFramework. Upload it to a convenient file storage with direct access (for example,
16-
creating a GitHub release with the archive attached, using Amazon S3 or Maven).
14+
To make your framework consumable, you'll need to upload two files:
15+
16+
* A ZIP archive with the XCFramework. You'll need to upload it to a convenient file storage with direct access (for example,
17+
creating a GitHub release with the archive attached, using Amazon S3 or Maven).
1718
Choose the option that is easiest to integrate into your workflow.
18-
* The `Package.swift` file describing the package. Prepare a Git repository and push it there.
19-
To decide how to organize your repositories, see the pros and cons of several options below.
19+
* The `Package.swift` file describing the package. You'll need to push it to a separate Git repository.
20+
21+
#### Project configuration options {initial-collapse-state="collapsed"}
2022

21-
### Swift package distribution
23+
In this tutorial, you'll store your XCFramework as a binary in your preferred file storage, and the `Package.swift` file
24+
in a separate Git repository.
2225

23-
Consider the following options for organizing your Git repositories:
26+
However, you can configure your project differently. Consider the following options for organizing Git repositories:
2427

25-
* Store the `Package.swift` file in an independent repository. This allows versioning it separately from the
26-
Kotlin Multiplatform project the file describes. This is the recommended approach: it allows scaling and generally easier
27-
to maintain.
28+
* Store the `Package.swift` file and the code that should be packaged into an XCFramework in separate Git repositories.
29+
This allows versioning the Swift manifest separately from the project the file describes. This is the recommended approach:
30+
it allows scaling and is generally easier to maintain.
2831
* Put the `Package.swift` file next to your Kotlin Multiplatform code. This is a more straightforward approach, but
29-
keep in mind in this case the Swift package and the code will use the same versioning. SPM uses
30-
Git tags for versioning packages, and this can conflict with tags used for the project.
31-
* Store the `Package.swift` within the consumer project's repository. This helps to avoid the versioning and maintenance issues.
32+
keep in mind that, in this case, the Swift package and the code will use the same versioning. SPM uses
33+
Git tags for versioning packages, which can conflict with tags used for your project.
34+
* Store the `Package.swift` file within the consumer project's repository. This helps to avoid versioning and maintenance issues.
3235
However, this approach can cause problems with multi-repository SPM setups of the consumer project and further automation:
3336

3437
* In a multi-package project, only one consumer package can depend on the external module (to avoid dependency conflicts
35-
within the project). So, all the logic that depends on your Kotlin Multiplatform module should be encapsulated in a particular consumer package.
36-
* If you publish the Kotlin Multiplatform project using an automated CI process, this process would need to include publishing the
37-
updated `Package.swift` file to the consumer repository. Such a phase in CI can be difficult to maintain as it may lead
38-
to conflicting updates of the consumer repository.
38+
within the project). So, all the logic that depends on your Kotlin Multiplatform module should be encapsulated in a
39+
particular consumer package.
40+
* If you publish the Kotlin Multiplatform project using an automated CI process, this process would need to include
41+
publishing the updated `Package.swift` file to the consumer repository. This may lead to conflicting updates of the
42+
consumer repository and so such a phase in CI can be difficult to maintain.
43+
44+
### Configure your multiplatform project
3945

40-
## Create the XCFramework and the Swift package manifest
46+
In the following example, the shared code of a Kotlin Multiplatform project is stored locally in the `shared` module.
47+
If your project is structured differently, substitute "shared" in code and path examples with your module's name.
4148

42-
> The following example assumes that the shared code of your Kotlin Multiplatform project is stored in the `shared` module.
43-
> If your project is structured differently, substitute "shared" in code and path examples with your module's name.
44-
>
45-
{type="tip"}
49+
To set up the publishing of an XCFramework:
50+
51+
1. Update your `shared/build.gradle.kts` configuration file with the `XCFramework` call in the iOS targets list:
4652

47-
To provide a Swift package:
48-
1. Set up the publishing of an [XCFramework](multiplatform-build-native-binaries.md#build-xcframeworks). Add the `XCFramework`
49-
call to your iOS targets description in the `shared/build.gradle.kts` file:
5053
```kotlin
5154
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework
5255

@@ -81,12 +84,22 @@ call to your iOS targets description in the `shared/build.gradle.kts` file:
8184

8285
The resulting framework will be created as the `shared/build/XCFrameworks/release/Shared.xcframework` folder in your project directory.
8386

84-
3. Put the `Shared.xcframework` folder in a ZIP archive and calculate the checksum for the resulting archive, for example:
87+
> In case you work with a Compose Multiplatform project, use the following Gradle task:
88+
>
89+
> `./gradlew :composeApp:assembleSharedReleaseXCFramework`
90+
>
91+
> You can then find the resulting framework in the `composeApp/build/XCFrameworks/release/Shared.xcframework` folder.
92+
>
93+
{type="tip"}
94+
95+
### Prepare the XCFramework and the Swift package manifest
96+
97+
1. Put the `Shared.xcframework` folder in a ZIP archive and calculate the checksum for the resulting archive, for example:
8598

8699
`swift package compute-checksum Shared.xcframework.zip`
87100

88-
4. <anchor name="upload"></anchor> Upload the ZIP file to the file storage of your choice.
89-
5. Create a `Package.swift` file with the following code:
101+
2. <anchor name="upload"></anchor> Upload the ZIP file to the file storage of your choice.
102+
3. Choose any directory and locally create a `Package.swift` file with the following code:
90103

91104
```Swift
92105
// swift-tools-version:5.3
@@ -109,39 +122,71 @@ call to your iOS targets description in the `shared/build.gradle.kts` file:
109122
)
110123
```
111124

112-
6. Validate the manifest.
113-
One way to do this is to run the following shell command in the directory with the `Package.swift` file:
125+
4. In the `url` field, specify the link to your ZIP archive with the XCFramework.
126+
5. [Optional] If you'd like to validate the resulting manifest, you can run the following shell command in the directory
127+
with the `Package.swift` file:
114128

115129
```shell
116130
swift package reset && swift package show-dependencies --format json
117131
```
118132

119-
The output will describe any found errors, or show the successful download and parsing result if the manifest is correct.
133+
The output will describe any errors found or show the successful download and parsing result if the manifest is correct.
120134

121-
7. Push the `Package.swift` file to the repository you settled on earlier. Make sure to create and push a git tag with the
122-
semantic version of the package.
135+
6. Push the `Package.swift` file to your remote repository. Make sure to create and push a Git tag with the
136+
semantic version of the package.
123137

124-
Now that both files are accessible, you can test the import in Xcode:
138+
### Add the package dependency
139+
140+
Now that both files are accessible, you can add the package dependency:
141+
142+
1. In Xcode, choose **File | Add Package Dependencies**.
143+
2. In the search field, enter the URL of the Git repository with the `Package.swift` file inside:
144+
145+
![Specify repo with the package file](native-spm-url.png)
125146

126-
1. Choose **File | Add Package Dependencies...**
127-
2. Provide the Git URL for the repository with the `Package.swift` file.
128147
3. Depending on the type of your project, the dialog will vary:
129148
* If you're making a Swift package, press the **Copy package** button. This will put a `.package` line in your clipboard.
130149
Paste this line into the [Package.Dependency](https://developer.apple.com/documentation/packagedescription/package/dependency)
131150
block of your own `Package.swift` file, and add the necessary product to the appropriate `Target.Dependency` block.
132151
* For other Xcode projects, press the **Add package** button, then select products and corresponding targets for the package.
133152
153+
### Check your setup
154+
155+
To check that everything is set up correctly, test the import in Xcode:
156+
157+
1. In your project, navigate to the `ContentView.swift` file.
158+
2. Replace the code with the following snippet:
159+
160+
```Swift
161+
import SwiftUI
162+
import Shared
163+
164+
struct ContentView: View {
165+
var body: some View {
166+
VStack {
167+
Image(systemName: "globe")
168+
.imageScale(.large)
169+
.foregroundStyle(.tint)
170+
Text("Hello, world! \(Shared.Platform_iosKt.getPlatform().name)")
171+
}
172+
.padding()
173+
}
174+
}
175+
176+
#Preview {
177+
ContentView()
178+
}
179+
```
180+
181+
Here, you import the `Shared` XCFramework and then use it to obtain the platform name in the `Text` field.
182+
183+
3. Ensure that the preview is updated with the new text.
184+
134185
## Exporting multiple modules as an XCFramework
135186
136187
To make code from several Kotlin Multiplatform modules available as an iOS binary, combine these modules in a single
137188
umbrella module. Then, build and export the XCFramework of this umbrella module.
138189
139-
<!--TODO remove this note when https://youtrack.jetbrains.com/issue/KT-66565 is fixed-->
140-
141-
> The name `umbrella` is reserved in Apple development. Don't use it for the module you are exporting.
142-
>
143-
{type="note"}
144-
145190
For example, you have a `network` and a `database` module, which you combine in an `together` module:
146191
147192
1. In the `together/build.gradle.kts` file, specify dependencies and the framework configuration:
@@ -203,8 +248,8 @@ For example, you have a `network` and a `database` module, which you combine in
203248
./gradlew :together:assembleTogetherReleaseXCFramework
204249
```
205250
206-
5. Follow steps 4–7 from [the previous section](#upload) for `together.xcframework`: archive, calculate the checksum, upload
207-
the archived XCFramework, create and push a `Package.swift` file.
251+
5. Follow steps 4–7 from [the previous section](#upload) for `together.xcframework`: archive, calculate the checksum,
252+
upload the archived XCFramework, create and push a `Package.swift` file.
208253
209-
Now you can import the dependency into an Xcode project. After adding the `import together` directive,
254+
Now, you can import the dependency into an Xcode project. After adding the `import together` directive,
210255
you should have classes from both the `network` and `database` modules available for import in Swift code.

0 commit comments

Comments
 (0)