This document provides a comprehensive technical architecture for iOS support in Wails v3. The implementation enables Go applications to run natively on iOS with a WKWebView frontend, maintaining the Wails philosophy of using web technologies for UI while leveraging Go for business logic.
- Architecture Overview
- Core Components
- Layer Architecture
- Implementation Details
- Battery Optimization
- Build System
- Security Considerations
- API Reference
- Battery Efficiency First: All architectural decisions prioritize battery life
- No Network Ports: Asset serving happens in-process via native APIs
- Minimal WebView Instances: Maximum 2 concurrent WebViews (1 primary, 1 for transitions)
- Native Integration: Deep iOS integration using Objective-C runtime
- Wails v3 Compatibility: Maintain API compatibility with existing Wails v3 applications
┌─────────────────────────────────────────────────────────────┐
│ iOS Application │
├─────────────────────────────────────────────────────────────┤
│ UIKit Framework │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ WailsViewController │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ WKWebView Instance │ │ │
│ │ │ ┌─────────────────────────────────────────┐ │ │ │
│ │ │ │ Web Application (HTML/JS) │ │ │ │
│ │ │ └─────────────────────────────────────────┘ │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Bridge Layer (CGO) │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │URL Handler │ │JS Bridge │ │Message Handler│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Go Runtime │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Wails Application │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ │
│ │ │App Logic │ │Services │ │Asset Server │ │ │
│ │ └──────────┘ └──────────┘ └──────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Purpose: Go interface for iOS platform operations
Key Functions:
platformRun(): Initialize and run the iOS applicationplatformQuit(): Gracefully shutdown the applicationisDarkMode(): Detect iOS dark mode stateExecuteJavaScript(windowID uint, js string): Execute JS in WebView
Exported Go Functions (Called from Objective-C):
ServeAssetRequest(windowID C.uint, urlStr *C.char, callbackID C.uint)HandleJSMessage(windowID C.uint, message *C.char)
Components:
@interface WailsSchemeHandler : NSObject <WKURLSchemeHandler>- Implements
WKURLSchemeHandlerprotocol - Intercepts
wails://URL requests - Bridges to Go for asset serving
- Manages pending requests with callback IDs
Methods:
startURLSchemeTask:: Intercept request, call Go handlerstopURLSchemeTask:: Cancel pending requestcompleteRequest:withData:mimeType:: Complete request with data from Go
@interface WailsMessageHandler : NSObject <WKScriptMessageHandler>- Implements JavaScript to Go communication
- Handles
window.webkit.messageHandlers.external.postMessage() - Serializes messages to JSON for Go processing
Methods:
userContentController:didReceiveScriptMessage:: Process JS messages
@interface WailsViewController : UIViewController- Main view controller containing WKWebView
- Manages WebView lifecycle
- Handles JavaScript execution requests
Properties:
webView: WKWebView instanceschemeHandler: Custom URL scheme handlermessageHandler: JS message handlerwindowID: Unique window identifier
Methods:
viewDidLoad: Initialize WebView with configurationexecuteJavaScript:: Run JS code in WebView
C Interface Functions:
void ios_app_init(void); // Initialize iOS app
void ios_app_run(void); // Run main loop
void ios_app_quit(void); // Quit application
bool ios_is_dark_mode(void); // Check dark mode
unsigned int ios_create_webview(void); // Create WebView
void ios_execute_javascript(unsigned int windowID, const char* js);
void ios_complete_request(unsigned int callbackID, const char* data, const char* mimeType);Responsibilities:
- Render HTML/CSS/JavaScript UI
- Handle user interactions
- Communicate with native layer
Key Features:
- WKWebView for modern web standards
- Hardware-accelerated rendering
- Efficient memory management
Request Interception:
WebView Request → WKURLSchemeHandler → Go ServeAssetRequest → AssetServer → Response
JavaScript Bridge:
JS postMessage → WKScriptMessageHandler → Go HandleJSMessage → Process → ExecuteJavaScript
Components:
- Application lifecycle management
- Service binding and method calls
- Asset serving from embedded fs.FS
- Business logic execution
iOS-Specific Features:
- Dark mode detection
- System appearance integration
- iOS-specific optimizations
- WebView makes request to
wails://localhost/path - WKURLSchemeHandler intercepts request
- Creates callback ID and stores
WKURLSchemeTask - Calls Go function
ServeAssetRequestwith URL and callback ID - Go processes request through AssetServer
- Go calls
ios_complete_requestwith response data - Objective-C completes the
WKURLSchemeTaskwith response
- Go calls
ios_execute_javascriptwith JS code - Bridge dispatches to main thread
- WKWebView evaluates JavaScript
- Completion handler logs any errors
- JavaScript calls
window.webkit.messageHandlers.wails.postMessage(data) - WKScriptMessageHandler receives message
- Serializes to JSON and passes to Go
- Go processes message in
HandleJSMessage - Go can respond via
ExecuteJavaScript
// Disable unnecessary features
config.suppressesIncrementalRendering = NO;
config.allowsInlineMediaPlayback = YES;
config.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;- Single WebView Instance: Reuse instead of creating new instances
- Automatic Reference Counting: Use ARC for Objective-C objects
- Lazy Loading: Initialize components only when needed
- Resource Cleanup: Properly release resources when done
- In-Process Serving: No network overhead
- Direct Memory Transfer: Pass data directly without serialization
- Efficient Caching: Leverage WKWebView's built-in cache
- Minimal Wake Locks: No background network activity
//go:build ios#cgo CFLAGS: -x objective-c -fobjc-arc
#cgo LDFLAGS: -framework Foundation -framework UIKit -framework WebKitSteps:
- Check dependencies (go, xcodebuild, xcrun)
- Set up iOS cross-compilation environment
- Build Go binary with iOS tags
- Create app bundle structure
- Generate Info.plist
- Sign for simulator
- Create launch script
Environment Variables:
export CGO_ENABLED=1
export GOOS=ios
export GOARCH=arm64
export SDK_PATH=$(xcrun --sdk iphonesimulator --show-sdk-path)xcrun simctl install "$DEVICE_ID" "WailsIOSDemo.app"
xcrun simctl launch "$DEVICE_ID" "com.wails.iosdemo"- Custom Scheme: Use
wails://to avoid conflicts - Origin Validation: Only serve to authorized WebViews
- No External Access: Scheme handler only responds to app's WebView
- Input Validation: Sanitize JS code before execution
- Sandboxed Execution: WKWebView provides isolation
- No eval(): Avoid dynamic code evaluation
- In-Memory Only: No temporary files on disk
- ATS Compliance: App Transport Security enabled
- Secure Communication: All data stays within app process
// Create new iOS application
app := application.New(application.Options{
Name: "App Name",
Description: "App Description",
})
// Run the application
app.Run()
// Execute JavaScript
app.ExecuteJavaScript(windowID, "console.log('Hello')")type MyService struct{}
func (s *MyService) Greet(name string) string {
return fmt.Sprintf("Hello, %s!", name)
}
app := application.New(application.Options{
Services: []application.Service{
application.NewService(&MyService{}),
},
})window.webkit.messageHandlers.wails.postMessage({
type: 'methodCall',
service: 'MyService',
method: 'Greet',
args: ['World']
});window.wailsCallback = function(data) {
console.log('Received:', data);
};// Execute JavaScript
ios_execute_javascript(windowID, "alert('Hello')");
// Complete asset request
ios_complete_request(callbackID, htmlData, "text/html");// Serve asset request
ServeAssetRequest(windowID, urlString, callbackID);
// Handle JavaScript message
HandleJSMessage(windowID, jsonMessage);- WebView Creation: < 100ms
- Asset Request: < 10ms for cached, < 50ms for first load
- JS Execution: < 5ms for simple scripts
- Message Passing: < 2ms round trip
- Memory Usage: < 50MB baseline
- Battery Impact: < 2% per hour active use
- Xcode Instruments: CPU, Memory, Energy profiling
- WebView Inspector: JavaScript performance
- Go Profiling: pprof for Go code analysis
- Production-ready error handling
- Comprehensive test suite
- Performance optimization
- Multiple window support
- System tray integration
- Native menu implementation
- Widget extension support
- App Clip support
- ShareSheet integration
- Siri Shortcuts
- Background task support
- Push notifications
- CloudKit integration
- Apple Watch companion app
This architecture provides a solid foundation for iOS support in Wails v3. The design prioritizes battery efficiency, native performance, and seamless integration with the existing Wails ecosystem. The proof of concept demonstrates all four required capabilities:
- ✅ WebView Creation: Native WKWebView with optimized configuration
- ✅ Request Interception: Custom scheme handler without network ports
- ✅ JavaScript Execution: Bidirectional communication bridge
- ✅ iOS Simulator Support: Complete build and deployment pipeline
The architecture is designed to scale from this proof of concept to a full production implementation while maintaining the simplicity and elegance that Wails developers expect.