Skip to content

Commit c8fb8cb

Browse files
committed
fix: resolve Crashlytics symbol linking and update iOS deployment target
- Bump application version to 8.5.7 across Android, Desktop, and iOS targets. - Raise and synchronize the minimum iOS deployment target to 15.0 across Kotlin Multiplatform, SwiftPM, and Xcode configurations. - Configure the iOS host executable to force-link and export Firebase Crashlytics symbols using `-Wl,-export_dynamic` and `-Wl,-u` linker flags. - Add `CrashlyticsDynamicSymbols.txt` and configure Xcode `STRIPFLAGS` to preserve required symbols when stripping the archive. - Implement a GitHub Actions script `.github/scripts/verify_ios_crashlytics_symbols.sh` to verify exported symbols in the IPA during the CI build process. - Update documentation and changelog to reflect the iOS 15.0 requirement and symbol export fixes.
1 parent fa54cd1 commit c8fb8cb

20 files changed

Lines changed: 132 additions & 28 deletions

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env bash
2+
3+
set -euo pipefail
4+
5+
ipa_path="${1:-app/iosApp/iosApp.ipa}"
6+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
required_symbols_file="$script_dir/../../app/iosApp/iosApp/CrashlyticsDynamicSymbols.txt"
8+
9+
if [[ ! -f "$ipa_path" ]]; then
10+
echo "error: IPA not found: $ipa_path" >&2
11+
exit 1
12+
fi
13+
14+
if [[ ! -f "$required_symbols_file" ]]; then
15+
echo "error: Required symbols file not found: $required_symbols_file" >&2
16+
exit 1
17+
fi
18+
19+
verification_dir="$(mktemp -d "${TMPDIR:-/tmp}/notedelight-ios-symbols.XXXXXX")"
20+
trap 'rm -rf -- "$verification_dir"' EXIT
21+
22+
unzip -q "$ipa_path" -d "$verification_dir"
23+
24+
payload_dir="$verification_dir/Payload"
25+
if [[ ! -d "$payload_dir" ]]; then
26+
echo "error: Payload directory not found in IPA: $ipa_path" >&2
27+
exit 1
28+
fi
29+
30+
app_bundle="$(find "$payload_dir" -maxdepth 1 -type d -name '*.app' -print -quit)"
31+
if [[ -z "$app_bundle" ]]; then
32+
echo "error: App bundle not found in IPA: $ipa_path" >&2
33+
exit 1
34+
fi
35+
36+
executable_name="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleExecutable' "$app_bundle/Info.plist")"
37+
app_executable="$app_bundle/$executable_name"
38+
if [[ ! -f "$app_executable" ]]; then
39+
echo "error: App executable not found: $app_executable" >&2
40+
exit 1
41+
fi
42+
43+
exported_symbols="$verification_dir/exported-symbols.txt"
44+
xcrun dyld_info -exports "$app_executable" |
45+
awk 'NF == 2 && $1 ~ /^0x/ { print $2 }' > "$exported_symbols"
46+
47+
required_symbols=()
48+
while IFS= read -r symbol; do
49+
if [[ -n "$symbol" ]]; then
50+
required_symbols+=("$symbol")
51+
fi
52+
done < "$required_symbols_file"
53+
54+
if ((${#required_symbols[@]} == 0)); then
55+
echo "error: Required symbols file is empty: $required_symbols_file" >&2
56+
exit 1
57+
fi
58+
59+
missing_symbols=0
60+
for symbol in "${required_symbols[@]}"; do
61+
if ! grep -Fqx "$symbol" "$exported_symbols"; then
62+
echo "error: Required Crashlytics symbol is not exported: $symbol" >&2
63+
missing_symbols=1
64+
fi
65+
done
66+
67+
if ((missing_symbols != 0)); then
68+
exit 1
69+
fi
70+
71+
echo "Verified Crashlytics symbols in $ipa_path"

.github/workflows/ios.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ jobs:
4848
- name: AppStore build lane with gym
4949
working-directory: ./app/iosApp
5050
run: fastlane appstore_build_gym_lane
51+
- name: Verify Crashlytics symbols in App Store IPA
52+
run: ./.github/scripts/verify_ios_crashlytics_symbols.sh app/iosApp/iosApp.ipa
5153
- name: AppStore publish lane with deliver
5254
working-directory: ./app/iosApp
5355
run: fastlane appstore_publish_deliver_lane

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [8.5.7] - 2026-07-29
9+
10+
### Bug Fixes
11+
- Preserve and export Firebase Crashlytics symbols from the iOS host executable so App Store builds can load the dynamic Kotlin framework at launch
12+
13+
### Chores
14+
- Verify required Crashlytics symbols in the generated IPA before App Store publication
15+
- Raise and synchronize the minimum iOS deployment target to 15.0 across Kotlin Multiplatform, SwiftPM, and Xcode configurations
16+
817
## [8.5.6] - 2026-07-28
918

1019
### Features

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ NoteDelight is a **Kotlin Multiplatform** note-taking application with database
2828
### Supported Platforms
2929

3030
- ✅ Android (minSdk 23)
31-
- ✅ iOS (14.0+)
31+
- ✅ iOS (15.0+)
3232
- ✅ Desktop (Windows, macOS, Linux)
3333
- ✅ Web (WebAssembly, experimental)
3434

app/android/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ android {
2525
applicationId = "com.softartdev.noteroom"
2626
minSdk = libs.versions.minSdk.get().toInt()
2727
targetSdk = libs.versions.targetSdk.get().toInt()
28-
versionCode = 856
29-
versionName = "8.5.6"
28+
versionCode = 857
29+
versionName = "8.5.7"
3030
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
3131
testInstrumentationRunnerArguments["clearPackageData"] = "true"
3232
vectorDrawables.useSupportLibrary = true

app/desktop/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ compose.desktop {
5656
nativeDistributions {
5757
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
5858
packageName = "Note Delight"
59-
packageVersion = "8.5.6"
59+
packageVersion = "8.5.7"
6060
description = "Note app with encryption"
6161
copyright = "© 2023 SoftArtDev"
6262
macOS.iconFile.set(project.file("src/jvmMain/resources/app_icon.icns"))

app/ios-kit/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ listOf(iosArm64(), iosSimulatorArm64()).forEach { iosTarget ->
1818
}
1919

2020
swiftPMDependencies {
21-
iosMinimumDeploymentTarget = "14.1"
21+
iosMinimumDeploymentTarget = "15.0"
2222
}
2323
```
2424

@@ -59,8 +59,8 @@ Swift application sources import the framework by its configured base name:
5959
import iosComposeKit
6060
```
6161

62-
The framework remains dynamic by project policy. Any change to linkage must be validated by launching the iOS application because link-only builds do not detect all framework embedding and dyld failures.
62+
The framework remains dynamic by project policy. Firebase Crashlytics symbols referenced by the framework are force-linked and exported by the Xcode application target. `CrashlyticsDynamicSymbols.txt` also preserves those exports when Xcode strips an archive. The generated IPA is verified before publishing. Any change to linkage must be validated by launching the iOS application because link-only builds do not detect all framework embedding and dyld failures.
6363

6464
## Verification
6565

66-
After changes, compile and link the framework, build `app/iosApp/iosApp.xcodeproj`, run the Xcode SQLCipher unit test, and launch the application on a simulator. Then run the repository verification sequence documented in `AGENTS.md`.
66+
After changes, compile and link the framework, build `app/iosApp/iosApp.xcodeproj`, run the Xcode SQLCipher unit test, launch the application on a simulator, and run `.github/scripts/verify_ios_crashlytics_symbols.sh` against the generated IPA. Then run the repository verification sequence documented in `AGENTS.md`.

app/ios-kit/build.gradle.kts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@ kotlin {
99
iosTarget.binaries.framework {
1010
baseName = "iosComposeKit"
1111
isStatic = false
12-
freeCompilerArgs += listOf("-Xoverride-konan-properties=minVersion.ios=14.1", "-linker-options", "-U _FIRCLSExceptionRecordNSException -U _OBJC_CLASS_\$_FIRStackFrame -U _OBJC_CLASS_\$_FIRExceptionModel -U _OBJC_CLASS_\$_FIRCrashlytics")
12+
freeCompilerArgs += listOf(
13+
"-Xoverride-konan-properties=minVersion.ios=15.0",
14+
"-linker-options",
15+
"-U _FIRCLSExceptionRecordNSException -U _OBJC_CLASS_\$_FIRStackFrame " +
16+
"-U _OBJC_CLASS_\$_FIRExceptionModel -U _OBJC_CLASS_\$_FIRCrashlytics",
17+
)
1318
export(projects.core.domain)
1419
export(project.dependencies.platform(libs.koin.bom))
1520
export(libs.koin.core)
@@ -18,7 +23,7 @@ kotlin {
1823
applyDefaultHierarchyTemplate()
1924

2025
swiftPMDependencies {
21-
iosMinimumDeploymentTarget = "14.1"
26+
iosMinimumDeploymentTarget = "15.0"
2227
}
2328
sourceSets {
2429
commonMain.dependencies {

app/iosApp/KotlinMultiplatformLinkedPackage/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import PackageDescription
33
let package = Package(
44
name: "KotlinMultiplatformLinkedPackage",
55
platforms: [
6-
.iOS("14.1")
6+
.iOS("15.0")
77
],
88
products: [
99
.library(

app/iosApp/KotlinMultiplatformLinkedPackage/subpackages/KotlinMultiplatformLinkedPackageDylib/Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import PackageDescription
33
let package = Package(
44
name: "KotlinMultiplatformLinkedPackageDylib",
55
platforms: [
6-
.iOS("14.1")
6+
.iOS("15.0")
77
],
88
products: [
99
.library(

0 commit comments

Comments
 (0)