iOS application demonstrating the Stellar SDK with SwiftUI and Compose Multiplatform integration.
This iOS app showcases the Stellar SDK's capabilities on iOS, featuring:
- Key Generation: Generate and manage Stellar keypairs
- Account Management: Fund testnet accounts and fetch account details
- Payments: Send XLM and custom assets
- Trustlines: Establish trust to hold issued assets
- Transaction Details: View transaction operations and events
- Smart Contracts: Fetch and parse Soroban contract details
- Contract Deployment: Upload and deploy WASM contracts to testnet
The app uses 100% shared Compose UI from the demo:shared module, wrapped in a minimal SwiftUI container.
┌──────────────────────────────────────────────┐
│ demo:shared (Kotlin) │
│ • All UI screens (Compose) │
│ • Business logic (Stellar SDK) │
│ • Navigation (Voyager) │
│ • Material 3 theme │
└──────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────┐
│ demo:iosApp (27 lines Swift) │
│ • StellarDemoApp.swift │
│ • UIViewControllerRepresentable wrapper │
│ • Launches Compose UI │
└──────────────────────────────────────────────┘
- Shared code: ~98% (all UI and business logic in Kotlin)
- iOS-specific: ~2% (SwiftUI wrapper only)
- SwiftUI Entry:
StellarDemoAppis the SwiftUI app entry point - Compose Wrapper:
ComposeViewwraps the Kotlin Compose UI usingUIViewControllerRepresentable - View Controller: Calls
MainViewController()from the shared Kotlin framework - Compose UI: Renders the full Compose Multiplatform UI in the iOS app
- macOS: Required for iOS development
- Xcode: 15.0 or newer
- xcodegen: Project generator
brew install xcodegen
- CocoaPods or Swift Package Manager: For libsodium dependency
- Deployment Target: iOS 14.0 or higher
- Swift: 5.9+
- Kotlin: 2.0+ (for building the framework)
- iOS: 14.0 or higher
- Architectures: arm64 (device), x86_64 or arm64 (simulator)
- Internet: Required (connects to Stellar testnet)
From project root:
# For iOS Simulator (Apple Silicon Mac)
./gradlew :demo:shared:linkDebugFrameworkIosSimulatorArm64
# For iOS Simulator (Intel Mac)
./gradlew :demo:shared:linkDebugFrameworkIosX64
# For iOS Device
./gradlew :demo:shared:linkDebugFrameworkIosArm64Note: The pre-build script in Xcode automatically builds the framework, but building it manually first can help troubleshoot issues.
cd demo/iosApp
xcodegen generateThis creates StellarDemo.xcodeproj from project.yml.
open StellarDemo.xcodeprojOr: Double-click StellarDemo.xcodeproj in Finder
In Xcode:
- Select scheme: "StellarDemo" (should be selected by default)
- Select destination:
- iOS Simulator (e.g., iPhone 15 Pro)
- Your connected iOS device
- Run: Click the Play button (▶) or press ⌘R
The Xcode project includes a pre-build script that automatically builds the Kotlin framework:
cd "$SRCROOT/../../.."
./gradlew :demo:shared:linkDebugFrameworkIosSimulatorArm64This runs before every build, ensuring the framework is up-to-date.
iosApp/
├── StellarDemo/
│ ├── StellarDemoApp.swift # SwiftUI app entry (27 lines)
│ └── Info.plist # iOS app configuration
├── project.yml # xcodegen configuration
├── StellarDemo.xcodeproj/ # Generated Xcode project (gitignored)
└── build/ # Build outputs (gitignored)
StellarDemoApp.swift (Entry Point):
import SwiftUI
import shared
@main
struct StellarDemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
var body: some View {
ComposeView()
.ignoresSafeArea(.all)
}
}
struct ComposeView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
let controller = MainViewControllerKt.MainViewController()
return controller
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}project.yml (Xcodegen Configuration):
name: StellarDemo
options:
bundleIdPrefix: com.soneso.demo
deploymentTarget:
iOS: 14.0
targets:
StellarDemo:
type: application
platform: iOS
sources:
- StellarDemo
dependencies:
- framework: ../shared/build/bin/iosSimulatorArm64/debugFramework/shared.framework
embed: true
preBuildScripts:
- script: |
cd "$SRCROOT/../../.."
./gradlew :demo:shared:linkDebugFrameworkIosSimulatorArm64
name: Build Kotlin FrameworkAll 11 features are implemented in the shared Kotlin module:
- Generate Ed25519 keypairs with iOS's SecureRandom (via libsodium)
- Copy keys to iOS clipboard
- Sign and verify data
- Request XLM from Friendbot
- Display funding status
- Error handling
- View account balances
- Display sequence number
- Show account flags
- Establish trustlines
- Build and sign transactions
- Submit to Horizon
- Transfer XLM or custom assets
- Amount validation
- Transaction signing
- Fetch and view transaction details from Horizon or Soroban RPC
- Display operations, events, and smart contract data
- Expandable operations and events with copy functionality
- Human-readable SCVal formatting
- Parse WASM contracts
- View contract metadata
- Display function specifications
- Upload and deploy WASM contracts from iOS
- Platform-specific resource loading from bundle
- One-step and two-step deployment support
- Invoke deployed "Hello World" contract
- Map-based argument conversion
- Automatic type handling
- Dynamic authorization handling
- Same-invoker vs different-invoker scenarios
- Conditional signing
- View:
InvokeTokenContractScreen.kt - SEP-41 token contract interaction
- Advanced multi-signature workflows with
buildInvoke() - Function selection from contract spec
- Automatic type conversion for token operations
- Uses:
ContractClient.buildInvoke(), token interface support
- SwiftUI: Modern declarative UI for app wrapper
- UIKit: UIViewController for Compose integration
- UIViewControllerRepresentable: Bridge between SwiftUI and Compose
- Compose Multiplatform: All UI screens and navigation
- Material 3: Material Design 3 components
- Voyager: Navigation library
- Stellar SDK: All Stellar functionality
- libsodium: Ed25519 cryptography (via Swift Package Manager)
- Package:
swift-sodium(provides bothClibsodiumandSodiumproducts) - Source: jedisct1/swift-sodium
- Clibsodium: C library used by Kotlin/Native code
- Sodium: Swift wrapper (optional, for Swift code)
- Package:
Add libsodium to your project:
- File → Add Packages...
- Search:
https://github.com/jedisct1/swift-sodium.git - Add Package: "Up to Next Major Version" from 0.9.1
- Products: Both
ClibsodiumandSodiumwill be added automatically (you cannot choose individual products)
Note: The swift-sodium package provides two products:
- Clibsodium: Required by the Kotlin/Native framework
- Sodium: Swift wrapper (not used by this demo, but included automatically)
Or add to Package.swift:
dependencies: [
.package(url: "https://github.com/jedisct1/swift-sodium.git", from: "0.9.1")
]The Kotlin framework is embedded in the app bundle:
- Debug:
shared.frameworkfromdebugFramework/ - Release:
shared.frameworkfromreleaseFramework/
Framework includes:
- All shared Compose UI code
- Stellar SDK functionality
- Navigation and theming
bundleIdPrefix: com.soneso.demo
# Full ID: com.soneso.demo.StellarDemoCFBundleShortVersionString: 1.0 # User-facing version
CFBundleVersion: 1 # Build numberdeploymentTarget:
iOS: 14.0UISupportedInterfaceOrientations:
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRightThe Info.plist must include CADisableMinimumFrameDurationOnPhone for Compose Multiplatform iOS apps:
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>This configuration is required by Compose Multiplatform's UIKit integration to disable CoreAnimation frame rate limiting. Without it, the app will crash with a PlistSanityCheck exception.
The project.yml includes this configuration automatically:
CADisableMinimumFrameDurationOnPhone: trueThe app connects to Stellar testnet by default:
- Horizon:
https://horizon-testnet.stellar.org - Soroban RPC:
https://soroban-testnet.stellar.org
No special configuration needed. HTTPS is used by default.
If needed, add to Info.plist:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
</dict>- Select simulator: Product → Destination → iOS Simulator → iPhone 15 Pro
- Run: ⌘R
- Test features: All 11 demo features should work on simulator
- Connect device via USB or WiFi
- Select team: Signing & Capabilities → Team → Select your Apple ID
- Select device: Product → Destination → Your Device Name
- Trust computer: Unlock device and tap "Trust"
- Run: ⌘R
- Trust developer: Settings → General → VPN & Device Management → Trust
- Simulator: iPhone 15 Pro (iOS 17.0+)
- Physical: iPhone 8 or newer (iOS 14.0+)
View logs in Xcode's console:
- Show console: View → Debug Area → Activate Console (⌘⇧Y)
- Filter logs: Type in the filter box (e.g., "Stellar", "KeyPair")
Set breakpoints in Swift code:
- Click line number in Swift files
- Run with debugger (⌘R)
Note: Cannot set breakpoints in Kotlin code from Xcode. Use print statements in Kotlin.
Print view hierarchy:
po view.recursiveDescription()
Print all logs:
# In Terminal, while app is running
xcrun simctl spawn booted log stream --predicate 'processImagePath contains "StellarDemo"'Framework not found:
# Rebuild the framework (from project root)
./gradlew :demo:shared:linkDebugFrameworkIosSimulatorArm64
# Regenerate Xcode project
cd demo/iosApp
xcodegen generatePre-build script fails:
- Check Gradle is accessible:
./gradlew --version - Ensure Java is installed:
java -version - Check script path in
project.ymlis correct
Architecture mismatch:
# Apple Silicon Simulator
./gradlew :demo:shared:linkDebugFrameworkIosSimulatorArm64
# Intel Simulator
./gradlew :demo:shared:linkDebugFrameworkIosX64
# Device (arm64 only)
./gradlew :demo:shared:linkDebugFrameworkIosArm64"No such module 'shared'":
- Clean build folder: Product → Clean Build Folder (⇧⌘K)
- Build the framework first:
./gradlew :demo:shared:linkDebugFrameworkIosSimulatorArm64 - Check framework path in Build Settings → Framework Search Paths
Code signing error:
- Select your team in Signing & Capabilities
- Change bundle ID if needed to avoid conflicts
- Ensure device is registered in developer portal
Simulator won't launch:
# Reset simulator
xcrun simctl erase all
# Kill simulator processes
killall SimulatorApp crashes on launch:
- Check Xcode console for stack traces
- Verify iOS version is 14.0+
- Ensure framework architecture matches destination (simulator vs device)
App crashes immediately (PlistSanityCheck exception):
This occurs when Info.plist is missing CADisableMinimumFrameDurationOnPhone:
# Solution: Regenerate project with proper configuration
cd demo/iosApp
xcodegen generateThe project.yml includes the required CADisableMinimumFrameDurationOnPhone configuration. See the "Compose Multiplatform Configuration" section above for details.
Crash logs location:
# View recent crash logs
ls -lt ~/Library/Logs/DiagnosticReports/ | grep StellarDemo | head -10
# Read crash log
cat ~/Library/Logs/DiagnosticReports/StellarDemo-*.ipsCompose UI not rendering:
- Verify
MainViewController()is called correctly - Check framework is embedded properly
- Update Compose version in shared module
Keyboard doesn't appear:
- Hardware → Keyboard → Toggle Software Keyboard (⌘K)
- Or use physical keyboard: Hardware → Keyboard → Connect Hardware Keyboard
Network errors:
- Check internet connection
- Verify Horizon URL is correct
- Check App Transport Security settings
- Debug: ~15 MB
- Release (optimized): ~10 MB
- First build: 2-3 minutes (including framework)
- Incremental build: 10-30 seconds
- Framework only: 30-60 seconds
- Startup time: 1-2 seconds (cold start)
- Frame rate: 60 FPS
- Memory usage: 60-100 MB
- Select device: Generic iOS Device
- Archive: Product → Archive
- Wait: Xcode builds and archives
- Organizer: Window → Organizer → Archives
- Select archive: Choose latest archive
- Distribute App: Click "Distribute App"
- Method: App Store Connect
- Options: Configure signing and options
- Upload: Upload to App Store Connect
Same process as App Store, but available immediately in TestFlight after processing.
- Method: Ad Hoc
- Export: Save .ipa file
- Distribute: Send to testers via email/link
- Install: Use TestFlight, Xcode, or third-party tools
- Create app in App Store Connect
- Archive and upload from Xcode
- Fill in metadata: Screenshots, description, keywords
- Submit for review
- Wait for approval (1-3 days typically)
- Upload build to App Store Connect
- Add testers: Internal or external
- Submit for beta review (external testers only)
- Distribute: Testers receive notification
- Enroll in Apple Developer Enterprise Program
- Archive with Enterprise certificate
- Export for Enterprise Distribution
- Host .ipa and manifest.plist
- Distribute link:
itms-services://?action=download-manifest&url=...
This project uses xcodegen to generate the Xcode project from project.yml.
- Version control friendly:
.xcodeprojis gitignored - No merge conflicts: Xcode project is generated, not edited
- Declarative configuration: Simple YAML instead of complex XML
- Reproducible builds: Same project every time
cd demo/iosApp
xcodegen generateRun this whenever you:
- Pull changes from git
- Modify
project.yml - Add/remove files
- Change build settings
Edit project.yml to change:
- Targets: Add new targets
- Build settings: Compiler flags, optimization
- Dependencies: Frameworks, libraries
- Scripts: Pre/post build scripts
See xcodegen documentation for all options.
For issues:
- iOS-specific: Check this README and Xcode console
- Framework build: Check Gradle output and shared module
- SDK functionality: See main SDK documentation
- Stellar protocol: Visit developers.stellar.org
Part of the Stellar KMP SDK project. See main repository for license details.