Readme · Guides · Reference · Contributing
The Optimization SDK Suite is pre-release (alpha). Breaking changes can be published at any time.
Reference app for native iOS bridge and preview-panel validation work. This app exercises two iOS
shells against the mock server in lib/mocks/ and hosts the XCUITest suite.
This reference app is not the iOS SDK package. The iOS SDK exists as a pre-release Swift Package;
for package status, see packages/ios.
Use this app when you need to validate native iOS bridge and preview-panel behavior against the shared mock API. The app includes two host shells:
OptimizationAppSwiftUI - SwiftUI shell, with sources under swiftui/.OptimizationAppUIKit - UIKit shell, with sources under uikit/.Both apps share shared/ for configuration, Contentful fetching, and analytics event storage. They
run the same UI test source tree from uitests/ against their respective host apps so SDK behavior
can be compared across UI frameworks.
The app defines one locale in shared config, passes it to the native SDK as top-level locale, and
passes it directly to the shared raw CDA fetch helper. Entries passed to SDK resolution use the
standard single-locale CDA entry shape. Do not use all-locale CDA responses or locale=*, because
entry resolution expects direct single-locale fields such as fields.nt_experiences and
fields.nt_variants. See
Locale handling in the Optimization SDK Suite
for the broader locale model and
Entry personalization and variant resolution
for the entry contract.
This mock app uses one Contentful locale. A production app can derive the application locale from
its own navigation, i18n, or account-preference layer and pass that value to both SDK locale and
CDA requests when they should stay aligned.
http://localhost:8000 for manual xcodebuild and raw Xcode flows. The local
bootstrap and E2E runner scripts start the mock server themselves.The Xcode project is generated by XcodeGen from
project.yml. Regenerate the project after you add, rename, or move a source file:
brew install xcodegen
xcodegen generate
From the monorepo root, start the mock API server before manual Xcode or raw xcodebuild flows:
pnpm serve:mocks
You can skip this manual mock-server step when using ./scripts/bootstrap.sh or
./scripts/run-e2e.sh; both scripts start the mock server. Before starting mocks, they stop any
existing process bound to MOCK_SERVER_PORT (default 8000), then clean up their own mock-server
child process on exit.
This implementation does not use a local .env file. Mock API settings live in shared iOS app
configuration and point simulator traffic to http://localhost:8000.
The bootstrap script runs preflight checks, then configures, builds, and launches the app on an iOS Simulator with the mock server running. When it finishes it leaves a simulator booted with the reference app and the mock server running in the foreground:
cd implementations/ios-sdk
./scripts/bootstrap.sh
The preflight checks verify that you are on macOS with Xcode (full IDE, not just the Command Line Tools), an iOS Simulator runtime, Node.js, and pnpm. If a check fails, the script prints the exact remediation steps and stops before building anything. XcodeGen is installed automatically via Homebrew if it is missing.
Useful environment variables:
APP_SHELL — swiftui (default) or uikit.IOS_SIM_NAME — simulator device name (default iPhone 16); falls back to the first available
iPhone if the named device is absent.SKIP_BUILD — set to true to reuse the last build.Unlike the Android app, no port forwarding is required: the iOS Simulator shares the host network,
so the app reaches the mock server at localhost:8000 directly.
Prefer the local runner from implementations/ios-sdk/. It runs preflight checks, builds the JS
bridge, generates the Xcode project, starts the mock server, resolves a simulator, runs XCUITest,
and cleans up its child processes:
./scripts/run-e2e.sh
APP_SHELL=both ./scripts/run-e2e.sh
ONLY_TESTING=OptimizationAppUITestsSwiftUI/PreviewPanelOverridesTests ./scripts/run-e2e.sh
From the monorepo root, use the release build/run wrappers when you need the CI-style split flow:
pnpm implementation:run -- ios-sdk test:e2e:ios:build:release
IOS_SCHEME=SwiftUI pnpm implementation:run -- ios-sdk test:e2e:ios:run:release
IOS_SCHEME=UIKit pnpm implementation:run -- ios-sdk test:e2e:ios:run:release
The split run commands expect the mock server to already be running at http://localhost:8000.
For lower-level debugging, raw xcodebuild remains available. Run the full suite against both app
shells from implementations/ios-sdk/:
xcodebuild test \
-project OptimizationApp.xcodeproj \
-scheme OptimizationAppSwiftUI \
-destination 'platform=iOS Simulator,name=iPhone 16'
xcodebuild test \
-project OptimizationApp.xcodeproj \
-scheme OptimizationAppUIKit \
-destination 'platform=iOS Simulator,name=iPhone 16'
Run a single test class against the SwiftUI shell:
xcodebuild test \
-project OptimizationApp.xcodeproj \
-scheme OptimizationAppSwiftUI \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-only-testing:OptimizationAppUITestsSwiftUI/PreviewPanelOverridesTests
Add the .swift file to uitests/Tests/ and run xcodegen generate. Both UI test bundles pick up
the file from the generated project.
The preview-panel override suite mirrors the React Native Detox suite in
implementations/react-native-sdk/e2e/preview-panel-overrides.test.js. Both reference the shared
contract document at implementations/PREVIEW_PANEL_SCENARIOS.md. Keep scenario names and fixture
IDs identical across platforms so cross-platform regressions are visible in CI diffs.
Use this app when you need a debuggable native iOS surface for changes in
packages/ios/ContentfulOptimization or the shared JS bridge. The Xcode project references the SDK
as a local Swift Package, so app builds compile the package from workspace source rather than from a
published artifact.
The app schemes include a pre-action that builds @contentful/optimization-js-bridge before Swift
Package resource resolution when bridge source is newer than the copied UMD resource. The pre-action
writes its output to /tmp/optimization-ios-build-js-bridge.log; check that file if Xcode appears
to use a stale bridge bundle or cannot find pnpm.
The normal loop is:
packages/ios/ContentfulOptimization/Sources/... or bridge TypeScript in
packages/universal/optimization-js-bridge/src/....OptimizationAppSwiftUI or OptimizationAppUIKit scheme. Xcode recompiles the
local Swift Package and refreshes the bridge bundle when needed.From the command line, build a shell from implementations/ios-sdk/:
xcodebuild build \
-project OptimizationApp.xcodeproj \
-scheme OptimizationAppSwiftUI \
-destination 'platform=iOS Simulator,name=iPhone 16'
Run the smallest check that covers the changed surface:
| Change area | Suggested validation |
|---|---|
| Bridge TypeScript only | pnpm --filter @contentful/optimization-js-bridge typecheck and pnpm --filter @contentful/optimization-js-bridge build |
| Swift package behavior | Targeted ./scripts/run-e2e.sh for the affected app shell or XCUITest class |
| Preview-panel or cross-platform behavior | Targeted preview-panel XCUITest plus the matching shared scenario contract review |
| Documentation-only README changes | Prettier on touched Markdown and git diff --check |
Common local pitfalls:
xcodegen generate after changing project.yml or adding, moving, or renaming iOS sources.pnpm, launch Xcode from a shell where pnpm --version works or check the
pre-action log for the exact PATH issue.iPhone 16.