iOS bygg og deployment
Denne guiden dekker hele iOS bygg-flyten fra lokal utvikling til TestFlight. Skrevet basert pa feilene vi fant og fikset under forste vellykkede TestFlight-upload (mars 2026).
Hurtigreferanse
heading.anchorLabel| Oppgave | Kommando |
|---|---|
| Kjor i simulator | ⌘R i Xcode |
| Kjor pa device (USB) | Velg device i Xcode → ⌘R |
| TestFlight-upload | doppler run -p im -c prd -- fastlane ios beta force:true |
| Bump versjon | fastlane ios bump type:patch |
| Promoter til App Store | fastlane ios promote |
| Sjekk toolchain | fastlane ios doctor |
Miljoer — automatisk basert pa byggtype
heading.anchorLabelIngen manuell konfigurasjon nodvendig. Appen velger miljo automatisk:
| Hvordan du kjorer | Miljo | API |
|---|---|---|
Simulator (⌘R) | Development | localhost:3001 |
| USB-device fra Xcode | Staging | api-stg.impulseai.app |
| TestFlight | Staging | api-stg.impulseai.app |
| App Store | Production | api.impulseai.app |
Deteksjon skjer i ImpulseApp.swift ved oppstart:
- Debug + simulator → Development
- Debug + device → Staging
- Release + sandboxReceipt → Staging (TestFlight)
- Release + ikke sandbox → Production (App Store)
Lokal utvikling (simulator)
heading.anchorLabelForutsetninger
heading.anchorLabel# 1. Start lokale tjenesterpnpm docker:start # Supabase + Redis
# 2. Start API-server (i egen terminal)doppler run -p im -c dev -- pnpm --filter api devKjor appen
heading.anchorLabel- Apne
iosApp.xcodeproji Xcode - Velg simulator (iPhone 17 e.l.)
⌘R— appen kobler tillocalhost:3001
KMP-endringer
heading.anchorLabelNar du endrer Kotlin-kode i shared/:
# Rask kompileringssjekk (13s)cd apps/mobile-kmp && ./gradlew :shared:compileKotlinIosSimulatorArm64Xcode embed-scriptet rebuilder automatisk nar .kt/.xml filer endres. Forste build etter Kotlin-endringer tar litt lengre tid.
Swift-only endringer
heading.anchorLabelIngen ekstra steg — Xcode inkrementell build er tilstrekkelig. Med InjectionIII kan du fa hot reload.
Device-testing (USB fra Xcode)
heading.anchorLabel- Koble iPhone til Mac med USB
- Velg din device i Xcode target-velgeren
⌘R— appen kobler til Staging API automatisk
TestFlight-upload
heading.anchorLabelForutsetninger
heading.anchorLabel- Doppler CLI installert og innlogget (
doppler login) - Ruby + Bundler (
bundle installiiosApp/) - Xcode CLI tools (
xcode-select --install)
Bygg og last opp
heading.anchorLabelcd apps/mobile-kmp/iosAppdoppler run -p im -c prd -- bundle exec fastlane ios beta force:trueDette gjor:
- Match: Henter signing certificates fra
im-certificatesrepo - Auto-inkrement: Bumper build-nummer fra TestFlight
- KMP: Bygger release framework (arm64)
- Archive: Bygger Release-IPA med riktig signing
- Upload: Laster opp til App Store Connect
- Commit: Committer versjonsbump + tagger
Tar ca. 10 minutter. Etter upload tar Apple 5-30 minutter pa processing.
Etter upload
heading.anchorLabel- Sjekk status i App Store Connect → TestFlight
- Nar status er “Complete” → opprett testgruppe og legg til testere via e-post
- Testere far invitasjon pa e-post → apner TestFlight-appen → installerer
TestFlight-testing
heading.anchorLabelTestere logger inn med Sign in with Apple (vanlig Apple ID). Alt fungerer som i produksjon, med disse forskjellene:
| Aspekt | TestFlight (Sandbox) | App Store |
|---|---|---|
| Betaling | Gratis — ingen belastning | Ekte penger |
| Abonnement-varighet | Akselerert (1 mnd = 5 min) | Normal tid |
| Fornyelse | Auto-fornyer 6 ganger, stopper | Fornyer til bruker kansellerer |
| Data | Lagres i staging Supabase | Lagres i production Supabase |
Sandbox tidsakselerasjon for subscriptions:
| Ekte varighet | Sandbox |
|---|---|
| 1 uke | 3 minutter |
| 1 maned | 5 minutter |
| 1 ar | 1 time |
App Store Connect webhooks
heading.anchorLabelFor at subscription-syncing fungerer med TestFlight, ma webhooks vare konfigurert:
- App Store Connect → din app → App Information
- Scroll til App Store Server Notifications
- Sett begge URL-er:
| Felt | URL |
|---|---|
| Production Server URL | https://api.impulseai.app/webhooks/appstore |
| Sandbox Server URL | https://api-stg.impulseai.app/webhooks/appstore |
Bumpe versjon (for releases)
heading.anchorLabel# Patch: 1.0.0 → 1.0.1bundle exec fastlane ios bump type:patch
# Minor: 1.0.0 → 1.1.0bundle exec fastlane ios bump type:minor
# Major: 1.0.0 → 2.0.0bundle exec fastlane ios bump type:majorBuild-nummer inkrementeres automatisk av fastlane ios beta.
Signing — hvordan det fungerer
heading.anchorLabelMatch (certificate management)
heading.anchorLabelCertificates og provisioning profiles lagres i et privat GitHub-repo (DigiteersHQ/im-certificates). Fastlane Match henter og installerer disse automatisk.
| Hemmelighet | Kilde | Bruk |
|---|---|---|
MATCH_GIT_URL | Doppler prd | Git-repo for certificates |
MATCH_PASSWORD | Doppler prd | Dekrypteringsnokkel for repo |
ASC_KEY_ID | Doppler prd | App Store Connect API |
ASC_ISSUER_ID | Doppler prd | App Store Connect API |
ASC_PRIVATE_KEY | Doppler prd | App Store Connect API (.p8) |
Alle secrets injiseres via doppler run -p im -c prd --.
Debug vs Release signing
heading.anchorLabel| Konfigurasjon | Signing | Satt av |
|---|---|---|
| Debug (simulator/device) | Apple Development (automatic) | Xcode |
| Release (archive) | Apple Distribution | Match via Fastlane |
CODE_SIGN_IDENTITY i project.pbxproj er "Apple Development" for begge configs. Match overstyrer til Distribution under archive.
Kritiske build-innstillinger
heading.anchorLabelDisse innstillingene i project.pbxproj matte fikses for at TestFlight-upload skulle fungere:
| Innstilling | Feil verdi | Riktig verdi | Hvorfor |
|---|---|---|---|
SDKROOT | auto | iphoneos | auto kan inkludere ugyldige plattformer |
ITSAppUsesNonExemptEncryption | (manglende) | NO | Apple venter pa export compliance-svar |
FRAMEWORK_SEARCH_PATHS | Hardkodede simulator-stier | $(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME) | Sikrer riktig framework per config |
| App-ikon | RGBA (med alfa) | RGB (uten alfa) | Apple avviser transparente ikoner |
KMP Framework-embedding
heading.anchorLabelXcode PBXGroup peker hardkodet til iosSimulatorArm64/debugFramework/. For Release device-builds (TestFlight/App Store) kopierer embed-scriptet arm64 release-framework dit:
if CONFIGURATION=Release && not simulator: cp arm64/releaseFramework → simulatorArm64/debugFrameworkDette er en pragmatisk losning. Xcode Embed Frameworks build phase bruker PBXGroup-stien uansett.
Privacy og App Store-krav
heading.anchorLabelImplementert
heading.anchorLabel| Krav | Status | Fil |
|---|---|---|
| App-ikon 1024x1024 RGB | ✅ | Assets.xcassets/AppIcon.appiconset/ |
| Privacy Manifest | ✅ | PrivacyInfo.xcprivacy |
| Speech Recognition API-deklarasjon | ✅ | PrivacyInfo.xcprivacy |
| Export compliance (kryptering) | ✅ | ITSAppUsesNonExemptEncryption = NO |
| Associated Domains | ✅ | iosApp.entitlements → impulseai.app |
Nodvendig for App Store (ikke TestFlight)
heading.anchorLabel| Krav | Status |
|---|---|
Privacy Policy pa impulseai.app/privacy | Trenger Sentry + TelemetryDeck tillegg |
AASA-fil pa impulseai.app/.well-known/ | Spesifikasjon levert til landing-team |
| Paid Apps Agreement i App Store Connect | Sjekkes manuelt |
Feilsoking
heading.anchorLabel”Invalid Bundle — invalid platform in CFBundleSupportedPlatforms”
heading.anchorLabelshared.framework ble bygget for simulator (iPhoneSimulator) i stedet for device (iPhoneOS). Verifiser:
# Sjekk arkivetARCHIVE=$(ls -td ~/Library/Developer/Xcode/Archives/**/*.xcarchive | head -1)for fw in "$ARCHIVE/Products/Applications/iosApp.app/Frameworks/"*.framework; do name=$(basename "$fw") plutil -p "$fw/Info.plist" | grep -A2 CFBundleSupportedPlatformsdoneFiks: Slett embed-marker og bygg pa nytt:
rm -f shared/build/.last_embed_marker“Conflicting provisioning settings”
heading.anchorLabelCODE_SIGN_IDENTITY i Release er satt til noe annet enn "Apple Development". Match overstyrer under archive — project.pbxproj skal alltid ha "Apple Development".
Build henger i “Processing” i App Store Connect
heading.anchorLabelVanligvis 5-30 minutter. Hvis det tar over en time, sjekk e-post fra Apple for feilmeldinger. Last opp ny build — den gamle kan ignoreres.
Fastlane “Git repository is dirty”
heading.anchorLabelBruk force:true:
doppler run -p im -c prd -- bundle exec fastlane ios beta force:trueKMP-endringer vises ikke i Xcode
heading.anchorLabelSlett embed-marker:
rm -f apps/mobile-kmp/shared/build/.last_embed_markerNøkkelfiler
heading.anchorLabel| Fil | Rolle |
|---|---|
iosApp/fastlane/Fastfile | Alle Fastlane-lanes (beta, promote, bump, doctor) |
iosApp/fastlane/Matchfile | Match-konfigurasjon (cert-repo) |
iosApp/fastlane/.env.default | Ikke-sensitive Fastlane-defaults |
iosApp/iosApp.xcodeproj/project.pbxproj | Build-innstillinger, signing, framework-stier |
iosApp/iosApp.entitlements | App-capabilities (SIWA, Associated Domains) |
iosApp/PrivacyInfo.xcprivacy | Privacy Manifest |
iosApp/ImpulseApp.swift | Miljodeteksjon, versjonsjekk, app-oppstart |
shared/.../config/Environment.kt | API-URL-er, nokler, AppConfig |