Skip to content

Commit e4b0844

Browse files
authored
Adding Swift Package Manager support due to CocoaPods deprecation (#4623)
Initial work towards #4622
1 parent c7cb359 commit e4b0844

16 files changed

Lines changed: 960 additions & 93 deletions

File tree

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
name: Test iOS packaging
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- '.github/workflows/ios-packaging.yml'
7+
- 'maven/codenameone-maven-plugin/**'
8+
- 'vm/ByteCodeTranslator/**'
9+
- 'scripts/build-ios-app.sh'
10+
- 'scripts/run-ios-ui-tests.sh'
11+
- 'scripts/run-ios-native-tests.sh'
12+
- 'scripts/ios/**'
13+
- 'scripts/hellocodenameone/**'
14+
- '!docs/**'
15+
push:
16+
branches: [ master ]
17+
paths:
18+
- '.github/workflows/ios-packaging.yml'
19+
- 'maven/codenameone-maven-plugin/**'
20+
- 'vm/ByteCodeTranslator/**'
21+
- 'scripts/build-ios-app.sh'
22+
- 'scripts/run-ios-ui-tests.sh'
23+
- 'scripts/run-ios-native-tests.sh'
24+
- 'scripts/ios/**'
25+
- 'scripts/hellocodenameone/**'
26+
- '!docs/**'
27+
28+
jobs:
29+
packaging-matrix:
30+
permissions:
31+
contents: read
32+
runs-on: macos-15
33+
timeout-minutes: 75
34+
strategy:
35+
fail-fast: false
36+
matrix:
37+
packaging:
38+
- name: pods-only
39+
args: >-
40+
-Dcodename1.arg.ios.dependencyManager=cocoapods
41+
-Dcodename1.arg.ios.pods=AFNetworking
42+
- name: spm-only
43+
args: >-
44+
-Dcodename1.arg.ios.dependencyManager=spm
45+
-Dcodename1.arg.ios.spm.packages=swift-collections|https://github.com/apple/swift-collections.git|from:1.1.0
46+
-Dcodename1.arg.ios.spm.products.swift-collections=Collections
47+
- name: both
48+
args: >-
49+
-Dcodename1.arg.ios.dependencyManager=both
50+
-Dcodename1.arg.ios.pods=AFNetworking
51+
-Dcodename1.arg.ios.spm.packages=swift-collections|https://github.com/apple/swift-collections.git|from:1.1.0
52+
-Dcodename1.arg.ios.spm.products.swift-collections=Collections
53+
54+
steps:
55+
- uses: actions/checkout@v4
56+
57+
- name: Ensure CocoaPods tooling
58+
run: |
59+
mkdir -p ~/.codenameone
60+
cp maven/UpdateCodenameOne.jar ~/.codenameone/
61+
set -euo pipefail
62+
GEM_USER_DIR="$(ruby -e 'print Gem.user_dir')"
63+
export PATH="$GEM_USER_DIR/bin:$PATH"
64+
gem install cocoapods xcodeproj --no-document --user-install
65+
pod --version
66+
67+
- name: Compute setup-workspace hash
68+
id: setup_hash
69+
run: |
70+
set -euo pipefail
71+
echo "hash=$(shasum -a 256 scripts/setup-workspace.sh | awk '{print $1}')" >> "$GITHUB_OUTPUT"
72+
73+
- name: Set TMPDIR
74+
run: echo "TMPDIR=${{ runner.temp }}" >> $GITHUB_ENV
75+
76+
- name: Cache codenameone-tools
77+
uses: actions/cache@v4
78+
with:
79+
path: ${{ runner.temp }}/codenameone-tools
80+
key: ${{ runner.os }}-cn1-tools-${{ steps.setup_hash.outputs.hash }}
81+
restore-keys: |
82+
${{ runner.os }}-cn1-tools-
83+
84+
- name: Restore cn1-binaries cache
85+
uses: actions/cache@v4
86+
with:
87+
path: ../cn1-binaries
88+
key: cn1-binaries-${{ runner.os }}-${{ steps.setup_hash.outputs.hash }}
89+
restore-keys: |
90+
cn1-binaries-${{ runner.os }}-
91+
92+
- name: Setup workspace
93+
run: ./scripts/setup-workspace.sh -q -DskipTests
94+
timeout-minutes: 40
95+
96+
- name: Build iOS port
97+
run: ./scripts/build-ios-port.sh -q -DskipTests
98+
timeout-minutes: 40
99+
100+
- name: Build sample iOS app
101+
id: build_ios_app
102+
env:
103+
IOS_DEPENDENCY_ARGS: ${{ matrix.packaging.args }}
104+
run: ./scripts/build-ios-app.sh -q -DskipTests
105+
timeout-minutes: 30
106+
107+
- name: Run iOS UI smoke
108+
env:
109+
ARTIFACTS_DIR: ${{ github.workspace }}/artifacts/${{ matrix.packaging.name }}
110+
run: |
111+
set -euo pipefail
112+
mkdir -p "${ARTIFACTS_DIR}"
113+
./scripts/run-ios-ui-tests.sh \
114+
"${{ steps.build_ios_app.outputs.workspace }}" \
115+
"" \
116+
"${{ steps.build_ios_app.outputs.scheme }}"
117+
timeout-minutes: 30
118+
119+
- name: Run native iOS notification tests
120+
if: matrix.packaging.name == 'both'
121+
env:
122+
ARTIFACTS_DIR: ${{ github.workspace }}/artifacts/${{ matrix.packaging.name }}-native
123+
run: |
124+
set -euo pipefail
125+
mkdir -p "${ARTIFACTS_DIR}"
126+
./scripts/run-ios-native-tests.sh \
127+
"${{ steps.build_ios_app.outputs.workspace }}" \
128+
"${{ steps.build_ios_app.outputs.scheme }}"
129+
timeout-minutes: 20
130+
131+
- name: Upload packaging artifacts
132+
if: always()
133+
uses: actions/upload-artifact@v4
134+
with:
135+
name: ios-packaging-${{ matrix.packaging.name }}
136+
path: artifacts
137+
if-no-files-found: warn
138+
retention-days: 14
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
name: Test iOS native test scripts
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- '.github/workflows/scripts-ios-native.yml'
7+
- 'scripts/setup-workspace.sh'
8+
- 'scripts/build-ios-port.sh'
9+
- 'scripts/build-ios-app.sh'
10+
- 'scripts/run-ios-native-tests.sh'
11+
- 'scripts/ios/create-shared-scheme.py'
12+
- 'scripts/ios/notification-tests/native-tests/**'
13+
- 'scripts/ios/notification-tests/install-native-notification-tests.sh'
14+
- 'scripts/ios/notification-tests/**'
15+
- 'scripts/hellocodenameone/**'
16+
- 'scripts/templates/**'
17+
- '!scripts/templates/**/*.md'
18+
- 'CodenameOne/src/**'
19+
- '!CodenameOne/src/**/*.md'
20+
- 'Ports/iOSPort/**'
21+
- '!Ports/iOSPort/**/*.md'
22+
- 'vm/**'
23+
- '!vm/**/*.md'
24+
- 'tests/**'
25+
- '!tests/**/*.md'
26+
- '!docs/**'
27+
- 'maven/**'
28+
- '!maven/core-unittests/**'
29+
push:
30+
branches: [ master ]
31+
paths:
32+
- '.github/workflows/scripts-ios-native.yml'
33+
- 'scripts/setup-workspace.sh'
34+
- 'scripts/build-ios-port.sh'
35+
- 'scripts/build-ios-app.sh'
36+
- 'scripts/run-ios-native-tests.sh'
37+
- 'scripts/ios/create-shared-scheme.py'
38+
- 'scripts/ios/notification-tests/native-tests/**'
39+
- 'scripts/ios/notification-tests/install-native-notification-tests.sh'
40+
- 'scripts/ios/notification-tests/**'
41+
- 'scripts/hellocodenameone/**'
42+
- 'scripts/templates/**'
43+
- '!scripts/templates/**/*.md'
44+
- 'CodenameOne/src/**'
45+
- '!CodenameOne/src/**/*.md'
46+
- 'Ports/iOSPort/**'
47+
- '!Ports/iOSPort/**/*.md'
48+
- 'vm/**'
49+
- '!vm/**/*.md'
50+
- 'tests/**'
51+
- '!tests/**/*.md'
52+
- '!docs/**'
53+
- 'maven/**'
54+
- '!maven/core-unittests/**'
55+
56+
jobs:
57+
native-ios:
58+
permissions:
59+
contents: read
60+
runs-on: macos-15
61+
timeout-minutes: 65
62+
concurrency:
63+
group: mac-ci-${{ github.workflow }}-${{ github.ref_name }}
64+
cancel-in-progress: true
65+
66+
steps:
67+
- uses: actions/checkout@v4
68+
69+
- name: Ensure CocoaPods tooling
70+
run: |
71+
mkdir -p ~/.codenameone
72+
cp maven/UpdateCodenameOne.jar ~/.codenameone/
73+
set -euo pipefail
74+
if ! command -v ruby >/dev/null; then
75+
echo "ruby not found"; exit 1
76+
fi
77+
GEM_USER_DIR="$(ruby -e 'print Gem.user_dir')"
78+
export PATH="$GEM_USER_DIR/bin:$PATH"
79+
gem install cocoapods xcodeproj --no-document --user-install
80+
pod --version
81+
82+
- name: Compute setup-workspace hash
83+
id: setup_hash
84+
run: |
85+
set -euo pipefail
86+
echo "hash=$(shasum -a 256 scripts/setup-workspace.sh | awk '{print $1}')" >> "$GITHUB_OUTPUT"
87+
88+
- name: Set TMPDIR
89+
run: echo "TMPDIR=${{ runner.temp }}" >> $GITHUB_ENV
90+
91+
- name: Cache codenameone-tools
92+
uses: actions/cache@v4
93+
with:
94+
path: ${{ runner.temp }}/codenameone-tools
95+
key: ${{ runner.os }}-cn1-tools-${{ steps.setup_hash.outputs.hash }}
96+
restore-keys: |
97+
${{ runner.os }}-cn1-tools-
98+
99+
- name: Restore cn1-binaries cache
100+
uses: actions/cache@v4
101+
with:
102+
path: ../cn1-binaries
103+
key: cn1-binaries-${{ runner.os }}-${{ steps.setup_hash.outputs.hash }}
104+
restore-keys: |
105+
cn1-binaries-${{ runner.os }}-
106+
107+
- name: Setup workspace
108+
run: ./scripts/setup-workspace.sh -q -DskipTests
109+
timeout-minutes: 40
110+
111+
- name: Build iOS port
112+
run: ./scripts/build-ios-port.sh -q -DskipTests
113+
timeout-minutes: 40
114+
115+
- name: Build sample iOS app
116+
id: build_ios_app
117+
env:
118+
IOS_UISCENE: "false"
119+
run: ./scripts/build-ios-app.sh -q -DskipTests
120+
timeout-minutes: 30
121+
122+
- name: Run native iOS notification tests (XCTest)
123+
env:
124+
ARTIFACTS_DIR: ${{ github.workspace }}/artifacts/native-ios-tests
125+
run: |
126+
set -euo pipefail
127+
mkdir -p "${ARTIFACTS_DIR}"
128+
./scripts/run-ios-native-tests.sh \
129+
"${{ steps.build_ios_app.outputs.workspace }}" \
130+
"${{ steps.build_ios_app.outputs.scheme }}"
131+
timeout-minutes: 25
132+
133+
- name: Upload native iOS artifacts
134+
if: always()
135+
uses: actions/upload-artifact@v4
136+
with:
137+
name: ios-native-tests
138+
path: artifacts
139+
if-no-files-found: warn
140+
retention-days: 14

.github/workflows/scripts-ios.yml

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Test iOS build scripts
1+
name: Test iOS UI build scripts
22

33
on:
44
pull_request:
@@ -127,11 +127,11 @@ jobs:
127127
- name: Setup workspace
128128
run: ./scripts/setup-workspace.sh -q -DskipTests
129129
# per-step timeout
130-
timeout-minutes: 25
130+
timeout-minutes: 40
131131

132132
- name: Build iOS port
133133
run: ./scripts/build-ios-port.sh -q -DskipTests
134-
timeout-minutes: 25
134+
timeout-minutes: 40
135135

136136
- name: Build sample iOS app and compile workspace (UIScene on)
137137
id: build-ios-app-scene
@@ -179,17 +179,6 @@ jobs:
179179
"${{ steps.build-ios-app.outputs.scheme }}"
180180
timeout-minutes: 30
181181

182-
- name: Run native iOS notification tests (XCTest)
183-
env:
184-
ARTIFACTS_DIR: ${{ github.workspace }}/artifacts
185-
run: |
186-
set -euo pipefail
187-
mkdir -p "${ARTIFACTS_DIR}"
188-
./scripts/run-ios-native-tests.sh \
189-
"${{ steps.build-ios-app.outputs.workspace }}" \
190-
"${{ steps.build-ios-app.outputs.scheme }}"
191-
timeout-minutes: 20
192-
193182
- name: Upload iOS artifacts
194183
if: always()
195184
uses: actions/upload-artifact@v4

docs/developer-guide/Advanced-Topics-Under-The-Hood.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ Codename One supports debugging applications on devices by using the natively ge
645645

646646
In iOS this is usually strait forward, just open the project with xcode and run it optionally disabling bitcode. Unzip the .bz2 file and open the `.xcworkspace` file if it's available otherwise open the `.xcodeproj` file inside the `dist` directory.
647647

648-
IMPORTANT: Only the `.xcworkspace` if it is there, it is activated by the CocoaPods build pipeline so it won't always be there
648+
IMPORTANT: The `.xcworkspace` is no longer exclusive to CocoaPods-based builds. Use it whenever it is generated, whether the project uses CocoaPods, Swift Package Manager, or both.
649649

650650
With Android Studio this is sometimes as very easy task as it is possible to actually open the gradle project in Android Studio and just run it. However, due to the fragile nature of the gradle project this stopped working for some builds and has been "flaky".
651651

docs/developer-guide/Working-With-iOS.asciidoc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ However, it seems that Apple will reject your app if you just include that and d
192192

193193
=== Using Cocoapods
194194

195+
NOTE: CocoaPods remains fully supported, but it is no longer the only supported iOS dependency path. Swift Package Manager (SPM) is also supported. The current guidance is documented in this section.
196+
195197
https://cocoapods.org/[CocoaPods] is a dependency manager for Swift and Objective-C Cocoa projects. It has over eighteen thousand libraries and can help you scale your projects elegantly. Cocoapods can be used in your Codename One project to include native iOS libraries without having to go through the hassle of bundling the actual library into your project. Rather than bundling .h and .a files in your ios/native directory, you can specify which "pods" your app uses via the `ios.pods` build hint. (There are other build hints also if you need more advanced features).
196198

197199
**Examples**
@@ -252,6 +254,22 @@ ios.pods=GoogleMaps
252254

253255
(Note that the `ios.pods.sources` directive is optional).
254256

257+
=== Using Swift Package Manager
258+
259+
Swift Package Manager can be used as an alternative to CocoaPods for remote Swift package dependencies on iOS. Select the dependency path with `ios.dependencyManager`. Supported values are `auto`, `cocoapods`, `spm`, and `both`.
260+
261+
For an SPM-only configuration, declare the packages in `ios.spm.packages` using the format `<identity>|<url>|<requirement>` and then declare the products to link using `ios.spm.products.<identity>`.
262+
263+
----
264+
ios.dependencyManager=spm
265+
ios.spm.packages=swift-collections|https://github.com/apple/swift-collections.git|from:1.1.0
266+
ios.spm.products.swift-collections=Collections
267+
----
268+
269+
Supported requirement formats are `from:`, `exact:`, `branch:`, `revision:`, and `range:`.
270+
271+
`ios.dependencyManager=auto` preserves backward compatibility. Existing projects with only `ios.pods` continue to use CocoaPods. Projects with only `ios.spm.*` use SPM. If both hint families are present, both are applied.
272+
255273
=== Including Dynamic Frameworks
256274

257275
If you need to use a dynamic framework (e.g. SomeThirdPartySDK.framework), and it isn't available via cocoapods, then you can add it to your project by simply zipping up the framework and copying it to your native/ios directory.

docs/website/content/blog/cocoapods.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ author: Steve Hannah
1111

1212
![Header Image](/blog/cocoapods/cocoapods.png)
1313

14+
_Editor note: CocoaPods remains supported, but current iOS dependency guidance now also covers Swift Package Manager (SPM) and mixed CocoaPods/SPM setups. See the current "Working with iOS" section in the developer guide._
15+
1416
[CocoaPods](https://cocoapods.org/) is a dependency manager for Swift and Objective-C Cocoa projects.
1517
It has over eighteen thousand libraries and can help you scale your projects elegantly. Cocoapods can be
1618
used in your Codename One project to include native iOS libraries without having to go through the hassle

docs/website/content/blog/tip-use-ios-cocoapods-dependencies-native-code.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ author: Shai Almog
1111

1212
![Header Image](/blog/tip-use-ios-cocoapods-dependencies-native-code/tip.jpg)
1313

14+
_Editor note: This post is still valid for CocoaPods, but current iOS dependency setup also supports Swift Package Manager (SPM). See the current "Working with iOS" section in the developer guide._
15+
1416
Last week I talked about [using gradle dependencies](/blog/tip-use-android-gradle-dependencies-native-code.html) to build native code, this week I’ll talk about the iOS equivalent: CocoaPods. We’ve [discussed CocoaPods before](/blog/cocoapods.html) but this bares repeating especially in the context of a specific cn1lib like [intercom](/blog/intercom-support.html).
1517

1618
CocoaPods allow us to add a native library dependency to iOS far more easily than Gradle. However, I did run into a caveat with target OS versioning. By default we target iOS 7.0 or newer which is supported by Intercom only for older versions of the library. Annoyingly CocoaPods seemed to work, to solve this we had to explicitly define the build hint `ios.pods.platform=8.0` to force iOS 8 or newer.

docs/website/content/blog/you-can-now-build-android-and-ios-apps-locally.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ These are unresolved questions, but we are poised to find out their answers, as
2828

2929
### iOS Builds
3030

31-
The Local iOS build target generates an Xcode project that you can open and build directly in Xcode. This target necessarily requires a Mac with Xcode and cocoapods installed. See [Building for iOS](https://shannah.github.io/cn1-maven-archetypes/cn1app-archetype-tutorial/getting-started.html#ios) from [this tutorial](https://shannah.github.io/cn1-maven-archetypes/cn1app-archetype-tutorial/getting-started.html) for more information.
31+
The Local iOS build target generates an Xcode project that you can open and build directly in Xcode. This target requires a Mac with Xcode installed. CocoaPods is only required for CocoaPods-based builds; projects that use Swift Package Manager use the normal Apple toolchain instead. See the current "Working with iOS" section in the developer guide and [Building for iOS](https://shannah.github.io/cn1-maven-archetypes/cn1app-archetype-tutorial/getting-started.html#ios) for more information.
3232

3333
![intellij-build-ios-project](https://www.codenameone.com/wp-content/uploads/2021/04/intellij-build-ios-project.png)
3434

0 commit comments

Comments
 (0)