iOS Phase 2: Build Error Resolution Session
Date: November 18, 2025 Session Type: Debugging & Build Fixes Scope: iOS Phase 2 post-merge integration issues PR: #278 - iOS Phase 2 build errors and Firebase initialization
Overview
This session focused on resolving build and runtime errors discovered after merging iOS Phase 2 PRs (#275 TournamentDetailViewModel, #276 TournamentDetailView). The integration testing phase revealed three categories of issues that required iterative debugging and fixes.
Context
Merged PRs
PR #275: TournamentDetailViewModel.swift (Agent 2/AAM)
- Firebase Firestore integration
- Tournament detail loading, join/leave operations
- Transaction-based participant management
PR #276: TournamentDetailView.swift (Agent 1/AAP)
- SwiftUI detail screen
- Navigation from TournamentListView
- UI components for tournament display and actions
Integration Status Pre-Session
- ✅ Code reviews completed
- ✅ Integration compatibility verified
- ✅ Both PRs merged to main
- ❌ Build errors on iOS framework compilation
- ❌ Runtime crashes in iOS app
Issues Discovered & Fixed
Issue 1: KMP-NativeCoroutines Plugin Compatibility
Error Message:
NoSuchMethodError: 'void org.gradle.api.provider.Property.convention(Object)'
Symptoms:
- iOS framework build failed during KMP compilation
- Gradle task
:shared:domain:podGenIOSfailed - Build worked in previous sessions but broke after dependency updates
Root Cause:
kmp-nativecoroutinesplugin versionALPHA-39incompatible with Kotlin2.2.21- Plugin uses deprecated Gradle API methods
- Version mismatch between Kotlin compiler and coroutines plugin
Solution:
- Updated
kmp-nativecoroutinesto versionALPHA-48ingradle/libs.versions.toml ALPHA-48includes fixes for Kotlin 2.2.x compatibility
Files Changed:
gradle/libs.versions.toml
Learning:
- KMP ecosystem requires close version alignment between Kotlin, coroutines plugin, and Gradle
ALPHAversions may have breaking changes between releases- Check plugin compatibility when updating Kotlin versions
Issue 2: Swift Type Conversion Errors (7 Total)
After resolving the build framework issue, Swift compilation revealed multiple type conversion errors in the iOS code.
Error Category A: Unused Value Bindings (2 errors)
Location: TournamentDetailViewModel.swift
Error Messages:
// Line 68
Immutable value 'tournament' was never used; consider replacing with '_' or removing it
// Line 139
Immutable value 'tournament' was never used; consider replacing with '_' or removing itProblem:
guard let tournament = tournament else { return }pattern used- Variable binding created but never referenced in subsequent code
- Only needed nil check, not the unwrapped value
Solution:
// Before
guard let tournament = tournament else {
errorMessage = "Tournament not loaded"
return
}
// After
guard tournament != nil else {
errorMessage = "Tournament not loaded"
return
}Files Changed:
iosApp/ArcheryApprentice/ArcheryApprentice/TournamentDetailViewModel.swift(lines 68, 139)
Error Category B: Swift Int to Kotlin Int32 Conversion (3 errors)
Location: TournamentDetailViewModel.swift
Error Messages:
// Lines 298, 299, 313
Cannot convert value of type 'Int' to expected argument type 'Int32'Problem:
- Swift
Intdefaults to platform word size (64-bit on modern iOS) - Kotlin
Intis always 32-bit - KMP shared module expects
Int32for Kotlin interop - Direct assignment causes type mismatch
Solution:
// Before
currentParticipants: currentParticipants,
maxParticipants: maxParticipants,
// After
currentParticipants: Int32(currentParticipants),
maxParticipants: Int32(maxParticipants),Files Changed:
iosApp/ArcheryApprentice/ArcheryApprentice/TournamentDetailViewModel.swift(lines 298, 299, 313)
Pattern:
- Always wrap Swift
IntinInt32()when passing to Kotlin types - Applies to: participant counts, array sizes, numeric fields from Firebase
Error Category C: KotlinLong to Int64 Conversion (1 error)
Location: TournamentDetailView.swift
Error Message:
// Line 107
Value of type 'KotlinLong?' has no member 'longValue'Problem:
- Attempted to use
.longValueproperty which doesn’t exist KotlinLonguses.int64Valuefor Swift interop- Different naming convention than expected
Solution:
// Before
let date = Date(timeIntervalSince1970: Double(timestamp.longValue) / 1000.0)
// After
let date = Date(timeIntervalSince1970: Double(timestamp.int64Value) / 1000.0)Files Changed:
iosApp/ArcheryApprentice/ArcheryApprentice/TournamentDetailView.swift(line 107)
Pattern:
- Use
.int64ValueforKotlinLong→ SwiftInt64conversion - Use
.int32ValueforKotlinInt→ SwiftInt32conversion
Error Category D: Property Name Mismatch (1 error)
Location: TournamentDetailView.swift
Error Message:
// Line 181
Value of type 'Tournament' has no member 'public_'Problem:
- Kotlin property
isPublic: Boolean - Swift code tried to access
public_(old naming convention) - Property name changed in shared module but iOS code not updated
Solution:
// Before
if tournament.public_ {
// ...
}
// After
if tournament.isPublic {
// ...
}Files Changed:
iosApp/ArcheryApprentice/ArcheryApprentice/TournamentDetailView.swift(line 181)
Pattern:
- Check Kotlin property names in shared module when accessing from Swift
- Kotlin
isPublic→ SwiftisPublic(no underscore suffix)
Issue 3: Firebase Initialization Issues
After fixing all build errors, runtime testing revealed Firebase initialization crashes.
Error Message (Runtime):
Fatal error: Failed to get FirebaseApp instance. Please call FirebaseApp.configure() before using Firebase.
Symptoms:
- App launched successfully
- Crash when navigating to TournamentListView
- Firestore queries attempted before Firebase initialization
Root Cause Analysis:
Problem 1: Missing GoogleService-Info.plist
- File required for Firebase iOS SDK configuration
- Contains API keys, project ID, bundle ID mappings
- Gitignored for security (not in repository)
- Each developer needs their own copy
Problem 2: Improper initialization logic in ArcheryApprenticeApp.swift
// Original (WRONG)
guard let _ = FirebaseApp.app() else {
print("⚠️ Firebase not configured")
return AnyView(Text("Firebase not configured"))
}
// Execution stops here if Firebase not configured
// WindowGroup never reachedProblem 3: No graceful fallback in ViewModels
- TournamentListViewModel immediately queries Firestore
- TournamentDetailViewModel immediately queries Firestore
- No check for Firebase availability
- Caused crashes when Firebase not configured
Solutions Applied:
Fix 1: Updated ArcheryApprenticeApp.swift initialization logic
// New (CORRECT)
@main
struct ArcheryApprenticeApp: App {
init() {
if FirebaseApp.app() == nil {
FirebaseApp.configure()
}
}
var body: some Scene {
WindowGroup {
if FirebaseApp.app() != nil {
ContentView()
} else {
VStack {
Text("⚠️ Firebase Configuration Required")
// ... instructions
}
}
}
}
}Changes:
- Use
if/elseinstead ofguard/return - Allow
WindowGroupto render regardless of Firebase status - Show configuration instructions if Firebase not available
- App remains functional (doesn’t crash)
Fix 2: Added Firebase availability checks in ViewModels
TournamentListViewModel.swift:
func loadTournaments() {
guard FirebaseApp.app() != nil else {
self.errorMessage = "Firebase not configured. Add GoogleService-Info.plist to project."
self.isLoading = false
return
}
// Proceed with Firestore query...
}TournamentDetailViewModel.swift:
func loadTournamentDetails() {
guard FirebaseApp.app() != nil else {
self.errorMessage = "Firebase not configured. Add GoogleService-Info.plist to project."
self.isLoading = false
return
}
// Proceed with Firestore query...
}Benefits:
- Graceful error messages instead of crashes
- Users see clear instructions
- App remains usable for non-Firebase features
- Developers can run app without Firebase setup
Files Changed:
iosApp/ArcheryApprentice/ArcheryApprentice/ArcheryApprenticeApp.swiftiosApp/ArcheryApprentice/ArcheryApprentice/TournamentListViewModel.swiftiosApp/ArcheryApprentice/ArcheryApprentice/TournamentDetailViewModel.swift
Documentation Created:
- Firebase setup instructions in error message UI
- Developer guide entry (see ios-firebase-setup)
Summary of Changes
Files Modified (9 total)
Gradle Configuration:
gradle/libs.versions.toml- Updated kmp-nativecoroutines to ALPHA-48
iOS Swift Files:
2. iosApp/ArcheryApprentice/ArcheryApprentice/ArcheryApprenticeApp.swift - Firebase init logic
3. iosApp/ArcheryApprentice/ArcheryApprentice/TournamentListViewModel.swift - Firebase availability check
4. iosApp/ArcheryApprentice/ArcheryApprentice/TournamentDetailViewModel.swift - Type conversions + Firebase checks
5. iosApp/ArcheryApprentice/ArcheryApprentice/TournamentDetailView.swift - Type conversions + property names
Build Status
Before Session:
- ❌ iOS framework build: FAILED (KMP plugin error)
- ❌ Swift compilation: FAILED (7 type errors)
- ❌ Runtime: CRASHED (Firebase not initialized)
After Session:
- ✅ iOS framework build: SUCCESS
- ✅ Swift compilation: SUCCESS (all 7 errors fixed)
- ✅ Runtime: GRACEFUL (shows Firebase config instructions)
Testing Performed
-
Build Verification:
- Clean build with
./gradlew clean build - iOS framework generation successful
- Xcode compilation successful
- Clean build with
-
Runtime Testing (Firebase not configured):
- App launches successfully
- Shows Firebase configuration message
- No crashes
- Graceful error handling
-
Integration Points Verified:
- Navigation from TournamentListView works
- TournamentDetailView renders correctly
- Type conversions working (Swift ↔ Kotlin)
Key Learnings
Swift-Kotlin Type Interoperability
Type Mapping Patterns:
// Kotlin → Swift
KotlinInt → Int32 (use .int32Value)
KotlinLong → Int64 (use .int64Value)
Kotlin String → String (direct)
Kotlin Boolean→ Bool (direct)
// Swift → Kotlin (for Shared module)
Swift Int → Int32 (wrap with Int32())
Swift Int64 → Long (wrap with KotlinLong(longLong:))
Swift String → String (direct)
Swift Bool → Boolean(direct)Property Access:
- Kotlin
isPublic: Boolean→ SwiftisPublic(no underscore) - Kotlin
public: Boolean→ Swiftpublic_(keyword collision, suffix added)
See: kotlin-swift-type-mappings
Firebase iOS Setup Requirements
Required Files:
GoogleService-Info.plist- Must be in Xcode project- Firebase SDK dependencies - In Podfile/SPM
- Initialization code - In app entry point
Best Practices:
- Check Firebase availability before Firestore queries
- Provide clear error messages for missing configuration
- Don’t crash if Firebase not configured
- Allow app to run in degraded mode
See: ios-firebase-setup
KMP Plugin Version Management
Critical Dependencies:
- Kotlin compiler version
- kmp-nativecoroutines plugin version
- Gradle version
- Must maintain compatibility across all three
When Updating Kotlin:
- Check kmp-nativecoroutines release notes
- Update plugin to compatible ALPHA version
- Test iOS framework build
- Document version combinations that work
Related Documentation
- week-28-ios-firebase-integration - Architecture decisions
- kotlin-swift-interop - Interop patterns and best practices
- ios-firebase-setup - Firebase configuration guide
Follow-Up Items
- Add GoogleService-Info.plist template to repository
- Create developer onboarding doc for iOS setup
- Document working Kotlin + kmp-nativecoroutines version combinations
- Consider CI check for Swift type conversions
References
- Main PR: #278 - iOS Phase 2 build errors and Firebase initialization
- Phase 2 PRs: #275 (ViewModel), #276 (View)
- Branch:
fix/ios-phase2-build-errors