iOS guide¶
Basic steps to build and run an iOS project generated by pn.
What gets generated¶
pn run ios unpacks the bundled iOS template (a Swift Xcode project plus the PythonNativeKit Swift package) into build/ios/ios_template and stages three things at the project root:
Python.xcframework: the embedded CPython runtime for yourapp.python_version(downloaded once, checksum-verified, and cached underbuild/ios/ios_runtime/).app/: your project's Python sources.app_packages.iphoneos/andapp_packages.iphonesimulator/: thepythonnativepackage plus[requirements].packagesresolved for that SDK (device or Simulator), so binary wheels such as numpy land in the right slice.pn run iosagainst a Simulator stages only the Simulator slice;pn build iosstages both. The "Install Python runtime" build phase copies the slice matching the SDK being built into the app bundle.
PythonNativeKit/ holds the native rendering core (component managers, gesture recognizers, animations, native modules) and is linked as a local Swift package. Native plugins from [plugins].paths and installed packages are copied into PythonNativeKit/Sources/PythonNativeKit/Plugins/ before xcodebuild runs.
The Xcode project links the framework at build time and embeds CPython through its C API (PythonRuntime.swift); an "Install Python runtime" build phase installs the standard library and converts binary modules into signed frameworks. The built .app is therefore complete as soon as xcodebuild finishes, with no post-build patching. If Python fails to start or your root component fails to mount, the app shows a full-screen error report with the Python traceback instead of failing silently.
Component model¶
The staged yoga/ directory supplies the YogaCore Swift package,
which PythonNativeKit depends on. Xcode compiles its C++ source for
the selected device or simulator architecture during the app build.
The desktop PythonNative wheel's host extension isn't used by the app.
Your app/ directory contains @pn.component function components.
The native iOS template boots Python with
pythonnative.bootstrap.start() and asks the Host native module to
mount your root component inside a ViewController (a thin subclass of
PythonNativeKit's PNViewController). You don't call
anything for this; just export your component and configure the entry
point in pythonnative.toml (app.entry_point).
Run / Prepare¶
To target a specific Simulator, or a physical device, list the available targets and pass one to --device:
Running on a physical device requires [ios].development_team in pythonnative.toml (for code signing), a paired device, and Developer Mode enabled on the device (Settings > Privacy & Security > Developer Mode).
Or prepare without building:
You can then open build/ios/ios_template/ios_template.xcodeproj in Xcode.
Viewing logs¶
After building and installing into the Simulator, pn run ios launches the app
with xcrun simctl launch --console-pty, which attaches your terminal to the
app's stdout/stderr. Python print() calls and exception tracebacks appear
inline until you press Ctrl+C, at which point the app is terminated cleanly.
SIMCTL_CHILD_PYTHONUNBUFFERED=1 is forwarded to the launched process so
output is line-buffered and doesn't get stuck behind Python's stream buffers.
Pass --no-logs to skip the console attach and use the legacy
fire-and-exit launch instead. That's useful when you want to continue
interacting with the app from Xcode or Console.app, or when running in
a non-interactive context like CI (see the e2e.yml workflow for an
example).
To re-attach to a running app later without rebuilding, use:
For a physical device, use Console.app or Xcode's Devices and
Simulators window; simctl console attach is Simulator-only.
Requirements¶
- iOS 13.0 or newer on the device or Simulator (
[ios].deployment_targetcan't go lower; it's the floor for BeeWare's CPython and for iOS wheels on PyPI). - Xcode 14 or newer.
simctl launch --console-ptywas added in Xcode 14; on older toolchains either upgrade Xcode or pass--no-logs. - Python
print()output on the Simulator is routed through the app's stderr by thepythonnative._ios_logmodule. This runs automatically when the embedded Python bootstraps on iOS, so you don't need to configure it.
Clean¶
Remove the build directory safely:
Notes¶
- Building and running for Simulator via the CLI is best-effort. Opening the generated project in Xcode is recommended for iterative development.
See Troubleshooting for the most common errors and their fixes.
Next steps¶
- Try the Android counterpart: Android guide.
- Build a real screen: Examples.
- Iterate faster with Hot reload.