There is a possibility that Scala Native could be compiled for iOS - with no support/bindings for ts frameworks, but still. If it's true, we could just draw on a canvas, since we are a game library, and that would not require integrating with any of these frameworks.
But there are possible blockers. They are provided by Claude, so they might be wrong, hallucinated, etc - cc @WojciechMazur
iOS Platform: Blockers and Path Forward
Supplements ios-backend-feasibility.md with actionable upstream blocker details.
Current status
SGE has 14 backend files marked as "deferred (iOS)". The existing ios-backend-feasibility.md documents the overall architecture. This document focuses on the concrete blockers in Scala Native and what we need from upstream.
Scala Native upstream blockers
Blocker 1: Immix GC heap allocation crashes on iOS (High)
Issue: scala-native#4334
Heap_getMemoryLimit() returns full device physical RAM via sysctl(HW_MEMSIZE). The Immix GC then calls mmap twice with that size (for smallHeap and largeHeap). On a 6GB iPhone this tries to mmap ~12GB — iOS kills the process with EXC_BAD_ACCESS.
Current workarounds:
GC_MAXIMUM_HEAP_SIZE environment variable to cap heap size
- Boehm GC (
nativeConfig ~= { _.withGC(GC.boehm) }) with tuned limits
GC.none (no collection — unacceptable for games)
Proper fix: Heap_getMemoryLimit() should use os_proc_available_memory() on iOS (available since iOS 13) instead of sysctl(HW_MEMSIZE). This is a targeted C-level patch.
File: nativelib/src/main/resources/scala-native/gc/immix/Heap.c (or similar — the heap limit function)
Blocker 2: C runtime preprocessor guards don't recognize iOS (Medium)
Issue: scala-native#2875
Two files fail on iOS SDK:
time_nano.c: Uses __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ which is undefined on iOS. The static_assert fires: "macOS version must be 10.12 or greater".
Fix: Add iOS variant:
#if defined(__APPLE__)
#if defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
// iOS: clock_gettime available since iOS 10
#elif defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
// macOS: existing check
#endif
#endif
process_monitor.cpp: Includes <sys/posix_sem.h> which doesn't exist on iOS. Apple removed POSIX semaphores from the iOS SDK.
Fix: Use dispatch_semaphore_t (Grand Central Dispatch) on iOS, or #ifdef out process monitoring entirely (games don't fork child processes).
Blocker 3: No LinktimeInfo.isIOS (Low)
Scala Native has LinktimeInfo.isWindows, isMac, isLinux, isFreeBSD but no isIOS. This prevents compile-time platform branching for iOS-specific code paths.
Fix: Add isIOS following the same pattern as PR #2809 which added isFreeBSD.
Blocker 4: Discover.scala hardcodes macOS include paths (Low)
When the target triple contains apple-ios, Discover.scala should use xcrun --sdk iphoneos --show-sdk-path for include paths instead of macOS Xcode paths. This may already be handled by clang when given the correct -isysroot flag, but should be verified.
What is NOT a blocker
| Concern |
Why it's fine |
| JIT restrictions |
Scala Native is fully AOT — no JIT needed |
| Code signing |
Scala Native produces .a static library; signing happens at Xcode level |
| Bitcode |
Deprecated in Xcode 14, removed in Xcode 16 |
| Exception handling |
iOS ARM64 uses same unwinding as macOS ARM64 (LLVM handles it) |
setjmp.S assembly |
The __aarch64__ block works on iOS — same Unix ARM64 ABI as macOS |
| Static library output |
BuildTarget.libraryStatic exists since Scala Native 0.4.8 |
@exported FFI |
Works — provides C-compatible entry points callable from Swift/ObjC |
Rust native libraries for iOS
All SGE Rust libraries can target iOS. Rust has Tier 2 support for iOS:
rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
cargo build --target aarch64-apple-ios --release
Per-library assessment:
| Library |
iOS viable? |
Notes |
| Buffer ops / ETC1 |
Yes |
Pure Rust/C, platform-independent |
| GLFW |
No |
Desktop-only. Replace with SDL3 on iOS |
| miniaudio |
Yes |
Core Audio backend for iOS. Build with -DMA_NO_RUNTIME_LINKING, link -framework CoreFoundation -framework CoreAudio -framework AudioToolbox |
| FreeType |
Yes |
Well-established iOS cross-compilation. Pre-built xcframeworks available |
| Rapier2D / 3D |
Yes |
Pure Rust, no platform dependencies, no_std compatible |
Distribution: Static libraries (.a) bundled into .xcframework format (Apple's standard for multi-architecture libraries).
multiarch-scala changes needed
New Platform entries
case object IosAarch64 extends Platform("ios-aarch64", "aarch64-apple-ios")
case object IosSimArm64 extends Platform("ios-sim-aarch64", "aarch64-apple-ios-sim")
case object IosSimX86_64 extends Platform("ios-sim-x86_64", "x86_64-apple-ios")
val ios: Seq[Platform] = Seq(IosAarch64, IosSimArm64, IosSimX86_64)
Note: Rust uses aarch64-apple-ios-sim while Scala Native uses aarch64-apple-ios-simulator — this discrepancy needs reconciliation in the Platform mapping.
NativeProviderPlugin for iOS
Same pattern as desktop Scala Native: provider JARs contain .a files + sn-provider.json manifest. iOS-specific link flags (e.g., -framework CoreAudio) go in the manifest.
iOS backend design (SGE-specific)
From ios-backend-feasibility.md, ~8 iOS-specific files needed:
| Subsystem |
Desktop (GLFW) |
iOS |
Shared code? |
| Windowing/Lifecycle |
GLFW |
SDL3 (handles UIKit internally) |
No |
| GL rendering |
ANGLE (GL ES → desktop GL) |
ANGLE (GL ES → Metal) |
Yes — same GL20/GL30 code |
| Audio |
miniaudio |
miniaudio (Core Audio backend) |
Yes — same wrapper |
| Physics |
Rapier |
Rapier |
Yes — identical |
| Files |
POSIX filesystem |
NSBundle + Documents dir |
No |
| Input |
GLFW keyboard + mouse |
SDL3 touch + accelerometer |
No |
ANGLE provides OpenGL ES → Metal translation on iOS, so SGE's entire GL rendering stack works unchanged. Metal is required — Apple deprecated OpenGL ES in iOS 12 (2018).
CI testing
iOS simulators run on macOS CI runners (macos-latest). The workflow:
- Build Scala Native static library for
aarch64-apple-ios-simulator
- Build Xcode test project linking the
.a file
- Run in simulator:
xcodebuild test -destination 'platform=iOS Simulator,name=iPhone 15'
What we need from Scala Native maintainers
Concrete patches (estimated <500 lines total):
Heap_getMemoryLimit(): Use os_proc_available_memory() on iOS
time_nano.c: Add __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ check
process_monitor.cpp: Use dispatch_semaphore_t on iOS or exclude process monitoring
LinktimeInfo: Add isIOS property
Discover.scala: iOS SDK path detection via xcrun --sdk iphoneos
The risk is that maintainers may not accept these without a full iOS CI matrix (they won't maintain what they can't test). We may need to offer CI resources or maintain a fork.
Comparison with Kotlin Native
Kotlin/Native has production-ready iOS support:
- Official targets:
iosArm64, iosSimulatorArm64, iosX64
- Generates
.framework bundles directly consumable by Xcode
- Bi-directional Swift/ObjC interop
- Custom tracing GC (replaced reference counting in 2023)
- Hundreds of production apps in the App Store
Scala Native is years behind. The patches above would bring it to "functional with caveats" — not production-grade.
Comparison with LibGDX's approach
LibGDX uses MobiVM (community fork of discontinued RoboVM): Java bytecode → LLVM IR → native ARM binary. It works but has a very small maintainer pool and uses deprecated Apple APIs (GLKit, EAGLContext). The MetalANGLE backend partially addresses this.
SGE's Scala Native approach would be more maintainable long-term since it compiles directly to LLVM IR without an intermediate bytecode translation layer.
References
There is a possibility that Scala Native could be compiled for iOS - with no support/bindings for ts frameworks, but still. If it's true, we could just draw on a canvas, since we are a game library, and that would not require integrating with any of these frameworks.
But there are possible blockers. They are provided by Claude, so they might be wrong, hallucinated, etc - cc @WojciechMazur
iOS Platform: Blockers and Path Forward
Supplements ios-backend-feasibility.md with actionable upstream blocker details.
Current status
SGE has 14 backend files marked as "deferred (iOS)". The existing ios-backend-feasibility.md documents the overall architecture. This document focuses on the concrete blockers in Scala Native and what we need from upstream.
Scala Native upstream blockers
Blocker 1: Immix GC heap allocation crashes on iOS (High)
Issue: scala-native#4334
Heap_getMemoryLimit()returns full device physical RAM viasysctl(HW_MEMSIZE). The Immix GC then callsmmaptwice with that size (forsmallHeapandlargeHeap). On a 6GB iPhone this tries tommap~12GB — iOS kills the process withEXC_BAD_ACCESS.Current workarounds:
GC_MAXIMUM_HEAP_SIZEenvironment variable to cap heap sizenativeConfig ~= { _.withGC(GC.boehm) }) with tuned limitsGC.none(no collection — unacceptable for games)Proper fix:
Heap_getMemoryLimit()should useos_proc_available_memory()on iOS (available since iOS 13) instead ofsysctl(HW_MEMSIZE). This is a targeted C-level patch.File:
nativelib/src/main/resources/scala-native/gc/immix/Heap.c(or similar — the heap limit function)Blocker 2: C runtime preprocessor guards don't recognize iOS (Medium)
Issue: scala-native#2875
Two files fail on iOS SDK:
time_nano.c: Uses__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__which is undefined on iOS. Thestatic_assertfires: "macOS version must be 10.12 or greater".Fix: Add iOS variant:
process_monitor.cpp: Includes<sys/posix_sem.h>which doesn't exist on iOS. Apple removed POSIX semaphores from the iOS SDK.Fix: Use
dispatch_semaphore_t(Grand Central Dispatch) on iOS, or#ifdefout process monitoring entirely (games don't fork child processes).Blocker 3: No
LinktimeInfo.isIOS(Low)Scala Native has
LinktimeInfo.isWindows,isMac,isLinux,isFreeBSDbut noisIOS. This prevents compile-time platform branching for iOS-specific code paths.Fix: Add
isIOSfollowing the same pattern as PR #2809 which addedisFreeBSD.Blocker 4:
Discover.scalahardcodes macOS include paths (Low)When the target triple contains
apple-ios,Discover.scalashould usexcrun --sdk iphoneos --show-sdk-pathfor include paths instead of macOS Xcode paths. This may already be handled by clang when given the correct-isysrootflag, but should be verified.What is NOT a blocker
.astatic library; signing happens at Xcode levelsetjmp.Sassembly__aarch64__block works on iOS — same Unix ARM64 ABI as macOSBuildTarget.libraryStaticexists since Scala Native 0.4.8@exportedFFIRust native libraries for iOS
All SGE Rust libraries can target iOS. Rust has Tier 2 support for iOS:
Per-library assessment:
-DMA_NO_RUNTIME_LINKING, link-framework CoreFoundation -framework CoreAudio -framework AudioToolboxno_stdcompatibleDistribution: Static libraries (
.a) bundled into.xcframeworkformat (Apple's standard for multi-architecture libraries).multiarch-scala changes needed
New Platform entries
Note: Rust uses
aarch64-apple-ios-simwhile Scala Native usesaarch64-apple-ios-simulator— this discrepancy needs reconciliation in the Platform mapping.NativeProviderPlugin for iOS
Same pattern as desktop Scala Native: provider JARs contain
.afiles +sn-provider.jsonmanifest. iOS-specific link flags (e.g.,-framework CoreAudio) go in the manifest.iOS backend design (SGE-specific)
From ios-backend-feasibility.md, ~8 iOS-specific files needed:
ANGLE provides OpenGL ES → Metal translation on iOS, so SGE's entire GL rendering stack works unchanged. Metal is required — Apple deprecated OpenGL ES in iOS 12 (2018).
CI testing
iOS simulators run on macOS CI runners (
macos-latest). The workflow:aarch64-apple-ios-simulator.afilexcodebuild test -destination 'platform=iOS Simulator,name=iPhone 15'What we need from Scala Native maintainers
Concrete patches (estimated <500 lines total):
Heap_getMemoryLimit(): Useos_proc_available_memory()on iOStime_nano.c: Add__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__checkprocess_monitor.cpp: Usedispatch_semaphore_ton iOS or exclude process monitoringLinktimeInfo: AddisIOSpropertyDiscover.scala: iOS SDK path detection viaxcrun --sdk iphoneosThe risk is that maintainers may not accept these without a full iOS CI matrix (they won't maintain what they can't test). We may need to offer CI resources or maintain a fork.
Comparison with Kotlin Native
Kotlin/Native has production-ready iOS support:
iosArm64,iosSimulatorArm64,iosX64.frameworkbundles directly consumable by XcodeScala Native is years behind. The patches above would bring it to "functional with caveats" — not production-grade.
Comparison with LibGDX's approach
LibGDX uses MobiVM (community fork of discontinued RoboVM): Java bytecode → LLVM IR → native ARM binary. It works but has a very small maintainer pool and uses deprecated Apple APIs (GLKit, EAGLContext). The MetalANGLE backend partially addresses this.
SGE's Scala Native approach would be more maintainable long-term since it compiles directly to LLVM IR without an intermediate bytecode translation layer.
References