Home > Developer Guide > Guides > iOS Firebase Setup


iOS Firebase Setup Guide

Purpose: Configure Firebase for iOS development and understand the initialization patterns used in the app

Context: Documented during iOS Phase 2 build error resolution (PR #278)

Impact: Proper Firebase setup prevents runtime crashes and enables Firestore functionality

Overview

The Archery Apprentice iOS app uses Firebase Firestore for backend data storage. This guide covers:

  1. Required files and configuration
  2. Firebase initialization patterns
  3. Error handling approaches
  4. Development workflow considerations

Prerequisites

  • Xcode installed
  • CocoaPods or Swift Package Manager configured
  • Access to Firebase project (or ability to create one)
  • iOS app bundle ID configured

Required Files

1. GoogleService-Info.plist

Purpose: Contains Firebase project configuration (API keys, project ID, bundle ID mappings)

Location: iosApp/ArcheryApprentice/GoogleService-Info.plist

Why It’s Not in the Repository:

  • Contains sensitive Firebase configuration
  • Gitignored for security (.gitignore entry: GoogleService-Info.plist)
  • Each developer may use different Firebase projects (dev/staging/prod)

How to Obtain:

  1. Go to Firebase Console
  2. Select your project (or create a new one)
  3. Navigate to Project Settings → General
  4. Scroll to “Your apps” section
  5. Click on iOS app (or add iOS app if not exists)
  6. Download GoogleService-Info.plist

How to Add to Xcode:

  1. Download GoogleService-Info.plist
  2. Open Xcode project: iosApp/ArcheryApprentice/ArcheryApprentice.xcworkspace
  3. Drag GoogleService-Info.plist into Xcode project navigator
  4. Ensure “Copy items if needed” is checked
  5. Ensure target “ArcheryApprentice” is selected
  6. Build and run

2. Firebase SDK Dependencies

Already Configured in the project via CocoaPods/SPM:

  • FirebaseCore - Core Firebase functionality
  • FirebaseFirestore - Cloud Firestore database
  • FirebaseAuth - User authentication

No additional installation needed - these are managed by the build system.

Firebase Initialization Pattern

The app uses a graceful initialization pattern that prevents crashes when Firebase is not configured.

App Entry Point (ArcheryApprenticeApp.swift)

import SwiftUI
import FirebaseCore
 
@main
struct ArcheryApprenticeApp: App {
 
    init() {
        // Attempt to configure Firebase if not already configured
        if FirebaseApp.app() == nil {
            FirebaseApp.configure()
        }
    }
 
    var body: some Scene {
        WindowGroup {
            // Check if Firebase is available before showing main UI
            if FirebaseApp.app() != nil {
                ContentView()
            } else {
                // Show configuration instructions instead of crashing
                VStack(spacing: 20) {
                    Image(systemName: "exclamationmark.triangle")
                        .font(.system(size: 60))
                        .foregroundColor(.orange)
 
                    Text("Firebase Configuration Required")
                        .font(.title)
                        .fontWeight(.bold)
 
                    Text("To use tournament features, add GoogleService-Info.plist to the Xcode project:")
                        .multilineTextAlignment(.center)
                        .padding(.horizontal)
 
                    VStack(alignment: .leading, spacing: 8) {
                        Text("1. Download GoogleService-Info.plist from Firebase Console")
                        Text("2. Drag the file into Xcode project navigator")
                        Text("3. Ensure 'Copy items if needed' is checked")
                        Text("4. Rebuild and run the app")
                    }
                    .font(.subheadline)
                    .padding()
                    .background(Color.gray.opacity(0.1))
                    .cornerRadius(8)
                }
                .padding()
            }
        }
    }
}

Key Points:

  • ✅ Uses if/else instead of guard/return to allow WindowGroup to render
  • ✅ App launches successfully even without Firebase
  • ✅ Shows clear error message with setup instructions
  • ✅ No crashes, graceful degradation

ViewModel Firebase Availability Checks

Each ViewModel that uses Firestore should check Firebase availability before attempting queries.

Pattern:

import FirebaseCore
import FirebaseFirestore
import FirebaseAuth
 
@MainActor
class TournamentListViewModel: ObservableObject {
    @Published var tournaments: [Tournament] = []
    @Published var isLoading: Bool = false
    @Published var errorMessage: String? = nil
 
    func loadTournaments() {
        // Check Firebase availability first
        guard FirebaseApp.app() != nil else {
            self.errorMessage = "Firebase not configured. Add GoogleService-Info.plist to project."
            self.isLoading = false
            return
        }
 
        // Firebase is available, proceed with query
        isLoading = true
        errorMessage = nil
 
        let db = Firestore.firestore()
        db.collection("tournaments")
            .getDocuments { [weak self] (snapshot, error) in
                // Handle query results...
            }
    }
}

Applied in:

  • TournamentListViewModel.swift - Tournament list loading
  • TournamentDetailViewModel.swift - Tournament detail loading, join/leave operations

Benefits:

  • Prevents crashes when Firebase not configured
  • Provides clear user feedback
  • Allows development without Firebase (mocking, UI testing)
  • Graceful error handling

Development Workflow

Scenario 1: First Time Setup

# 1. Clone repository
git clone https://github.com/blamechris/archery-apprentice.git
cd archery-apprentice
 
# 2. Build shared KMP framework
./gradlew :shared:domain:podGenIOS
 
# 3. Install iOS dependencies
cd iosApp/ArcheryApprentice
pod install  # or Swift Package Manager resolve
 
# 4. Open Xcode workspace
open ArcheryApprentice.xcworkspace
 
# 5. Add GoogleService-Info.plist
# - Download from Firebase Console
# - Drag into Xcode project
# - Ensure "Copy items if needed" is checked
 
# 6. Build and run
# - Select target device/simulator
# - Cmd+R to build and run

Scenario 2: Working Without Firebase

If you want to work on UI features without Firebase:

Option A: App shows configuration message

  • Firebase check fails gracefully
  • App shows setup instructions
  • Can still test non-Firebase features

Option B: Use mock data

  • Create mock ViewModels with hardcoded data
  • Swap in ContentView for testing
  • No Firebase calls attempted

Common Issues & Solutions

Issue 1: “Firebase not configured” at Runtime

Symptom: App shows Firebase configuration required message

Cause: Missing GoogleService-Info.plist

Solution:

  1. Download GoogleService-Info.plist from Firebase Console
  2. Add to Xcode project (drag and drop)
  3. Ensure “Copy items if needed” is checked
  4. Rebuild app

Issue 2: Build Succeeds But Crashes on Launch

Symptom: App builds successfully but crashes immediately when running

Cause: Firebase SDK not initialized, or initialization logic uses guard/return preventing WindowGroup from rendering

Solution: Verify ArcheryApprenticeApp.swift uses the pattern shown above:

// ✅ CORRECT - Uses if/else
if FirebaseApp.app() != nil {
    ContentView()
} else {
    // Show error message
}
 
// ❌ WRONG - Uses guard/return
guard FirebaseApp.app() != nil else {
    return AnyView(Text("Error"))  // WindowGroup never reached
}

Issue 3: Firebase Queries Return Empty Results

Symptom: App runs, Firebase configured, but queries return no data

Possible Causes:

  1. Wrong Firebase project: Using dev project but querying prod data (or vice versa)
  2. Firestore rules: Security rules preventing read access
  3. Collection names: Querying wrong collection name
  4. Network connectivity: Device/simulator can’t reach Firebase

Debugging:

// Add logging to see what's happening
func loadTournaments() {
    guard FirebaseApp.app() != nil else { return }
 
    let db = Firestore.firestore()
    print("Firebase project ID: \(FirebaseApp.app()?.options.projectID ?? "unknown")")
 
    db.collection("tournaments")
        .getDocuments { (snapshot, error) in
            if let error = error {
                print("Firestore error: \(error.localizedDescription)")
            } else {
                print("Retrieved \(snapshot?.documents.count ?? 0) documents")
            }
        }
}

Issue 4: Multiple Firebase Apps Configured

Symptom: Warning “Firebase app named __FIRAPP_DEFAULT already exists”

Cause: FirebaseApp.configure() called multiple times

Solution: Check before configuring (already implemented):

init() {
    if FirebaseApp.app() == nil {
        FirebaseApp.configure()
    }
}

Security Considerations

GoogleService-Info.plist

DO NOT:

  • ❌ Commit GoogleService-Info.plist to git
  • ❌ Share the file publicly
  • ❌ Use production Firebase config in development

DO:

  • ✅ Keep file in .gitignore
  • ✅ Use separate Firebase projects for dev/staging/prod
  • ✅ Rotate API keys if accidentally exposed
  • ✅ Configure Firestore security rules appropriately

Firestore Security Rules

Production Security Rules (example):

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Tournaments: public read, authenticated write
    match /tournaments/{tournamentId} {
      allow read: if true;
      allow create: if request.auth != null;
      allow update, delete: if request.auth.uid == resource.data.createdBy;
    }
  }
}

