Case Study: ASimpleAuthKit
Production-Ready iOS Authentication Library
A comprehensive Swift Package that simplifies Firebase Authentication integration for iOS apps, providing SwiftUI-friendly APIs, biometric authentication, account linking, and enterprise-grade security features.
The Challenge
Firebase Authentication is powerful but was designed in an era before SwiftUI and modern Swift concurrency. This creates a significant architectural mismatch for modern iOS apps. After building several applications with it, I repeatedly encountered the same pain points:
- Architectural Mismatch with SwiftUI: Firebase's imperative, singleton-based APIs are fundamentally incompatible with SwiftUI's declarative, state-driven nature. This forces developers to write complex, bug-prone bridging code.
- "Callback Hell": The SDK relies heavily on nested completion handlers (callbacks), which are difficult to reason about and lead to unmaintainable code, a problem that modern
async/await
is designed to solve. - Account Linking Complexity: The multi-step account linking flow is managed through callbacks and global state, making it extremely difficult to represent cleanly in a SwiftUI view hierarchy.
- Untestable by Design: The heavy use of global singletons (
Auth.auth()
) makes the SDK nearly impossible to mock, preventing isolated and reliable unit testing of an app's authentication logic. - Generic Error UX: Firebase returns cryptic error codes (e.g.,
invalid-credential
) that confuse users instead of guiding them toward a solution.
The goal was to create a modern adapter or bridge—a reusable library that translates Firebase's difficult APIs into a system that feels truly native to SwiftUI and Swift Concurrency.
My Approach
I designed ASimpleAuthKit to act as a sophisticated bridge between two worlds: the imperative Firebase SDK and the declarative SwiftUI framework. The approach was centered on developer experience, production readiness, and comprehensive testability.
Architecture Strategy:
- The SwiftUI Bridge: The library's primary role is to serve as an adapter. It consumes Firebase's complex, callback-based operations and exposes them through a clean, state-driven
ObservableObject
, which is the cornerstone of state management in SwiftUI. - State-Driven by Default: Instead of forcing developers to track scattered boolean flags (
isAuthenticating
,isSignedIn
,showError
), the library models everything as a single, comprehensiveAuthState
enum. This allows the entire UI to be driven by a simpleswitch
statement. - Protocol-Oriented Decoupling: Used dependency injection and protocols (
FirebaseAuthClientProtocol
, etc.) to completely isolate the core logic from Firebase singletons. This was the key to making the system 100% testable. - Modern Concurrency Transformation: Aggressively wrapped all of Firebase's legacy completion handlers in modern
async/await
functions. This eliminates callback hell, simplifies error handling, and makes the asynchronous code vastly more readable and maintainable.
Production Features:
- Biometric Authentication: Complete Face ID/Touch ID implementation with secure keychain storage.
- Account Linking Automation: Handles complex Firebase account linking flows transparently.
- Keychain Access Groups: Support for sharing authentication state between multiple apps.
- User-Centric Error Handling: Translates cryptic Firebase codes into helpful, actionable messages for end-users.
Technical Implementation
The library's architecture balances simplicity for the consumer with sophisticated internal functionality. The core is the AuthService
, which manages a state machine driven by the AuthState
enum.
The State Machine Core:
By defining every possible state, UI development becomes a simple matter of responding to the current state, which is published automatically.
public enum AuthState: Equatable, Sendable { case signedOut case authenticating(String?) case signedIn(AuthUser) case requiresBiometrics case requiresAccountLinking(email: String, attemptedProviderId: String?) case emailInUseSuggestSignIn(email: String) case requiresMergeConflictResolution }
Automated Account Linking:
This flow is one of the most complex parts of Firebase Auth, and the library fully automates it:
- When a sign-in fails with
account-exists-with-different-credential
, the library catches the error and stores the pending credential. - It transitions the state to
.requiresAccountLinking
, providing the UI with the necessary context (email and provider). - The app's UI prompts the user to sign in with their existing method (e.g., Google).
- Upon successful re-authentication,
AuthService
automatically retrieves the stored credential and calls Firebase'slink(with:)
function. - The final state is
.signedIn
, with the user's account now linked to both providers, all handled transparently.
Secure Biometric Authentication:
BiometricController
: A dedicated controller that manages user preferences for biometrics.SecureStorageProtocol
: An abstraction overKeychainStorage
that securely stores the last authenticated user's ID, associating the biometric preference with a specific account on the device.- Client-Controlled Policy: The library provides the capability but lets the consuming app decide when to require biometrics (e.g., on app launch), giving developers full control over the user experience.
Test-Driven Architecture:
MockFirebaseAuthenticator
&MockFirebaseAuthClient
: Complete mock implementations of all Firebase-interacting protocols.- Behavioral Simulation: Mocks can be configured to return specific users or errors, allowing for testing of every path, including complex ones like account linking and merge conflicts.
- State Transition Verification: Tests (
AuthServiceTests.swift
) verify every state transition, ensuring the UI will always behave predictably. For example,testCreateAccountWithEmail_Failure_EmailAlreadyInUse_setsStateToEmailInUseSuggestSignIn
.
Results & Impact
ASimpleAuthKit successfully transforms Firebase authentication from a major integration hurdle into a streamlined, plug-and-play experience.
The library's impact is significant:
- Drastically Simplified Integration: Reduced Firebase auth setup from weeks of complex, error-prone work to just a few hours of configuration.
- Enhanced Security: Provides a robust biometric authentication layer with secure keychain management, a feature not offered natively by Firebase.
- Superior User Experience: Replaces confusing technical errors with clear, actionable guidance, reducing user frustration and support requests.
- Unprecedented Reliability: The protocol-based architecture and comprehensive test suite ensure robust, predictable behavior across all authentication scenarios.
- Increased Developer Productivity: The SwiftUI-native API and automated complex flows allow developers to focus on building features, not authentication boilerplate.
Usage Example
The result of this architecture is code that is clean, declarative, and idiomatic to SwiftUI. ASimpleAuthKit completely hides the underlying complexity, boilerplate, and architectural mismatch of the Firebase SDK.
Declarative UI Driven by State:
// The 'authService' object handles all the complex Firebase logic internally. // The view only needs to react to its simple '.state' property. struct LoginView: View { @StateObject private var authService = AuthService(config: AuthConfig()) var body: some View { switch authService.state { case .signedOut: // Show the login form loginForm case .authenticating(let message): // Show a loading indicator ProgressView(message ?? "Authenticating...") case .signedIn(let user): // Show the main app content Text("Welcome, \(user.displayName ?? "User")!") case .requiresAccountLinking(let email, _): // Show a dedicated UI to handle linking accountLinkingPrompt(email: email) // ... other states } } // ... (form implementation) }
Explore the Implementation
ASimpleAuthKit is a powerful example of how thoughtful architecture can solve complex, recurring problems in mobile development.
View Project on GitHubNeed a robust, secure mobile application?
My experience building production-grade systems like ASimpleAuthKit enables me to architect and implement secure, user-friendly, and highly testable mobile applications. Whether you need sophisticated Firebase integration, custom security flows, or a scalable app architecture, I can build a solution that prioritizes quality and long-term maintainability.
Discuss Your Project