Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ public protocol ExternalAuthProvider {
@MainActor var authButton: ButtonType { get }
}

public protocol GoogleProviderProtocol: ExternalAuthProvider {
public protocol GoogleProviderAuthUIProtocol: ExternalAuthProvider {
@MainActor func signInWithGoogle(clientID: String) async throws -> AuthCredential
}

public protocol FacebookProviderProtocol: ExternalAuthProvider {
public protocol FacebookProviderAuthUIProtocol: ExternalAuthProvider {
@MainActor func signInWithFacebook(isLimitedLogin: Bool) async throws -> AuthCredential
}

public protocol PhoneAuthProviderProtocol: ExternalAuthProvider {
public protocol PhoneAuthProviderAuthUIProtocol: ExternalAuthProvider {
@MainActor func verifyPhoneNumber(phoneNumber: String) async throws -> String
}

Expand Down Expand Up @@ -66,9 +66,9 @@ private final class AuthListenerManager {
@Observable
public final class AuthService {
public init(configuration: AuthConfiguration = AuthConfiguration(), auth: Auth = Auth.auth(),
googleProvider: (any GoogleProviderProtocol)? = nil,
facebookProvider: (any FacebookProviderProtocol)? = nil,
phoneAuthProvider: (any PhoneAuthProviderProtocol)? = nil) {
googleProvider: (any GoogleProviderAuthUIProtocol)? = nil,
facebookProvider: (any FacebookProviderAuthUIProtocol)? = nil,
phoneAuthProvider: (any PhoneAuthProviderAuthUIProtocol)? = nil) {
self.auth = auth
self.configuration = configuration
self.googleProvider = googleProvider
Expand All @@ -89,14 +89,14 @@ public final class AuthService {
public var errorMessage = ""
public let passwordPrompt: PasswordPromptCoordinator = .init()

public var googleProvider: (any GoogleProviderProtocol)?
public var facebookProvider: (any FacebookProviderProtocol)?
public var phoneAuthProvider: (any PhoneAuthProviderProtocol)?
public var googleProvider: (any GoogleProviderAuthUIProtocol)?
public var facebookProvider: (any FacebookProviderAuthUIProtocol)?
public var phoneAuthProvider: (any PhoneAuthProviderAuthUIProtocol)?

private var listenerManager: AuthListenerManager?
private var signedInCredential: AuthCredential?

private var safeGoogleProvider: any GoogleProviderProtocol {
private var safeGoogleProvider: any GoogleProviderAuthUIProtocol {
get throws {
guard let provider = googleProvider else {
throw AuthServiceError
Expand All @@ -106,7 +106,7 @@ public final class AuthService {
}
}

private var safeFacebookProvider: any FacebookProviderProtocol {
private var safeFacebookProvider: any FacebookProviderAuthUIProtocol {
get throws {
guard let provider = facebookProvider else {
throw AuthServiceError
Expand All @@ -116,7 +116,7 @@ public final class AuthService {
}
}

private var safePhoneAuthProvider: any PhoneAuthProviderProtocol {
private var safePhoneAuthProvider: any PhoneAuthProviderAuthUIProtocol {
get throws {
guard let provider = phoneAuthProvider else {
throw AuthServiceError
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import CommonCrypto
import FirebaseCore
import Foundation
import Security

Expand Down Expand Up @@ -47,3 +48,20 @@ public class CommonUtils {
return hash.map { String(format: "%02x", $0) }.joined()
}
}

public extension FirebaseOptions {
static func dummyConfigurationForPreview() {
guard FirebaseApp.app() == nil else { return }

let options = FirebaseOptions(
googleAppID: "1:123:ios:123abc456def7890",
gcmSenderID: "dummy"
)
options.apiKey = "dummy"
options.projectID = "dummy-project-id"
options.bundleID = Bundle.main.bundleIdentifier ?? "com.example.dummy"
options.clientID = "dummy-abc.apps.googleusercontent.com"

FirebaseApp.configure(options: options)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Created by Russell Wheatley on 20/03/2025.
//
import FirebaseAuth
import FirebaseCore
import SwiftUI

private enum FocusableField: Hashable {
Expand Down Expand Up @@ -132,3 +133,9 @@ extension EmailAuthView: View {
}
}
}

#Preview {
FirebaseOptions.dummyConfigurationForPreview()
return EmailAuthView()
.environment(AuthService())
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import FirebaseAuth
import FirebaseCore
import SwiftUI

public struct EmailLinkView {
Expand Down Expand Up @@ -74,3 +75,9 @@ extension EmailLinkView: View {
})
}
}

#Preview {
FirebaseOptions.dummyConfigurationForPreview()
return EmailLinkView()
.environment(AuthService())
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ extension PasswordPromptSheet: View {
.padding()
}
}

#Preview {
PasswordPromptSheet(coordinator: PasswordPromptCoordinator())
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import FirebaseCore
import SwiftUI

public struct PasswordRecoveryView {
Expand Down Expand Up @@ -67,3 +68,9 @@ extension PasswordRecoveryView: View {
})
}
}

#Preview {
FirebaseOptions.dummyConfigurationForPreview()
return PasswordRecoveryView()
.environment(AuthService())
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import FirebaseCore
import SwiftUI

@MainActor
Expand Down Expand Up @@ -51,3 +52,9 @@ extension SignedInView: View {
}
}
}

#Preview {
FirebaseOptions.dummyConfigurationForPreview()
return SignedInView()
.environment(AuthService())
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import FirebaseCore
import SwiftUI

public struct VerifyEmailView {
Expand Down Expand Up @@ -42,3 +43,9 @@ extension VerifyEmailView: View {
}
}
}

#Preview {
FirebaseOptions.dummyConfigurationForPreview()
return VerifyEmailView()
.environment(AuthService())
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public enum FacebookProviderError: Error {
case authenticationToken(String)
}

public class FacebookProviderSwift: FacebookProviderProtocol {
public class FacebookProviderAuthUI: FacebookProviderAuthUIProtocol {
let scopes: [String]
let shortName = "Facebook"
let providerId = "facebook.com"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import FacebookCore
import FacebookLogin
import FirebaseAuth
import FirebaseAuthSwiftUI
import FirebaseCore
import SwiftUI

@MainActor
Expand Down Expand Up @@ -108,3 +109,9 @@ extension SignInWithFacebookButton: View {
Text(errorMessage).foregroundColor(.red)
}
}

#Preview {
FirebaseOptions.dummyConfigurationForPreview()
return SignInWithFacebookButton()
.environment(AuthService())
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
import FirebaseAuthSwiftUI

public extension AuthService {

@discardableResult
public func withGoogleSignIn() -> AuthService {
func withGoogleSignIn() -> AuthService {
let clientID = auth.app?.options.clientID ?? ""
self.googleProvider = GoogleProviderSwift(clientID: clientID)
googleProvider = GoogleProviderAuthUI(clientID: clientID)
return self
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public enum GoogleProviderError: Error {
case user(String)
}

public class GoogleProviderSwift: @preconcurrency GoogleProviderProtocol {
public class GoogleProviderAuthUI: @preconcurrency GoogleProviderAuthUIProtocol {
let scopes: [String]
let shortName = "Google"
let providerId = "google.com"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import FirebaseAuthSwiftUI
import FirebaseCore
import SwiftUI

@MainActor
Expand Down Expand Up @@ -49,3 +50,9 @@ extension SignInWithGoogleButton: View {
}
}
}

#Preview {
FirebaseOptions.dummyConfigurationForPreview()
return SignInWithGoogleButton()
.environment(AuthService())
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import SwiftUI

public typealias VerificationID = String

public class PhoneAuthProviderSwift: @preconcurrency PhoneAuthProviderProtocol {

public class PhoneAuthProviderAuthUI: @preconcurrency PhoneAuthProviderAuthUIProtocol {
public var authButton: Button<Text> {
// TODO: implement me
return Button("Phone", action: { })
return Button("Phone", action: {})
}

public init() {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import FirebaseAuthSwiftUI
import FirebaseCore
import SwiftUI

@MainActor
Expand Down Expand Up @@ -90,3 +91,9 @@ extension PhoneAuthButtonView: View {
Text(errorMessage).foregroundColor(.red)
}
}

#Preview {
FirebaseOptions.dummyConfigurationForPreview()
return PhoneAuthButtonView()
.environment(AuthService())
}
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ let package = Package(
dependencies: [
"FirebaseAuthSwiftUI",
"GoogleSignIn",
.product(name: "GoogleSignInSwift", package: "GoogleSignIn")
.product(name: "GoogleSignInSwift", package: "GoogleSignIn"),
],
path: "FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources"
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// ContentView.swift
// FirebaseSwiftUIExample
//
// Created by Russell Wheatley on 23/04/2025.
//

import FirebaseAuth
import FirebaseAuthSwiftUI
import FirebaseFacebookSwiftUI
import FirebaseGoogleSwiftUI
import FirebasePhoneAuthSwiftUI
import SwiftUI

struct ContentView: View {
let authService: AuthService

init() {
Auth.auth().signInAnonymously()

let actionCodeSettings = ActionCodeSettings()
actionCodeSettings.handleCodeInApp = true
actionCodeSettings
.url = URL(string: "https://flutterfire-e2e-tests.firebaseapp.com")
actionCodeSettings.linkDomain = "flutterfire-e2e-tests.firebaseapp.com"
actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
let configuration = AuthConfiguration(
shouldAutoUpgradeAnonymousUsers: true,
emailLinkSignInActionCodeSettings: actionCodeSettings
)
let facebookProvider = FacebookProviderAuthUI()
let phoneAuthProvider = PhoneAuthProviderAuthUI()
authService = AuthService(
configuration: configuration,
facebookProvider: facebookProvider,
phoneAuthProvider: phoneAuthProvider
)
.withGoogleSignIn()
}

var body: some View {
AuthPickerView {
SignInWithGoogleButton()
SignInWithFacebookButton()
PhoneAuthButtonView()
}.environment(authService)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@
//
// Created by Russell Wheatley on 18/02/2025.
//

import FacebookCore
import FirebaseAuth
import FirebaseAuthSwiftUI
import FirebaseCore
import FirebaseFacebookSwiftUI
import FirebaseGoogleSwiftUI
import FirebasePhoneAuthSwiftUI
import GoogleSignIn
Expand Down Expand Up @@ -74,40 +71,3 @@ struct FirebaseSwiftUIExampleApp: App {
}
}
}

struct ContentView: View {
let authService: AuthService

init() {
// Auth.auth().signInAnonymously()

let actionCodeSettings = ActionCodeSettings()
actionCodeSettings.handleCodeInApp = true
actionCodeSettings
.url = URL(string: "https://flutterfire-e2e-tests.firebaseapp.com")
actionCodeSettings.linkDomain = "flutterfire-e2e-tests.firebaseapp.com"
actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
let configuration = AuthConfiguration(
shouldAutoUpgradeAnonymousUsers: true,
emailLinkSignInActionCodeSettings: actionCodeSettings
)
let facebookProvider = FacebookProviderSwift()
let phoneAuthProvider = PhoneAuthProviderSwift()
authService = AuthService(
configuration: configuration,
googleProvider: nil,
facebookProvider: facebookProvider,
phoneAuthProvider: phoneAuthProvider
)
// Transition to this api
.withGoogleSignIn()
}

var body: some View {
AuthPickerView {
SignInWithGoogleButton()
SignInWithFacebookButton()
PhoneAuthButtonView()
}.environment(authService)
}
}
Loading