Development Security Rules (more permissive for testing):

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // WARNING: Only use for development!
    match /{document=**} {
      allow read, write: if true;
    }
  }
}

Testing Firebase Integration

Manual Testing Checklist

  • App launches without Firebase configured (shows error message)
  • App launches with Firebase configured (shows ContentView)
  • TournamentListView loads tournaments from Firestore
  • TournamentDetailView displays tournament details
  • Join tournament operation succeeds
  • Leave tournament operation succeeds
  • Error messages display correctly when Firebase unavailable

Automated Testing (Future Enhancement)

Consider adding:

  • Unit tests with mock Firebase SDK
  • Integration tests with Firebase emulator
  • UI tests with stubbed network responses

Session Documentation:

Architecture Documentation:

Interop Documentation:

External Resources:

Summary

Firebase setup for iOS requires:

  1. GoogleService-Info.plist added to Xcode project
  2. ✅ Graceful initialization in ArcheryApprenticeApp.swift
  3. ✅ Firebase availability checks in ViewModels
  4. ✅ Proper error handling throughout

The app is designed to work gracefully without Firebase, showing clear setup instructions instead of crashing. This allows development to continue even when Firebase is not configured.


Last Updated: 2025-11-18 Related PR: #278 (iOS Phase 2 build error fixes)