Skip to content

question: how to make Views concrete? (i.e not use Any View) #1249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import SwiftUI

public protocol ExternalAuthProvider {
associatedtype ButtonType: View
@MainActor var authButton: ButtonType { get }
var id: String { get }
@MainActor func authButton() -> AnyView
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you try returning some View? You might need to annotate the function with @ViewBuilder

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cannot have some View as return type on protocol apparently. Get Xcode compiler error: 'some' type cannot be the return type of a protocol requirement; did you mean to add an associated type?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put together something that might help: https://gist.github.com/peterfriese/d9745f366fb4f857daf077e93b1eb01f

import SwiftUI

protocol AuthProvider: Identifiable {
  var id: String { get }
  associatedtype ButtonType: View
  var authButtonView: Self.ButtonType { get }
}

protocol FooProviderProtocol: AuthProvider {
  func doFoo()
}

struct FooProviderImpl1: FooProviderProtocol {
  var id: String = "FooProviderImpl1"

  func doFoo() {
    print("1")
  }

  var authButtonView: some View {
    Text("1")
  }
}

struct FooProviderImpl2: FooProviderProtocol {
  var id: String = "FooProviderImpl2"

  func doFoo() {
    print("2")
  }

  var authButtonView: some View {
    Text("2")
      .background(Color.red)
  }
}

protocol BarProviderProtocol: AuthProvider {
  func doBar()
}

struct BarProviderImpl1: BarProviderProtocol {
  var id: String = "BarProviderImpl1"
  
  func doBar() {
    print("1")
  }
  
  var authButtonView: some View {
    Text("1")
  }
}

struct BarProviderImpl2: BarProviderProtocol {
  var id: String = "BarProviderImpl2"
  
  func doBar() {
    print("2")
  }
  
  var authButtonView: some View {
    Text("2")
      .background(Color.red)
  }
}


struct ContentView<Provider: AuthProvider>: View {
  var provider: Provider
  var body: some View {
    VStack {
      provider.authButtonView
    }
  }
}

#Preview {
  ContentView(provider: BarProviderImpl2())
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've responded in the other PR: #1250

}

public protocol GoogleProviderProtocol: ExternalAuthProvider {
Expand Down Expand Up @@ -96,6 +96,21 @@ public final class AuthService {
private var listenerManager: AuthListenerManager?
private var signedInCredential: AuthCredential?

private var providers: [ExternalAuthProvider] = []
public func register(provider: ExternalAuthProvider) {
providers.append(provider)
}

public func renderButtons(spacing: CGFloat = 16) -> AnyView {
AnyView(
VStack(spacing: spacing) {
ForEach(providers, id: \.id) { provider in
provider.authButton()
}
}
)
}

private var safeGoogleProvider: any GoogleProviderProtocol {
get throws {
guard let provider = googleProvider else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1047,6 +1047,9 @@
}
}
}
},
"Update password" : {

},
"UpdateEmailAlertMessage" : {
"comment" : "Alert action message shown before updating email action. Use short/abbreviated translation for 'email' which is less than 15 chars.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ extension AuthPickerView: View {
Text(authService.authenticationFlow == .login ? "Login" : "Sign up")
VStack { Divider() }
EmailAuthView()
providerButtons()
authService.renderButtons()
VStack { Divider() }
HStack {
Text(authService
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// AuthService+Facebook.swift
// FirebaseUI
//
// Created by Russell Wheatley on 01/05/2025.
//

import FirebaseAuthSwiftUI

public extension AuthService {
@discardableResult
func withFacebookSignIn(scopes scopes: [String]? = nil) -> AuthService {
facebookProvider = FacebookProviderSwift(scopes: scopes)
return self
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public enum FacebookProviderError: Error {
}

public class FacebookProviderSwift: FacebookProviderProtocol {
public let id: String = "facebook"
let scopes: [String]
let shortName = "Facebook"
let providerId = "facebook.com"
Expand All @@ -34,9 +35,9 @@ public class FacebookProviderSwift: FacebookProviderProtocol {
rawNonce = CommonUtils.randomNonce()
shaNonce = CommonUtils.sha256Hash(of: rawNonce)
}

@MainActor public var authButton: SignInWithFacebookButton {
return SignInWithFacebookButton()
@MainActor public func authButton() -> AnyView {
AnyView(SignInWithFacebookButton())
}

@MainActor public func signInWithFacebook(isLimitedLogin: Bool) async throws -> AuthCredential {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@
import FirebaseAuthSwiftUI

public extension AuthService {

@discardableResult
public func withGoogleSignIn() -> AuthService {
func withGoogleSignIn(scopes scopes: [String]? = nil) -> AuthService {
let clientID = auth.app?.options.clientID ?? ""
self.googleProvider = GoogleProviderSwift(clientID: clientID)
googleProvider = GoogleProviderSwift(scopes: scopes, clientID: clientID)
register(provider: googleProvider!)
return self
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public enum GoogleProviderError: Error {
}

public class GoogleProviderSwift: @preconcurrency GoogleProviderProtocol {
public let id: String = "google"
let scopes: [String]
let shortName = "Google"
let providerId = "google.com"
Expand All @@ -25,12 +26,12 @@ public class GoogleProviderSwift: @preconcurrency GoogleProviderProtocol {
self.clientID = clientID
}

@MainActor public var authButton: GoogleSignInButton {
return GoogleSignInButton {
@MainActor public func authButton() -> AnyView {
AnyView(GoogleSignInButton {
Task {
try await self.signInWithGoogle(clientID: self.clientID)
}
}
})
}

@MainActor public func signInWithGoogle(clientID: String) async throws -> AuthCredential {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import SwiftUI
public typealias VerificationID = String

public class PhoneAuthProviderSwift: @preconcurrency PhoneAuthProviderProtocol {

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

public let id: String = "phone"
public init() {}

@MainActor public func authButton() -> AnyView {
AnyView(Text("phone button TODO"))
}

@MainActor public func verifyPhoneNumber(phoneNumber: String) async throws -> VerificationID {
return try await withCheckedThrowingContinuation { continuation in
Expand Down
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
Expand Up @@ -95,17 +95,15 @@ struct ContentView: View {
let phoneAuthProvider = PhoneAuthProviderSwift()
authService = AuthService(
configuration: configuration,
googleProvider: nil,
facebookProvider: facebookProvider,
phoneAuthProvider: phoneAuthProvider
)
// Transition to this api
.withGoogleSignIn()
// Transition to this api
.withGoogleSignIn()
}

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