iOS
Wails v3 applications run on iOS as native UIKit apps: a WKWebView renders
the frontend, assets are served in-process over a custom wails:// URL
scheme (no localhost server, no open ports), and the standard
@wailsio/runtime works unchanged — service bindings, events, dialogs and
the clipboard use the same /wails/runtime transport as on desktop.
The same main.go builds for desktop and iOS. iOS-specific behaviour is
configured through application.Options.IOS, with per-platform Go files
guarded by //go:build ios.
Requirements
Section titled “Requirements”- macOS with full Xcode installed (the command-line tools alone are not
enough) —
wails3 doctorshows the iOS SDKs it can find - Go 1.25+ and npm
Running on the Simulator
Section titled “Running on the Simulator”From your project directory:
wails3 task ios:runThis generates the iOS scaffolding (build/ios/), compiles your Go code as
a C archive (GOOS=ios), links it against UIKit, bundles, signs ad-hoc,
boots a simulator if necessary, and launches the app.
Useful companions:
wails3 task ios:logs:dev # stream the app's logs from the simulatorwails3 task ios:xcode # open the generated Xcode projectIn debug builds the webview is inspectable from Safari’s Develop menu.
Packaging
Section titled “Packaging”wails3 task ios:package # production .app for the simulatorwails3 task ios:deploy-simulator # install + launch itProduction builds use -tags production,ios, are stripped, and compile out
the framework’s internal diagnostics.
Device builds
Section titled “Device builds”wails3 task ios:package IOS_PLATFORM=device \ CODESIGN_IDENTITY="Apple Development: You (TEAMID)" \ PROVISIONING_PROFILE=path/to/profile.mobileprovision
wails3 task ios:deploy-device [DEVICE_ID=<udid>] # via xcrun devicectlwails3 task ios:package:ipa IOS_PLATFORM=device ... # distribution .ipaIOS_PLATFORM=device selects the iphoneos SDK and the
arm64-apple-ios<min> target. Entitlements from
build/ios/entitlements.plist are applied to device builds only —
get-task-allow by default; add capability keys as your app needs them.
Configuration
Section titled “Configuration”build/config.yml:
ios: bundleID: com.example.myapp displayName: My App version: 1.0.0 minIOSVersion: "15.0"Application options (application.Options.IOS, applied at startup) include
DisableScroll, DisableBounce, DisableScrollIndicators,
DisableInputAccessoryView, EnableBackForwardNavigationGestures,
DisableLinkPreview, EnableInlineMediaPlayback,
EnableAutoplayWithoutUserAction, DisableInspectable, UserAgent,
ApplicationNameForUserAgent, BackgroundColour, and native bottom tabs via
EnableNativeTabs + NativeTabsItems.
Native device features — the IOS manager
Section titled “Native device features — the IOS manager”iOS-specific native capabilities are exposed through the package-level
application.IOS manager. You call it from Go, inside a //go:build ios
file, so your shared code stays platform-agnostic (Android has the same shape
via application.Android). This is the “manager pattern”: one singleton,
flat method names, no per-feature objects to construct.
One-shot actions return immediately:
//go:build ios
application.IOS.Haptic("impact-medium") // impact-light|impact-medium|impact-heavy|success|warning|error|selectionapplication.IOS.Share(`{"text":"Hi","url":"https://wails.io"}`)application.IOS.SetKeepAwake(true)application.IOS.PostNotification(`{"title":"Done","body":"Build finished","delay":2}`)application.IOS.SecureSet("token", "abc") // Keychain-backedQuery helpers return JSON strings — SafeAreaJSON(), AppInfoJSON(),
PowerJSON(), NetworkJSON(), StorageJSON(), GetOrientation(),
GetBrightness().
Asynchronous results: common:* and ios:* events
Section titled “Asynchronous results: common:* and ios:* events”Anything that finishes later — a permission prompt, a sensor stream, a camera
capture — delivers its result as a Wails event rather than a return value.
Listen on either side of the bridge. Names use a common: prefix for
capabilities shared with Android and an ios: prefix for iOS-only ones.
// Goapp.Event.On("common:location", func(e *application.CustomEvent) { // e.Data -> {"lat":..,"lng":..,"accuracy":..} or {"error":..}})// frontendimport { Events } from "@wailsio/runtime";Events.On("common:notification", (e) => { /* {ok, scheduled, presented, tapped, error} */ });| Event | Triggered by | Payload |
|---|---|---|
common:biometric | BiometricAuthenticate(reason) | {ok, error} |
common:location | GetLocation() | {lat, lng, accuracy} / {error} |
common:motion | SetMotion(true) | {x, y, z} |
common:proximity | SetProximity(true) | {near} |
common:keyboard | SetKeyboardWatch(true) | {visible, height} |
common:torch | SetTorch(bool) | {on, available} |
common:notification | PostNotification(json) | {ok, scheduled, presented, tapped, error} |
common:capture | CapturePhoto() / CaptureVideo() | {type, path, size, thumb} |
common:screenCapture | SetScreenProtect(true) | {screenshot, recording} |
ios:backgroundTask | BeginBackgroundTask(seconds) | {message, granted} |
The kitchen-sink example under v3/examples/mobile wires every feature above
end to end.
Runtime WebView controls
Section titled “Runtime WebView controls”Beyond the compile-time application.Options.IOS flags, several WebView
behaviours can be toggled at runtime from Go:
application.IOS.SetScrollEnabled(false)application.IOS.SetBounceEnabled(false)application.IOS.SetScrollIndicatorsEnabled(false)application.IOS.SetBackForwardGesturesEnabled(true)application.IOS.SetLinkPreviewEnabled(false)application.IOS.SetInspectableEnabled(true)application.IOS.SetCustomUserAgent("MyApp/1.0")The bundled @wailsio/runtime also exposes a small frontend iOS namespace:
import { IOS } from "@wailsio/runtime";await IOS.Haptics.Impact("medium"); // light|medium|heavy|soft|rigidconst info = await IOS.Device.Info();Native bottom-tab selections arrive as a nativeTabSelected CustomEvent on
window.
What works, what doesn’t
Section titled “What works, what doesn’t”| Area | Status |
|---|---|
WKWebView + in-process assets (wails://) | ✅ |
| Service bindings, events (both directions) | ✅ |
| Message dialogs | ✅ UIAlertController with button callbacks |
| Open file / files / directory dialogs | ✅ UIDocumentPicker (files imported as sandbox copies) |
| Save file dialogs | ❌ Returns an error — write inside the app sandbox instead |
| Clipboard | ✅ UIPasteboard |
| Screens API | ✅ UIScreen metrics incl. safe-area work area |
Lifecycle events (events.IOS.*) | ✅ |
| Window geometry, menus, system tray | Intentional no-ops |
| Multiple windows | Only the first window is displayed |
Porting notes
Section titled “Porting notes”- Desktop code compiles unchanged under
GOOS=ios; geometry/menu/tray calls become no-ops because iOS apps are fullscreen. iosimplies thedarwinbuild tag: macOS-only files need//go:build darwin && !ios, and at runtimeruntime.GOOSis"ios".- Replace save-file dialogs with writes into the app sandbox (for example the Documents directory) plus a share flow.
- Design the frontend responsively; safe areas are handled natively.