diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift index 6438bf21ad..c0052e9c26 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -1,9 +1,11 @@ @preconcurrency import FirebaseAuth import SwiftUI -public protocol ExternalAuthProvider { - var id: String { get } +public protocol ExternalAuthProvider: Identifiable { + var id: String { get } + associatedtype ButtonType: View @MainActor func authButton() -> AnyView + @MainActor var authButtonView: Self.ButtonType { get } } public protocol GoogleProviderProtocol: ExternalAuthProvider { @@ -65,15 +67,9 @@ private final class AuthListenerManager { @MainActor @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) { + public init(configuration: AuthConfiguration = AuthConfiguration(), auth: Auth = Auth.auth()) { self.auth = auth self.configuration = configuration - self.googleProvider = googleProvider - self.facebookProvider = facebookProvider - self.phoneAuthProvider = phoneAuthProvider string = StringUtils(bundle: configuration.customStringsBundle ?? Bundle.module) listenerManager = AuthListenerManager(auth: auth, authEnvironment: self) } @@ -96,19 +92,13 @@ public final class AuthService { private var listenerManager: AuthListenerManager? private var signedInCredential: AuthCredential? - private var providers: [ExternalAuthProvider] = [] - public func register(provider: ExternalAuthProvider) { + private var providers: [any ExternalAuthProvider] = [] + public func register(provider: any ExternalAuthProvider) { providers.append(provider) } - public func renderButtons(spacing: CGFloat = 16) -> AnyView { - AnyView( - VStack(spacing: spacing) { - ForEach(providers, id: \.id) { provider in - provider.authButton() - } - } - ) + var availableProviders: [any ExternalAuthProvider] { + return providers } private var safeGoogleProvider: any GoogleProviderProtocol { diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift index 0d1d84fbac..375c785580 100644 --- a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift @@ -28,7 +28,8 @@ extension AuthPickerView: View { Text(authService.authenticationFlow == .login ? "Login" : "Sign up") VStack { Divider() } EmailAuthView() - authService.renderButtons() + // Xcode compiler exception: Type 'any ExternalAuthProvider' cannot conform to 'ExternalAuthProvider' + RenderButtonsView(providers: authService.availableProviders) VStack { Divider() } HStack { Text(authService diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/RenderButtonsView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/RenderButtonsView.swift new file mode 100644 index 0000000000..c14be0ad1b --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/RenderButtonsView.swift @@ -0,0 +1,25 @@ +// +// RenderButtonsView.swift +// FirebaseUI +// +// Created by Russell Wheatley on 08/05/2025. +// +import SwiftUI + +struct RenderButtonsView: View { + var providers: [Provider] = [] + + public func renderButtonViews(spacing: CGFloat = 16) -> some View { + VStack(spacing: spacing) { + ForEach(providers, id: \.id) { provider in + provider.authButtonView + } + } + } + + var body: some View { + VStack { + renderButtonViews() + } + } +} diff --git a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/AuthService+Facebook.swift b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/AuthService+Facebook.swift index bd3099e69b..253d45fed7 100644 --- a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/AuthService+Facebook.swift +++ b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/AuthService+Facebook.swift @@ -11,6 +11,7 @@ public extension AuthService { @discardableResult func withFacebookSignIn(scopes scopes: [String]? = nil) -> AuthService { facebookProvider = FacebookProviderSwift(scopes: scopes) + register(provider: facebookProvider!) return self } } diff --git a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderSwift.swift b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderSwift.swift index 14847579c4..e846445eff 100644 --- a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderSwift.swift +++ b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderSwift.swift @@ -35,11 +35,15 @@ public class FacebookProviderSwift: FacebookProviderProtocol { rawNonce = CommonUtils.randomNonce() shaNonce = CommonUtils.sha256Hash(of: rawNonce) } - + @MainActor public func authButton() -> AnyView { AnyView(SignInWithFacebookButton()) } + @MainActor public var authButtonView: some View { + SignInWithFacebookButton() + } + @MainActor public func signInWithFacebook(isLimitedLogin: Bool) async throws -> AuthCredential { let trackingStatus = ATTrackingManager.trackingAuthorizationStatus let tracking: LoginTracking = trackingStatus != .authorized ? .limited : diff --git a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderSwift.swift b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderSwift.swift index 1934f5ddc1..7d220b1b61 100644 --- a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderSwift.swift +++ b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderSwift.swift @@ -34,6 +34,14 @@ public class GoogleProviderSwift: @preconcurrency GoogleProviderProtocol { }) } + @MainActor public var authButtonView: some View { + GoogleSignInButton { + Task { + try await self.signInWithGoogle(clientID: self.clientID) + } + } + } + @MainActor public func signInWithGoogle(clientID: String) async throws -> AuthCredential { guard let presentingViewController = await (UIApplication.shared.connectedScenes .first as? UIWindowScene)?.windows.first?.rootViewController else { diff --git a/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderSwift.swift b/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderSwift.swift index 220e4c97b0..e1d3f6d394 100644 --- a/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderSwift.swift +++ b/FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderSwift.swift @@ -7,11 +7,15 @@ public typealias VerificationID = String public class PhoneAuthProviderSwift: @preconcurrency PhoneAuthProviderProtocol { public let id: String = "phone" public init() {} - + @MainActor public func authButton() -> AnyView { AnyView(Text("phone button TODO")) } + @MainActor public var authButtonView: some View { + Text("phone button TODO") + } + @MainActor public func verifyPhoneNumber(phoneNumber: String) async throws -> VerificationID { return try await withCheckedThrowingContinuation { continuation in PhoneAuthProvider.provider() diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift index aad1ca43ac..f54d19bbed 100644 --- a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift @@ -91,12 +91,9 @@ struct ContentView: View { shouldAutoUpgradeAnonymousUsers: true, emailLinkSignInActionCodeSettings: actionCodeSettings ) - let facebookProvider = FacebookProviderSwift() - let phoneAuthProvider = PhoneAuthProviderSwift() + authService = AuthService( - configuration: configuration, - facebookProvider: facebookProvider, - phoneAuthProvider: phoneAuthProvider + configuration: configuration ) // Transition to this api .withGoogleSignIn()