Skip to content

SwiftfulThinking/SwiftfulFirebaseAuth

Repository files navigation

🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

⚠️ This repository is deprecated! It will remain available for those using it, but no further updates will be made.

⚠️ Please consider migrating to the new repos:

⚠️ The new repos are restricted to iOS 17+, with Swift 6 and @Observable macro support.

⚠️ The new parent repo, SwiftfulAuthenticating, is dependency agnostic and decoupled from Firebase. This improves testability, mockability, and allows developers to pick-and-choose their auth source.

⚠️ Firebase Authentication can still be added to SwiftfulAuthenticating via SwiftfulAuthenticatingFirebase

⚠️ The new repo drops support for Sign In With Phone, as different auth sources handle this differently (tbd).

🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨

SwiftfulFirebaseAuth 🤙

Convenience methods to manage Firebase Authentication in Swift projects.

  • âś… Sign In With Apple
  • âś… Sign In With Google
  • âś… Sign In With Phone
Task {
     do {
          let (userAuthInfo, isNewUser) = try await authManager.signInApple()
          // User is signed in

          if isNewUser {
               // New user -> Create user profile in Firestore
          } else {
               // Existing user -> sign in
          }
     } catch {
          // User auth failed
     }
}

Sample project: https://github.com/SwiftfulThinking/SwiftfulFirebasePackagesExample

Setup

Details (Click to expand)

1. Import the package to your project.

2. Import the package to your file.

import SwiftfulFirebaseAuth

3. Create one instance of AuthManager for your application.

let authManager = AuthManager(configuration: .firebase)

// Use Mock configuration to avoid running Firebase while developing (ex. for SwiftUI Previews).
let authManager = AuthManager(configuration: .mock(.signInAndOut))

// Use Mock configurations to override settings for Previews.
// .signInAndOut (default behavior)
// .signedIn (starts signed in)
// .signedOut (user starts signed out)
#Preview {
    MyView()
            .environment(\.authManager, AuthManager(configuration: .mock(.signedIn)))
}

4. Configure your Firebase project.

Add the Firebase SDK to your application and configure() the SDK on launch.

@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
     FirebaseApp.configure()
     return true
}

Sign In With Apple

Details (Click to expand)

Firebase docs: https://firebase.google.com/docs/auth/ios/apple

1. Enable Apple as a Sign-In Method in Firebase Authentication console.

  • Firebase Console -> Authentication -> Sign-in method -> Add new provider

2. Add Sign in with Apple Signing Capability to your Xcode project.

  • Xcode Project Navigator -> Target -> Signing & Capabilities -> + Capability -> Sign in with Apple (requires Apple Developer Account)

3. Add Apple Button (optional)

SignInWithAppleButtonView()
    .frame(height: 50)

4. Sign in

try await authManager.signInApple()

Sign In With Google

Details (Click to expand)

Firebase docs: https://firebase.google.com/docs/auth/ios/google-signin

1. Enable Google as a Sign-In Method in Firebase Authentication console.

  • Firebase Console -> Authentication -> Sign-in method -> Add new provider

2. Update you app's the info.plist file.

  • Firebase Console -> Project Settings -> Your apps -> GoogleService-Info.plist

3. Add custom URL scheme (URL Types -> REVERSED_CLIENT_ID)

  • GoogleService-Info.plist -> REVERSED_CLIENT_ID
  • Xcode Project Navigator -> Target -> Info -> URL Types -> add REVERSED_CLIENT_ID as URL Schemes value

4. Add Google Button (optional)

SignInWithGoogleButtonView()
    .frame(height: 50)

5. Sign in

let clientId = FirebaseApp.app()?.options.clientID
try await authManager.signInGoogle(GIDClientID: clientId)

Sign In With Phone

Details (Click to expand)

Firebase docs: https://firebase.google.com/docs/auth/ios/phone-auth

1. Enable Phone Number as a Sign-In Method in Firebase Authentication console.

  • Firebase Console -> Authentication -> Sign-in method -> Add new provider

2. Enable APNs notifications (silent push notifications).

  • Create an APNs Authentication Key in Apple Developer Member Center (requires Apple Developer Account)
  • Certificates, Identifiers & Profiles -> New Key for Apple Push Notifications service (APNs) -> download .p8 file

3. Upload APNs key to Firebase.

  • Firebase Console -> Project Settings -> Cloud Messaging -> APNs Authentication Key

4. Enable reCAPTCHA verification (optional?).

  • Firebase Console -> Project Settings -> Encoded App ID
  • Xcode Project Navigator -> Target -> Info -> URL Types -> add Encoded App ID as URL Schemes value

5. Add UIDelegate methods to handle push notifications

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
     Auth.auth().setAPNSToken(deviceToken, type: .prod)
}

func application(_ application: UIApplication, didReceiveRemoteNotification notification: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
     if Auth.auth().canHandleNotification(notification) {
          completionHandler(.noData)
          return
     }
}
    
func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
     if Auth.auth().canHandle(url) {
          return true
     }
     return false
}

6. Get the user's phone number

7. Add Phone Number Button (optional)

SignInWithPhoneButtonView()
     .frame(height: 50)

8. Send verification code to user's phone.

try await authManager.signInPhone_Start(phoneNumber: phoneNumber)

9. Verify code and sign in

try await authManager.signInPhone_Verify(code: code)

Already Signed In

Details (Click to expand)

Synchronously get user's authentication info.

let userAuthProfile: UserAuthInfo? = authManager.currentUser.profile
let userIsSignedIn: Bool = authManager.currentUser.isSignedIn
let userId: String? = authManager.currentUser.userId

Asynchronously listen for changes to user's authentication info.

Task {
     for await authInfo in authManager.$currentUser.values {
          let userAuthProfile: UserAuthInfo? = authInfo.profile
          let userIsSignedIn: Bool = authInfo.isSignedIn
          let userId: String? = authInfo.userId
     }                
}

Sign out or delete user's authentication.

try authManager.signOut()
try await authManager.deleteAuthentication()

Want to contribute?

Open a PR! New Sign-In Methods must use Swift Concurrency (async/await).