Skip to content

Commit fb8728b

Browse files
Merge pull request #1243 from firebase/mc/buttons
2 parents a29ab0a + a6bafd9 commit fb8728b

File tree

9 files changed

+98
-41
lines changed

9 files changed

+98
-41
lines changed

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift

+19-13
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
@preconcurrency import FirebaseAuth
22
import SwiftUI
33

4-
public protocol GoogleProviderProtocol {
5-
func handleUrl(_ url: URL) -> Bool
4+
public protocol ExternalAuthProvider {
5+
associatedtype ButtonType: View
6+
@MainActor var authButton: ButtonType { get }
7+
}
8+
9+
public protocol GoogleProviderProtocol: ExternalAuthProvider {
610
@MainActor func signInWithGoogle(clientID: String) async throws -> AuthCredential
711
}
812

9-
public protocol FacebookProviderProtocol {
13+
public protocol FacebookProviderProtocol: ExternalAuthProvider {
1014
@MainActor func signInWithFacebook(isLimitedLogin: Bool) async throws -> AuthCredential
1115
}
1216

13-
public protocol PhoneAuthProviderProtocol {
17+
public protocol PhoneAuthProviderProtocol: ExternalAuthProvider {
1418
@MainActor func verifyPhoneNumber(phoneNumber: String) async throws -> String
1519
}
1620

@@ -61,9 +65,9 @@ private final class AuthListenerManager {
6165
@Observable
6266
public final class AuthService {
6367
public init(configuration: AuthConfiguration = AuthConfiguration(), auth: Auth = Auth.auth(),
64-
googleProvider: GoogleProviderProtocol? = nil,
65-
facebookProvider: FacebookProviderProtocol? = nil,
66-
phoneAuthProvider: PhoneAuthProviderProtocol? = nil) {
68+
googleProvider: (any GoogleProviderProtocol)? = nil,
69+
facebookProvider: (any FacebookProviderProtocol)? = nil,
70+
phoneAuthProvider: (any PhoneAuthProviderProtocol)? = nil) {
6771
self.auth = auth
6872
self.configuration = configuration
6973
self.googleProvider = googleProvider
@@ -84,12 +88,14 @@ public final class AuthService {
8488
public var errorMessage = ""
8589
public let passwordPrompt: PasswordPromptCoordinator = .init()
8690

91+
public var googleProvider: (any GoogleProviderProtocol)?
92+
public var facebookProvider: (any FacebookProviderProtocol)?
93+
public var phoneAuthProvider: (any PhoneAuthProviderProtocol)?
94+
8795
private var listenerManager: AuthListenerManager?
88-
private let googleProvider: GoogleProviderProtocol?
89-
private let facebookProvider: FacebookProviderProtocol?
90-
private let phoneAuthProvider: PhoneAuthProviderProtocol?
96+
private var signedInCredential: AuthCredential?
9197

92-
private var safeGoogleProvider: GoogleProviderProtocol {
98+
private var safeGoogleProvider: any GoogleProviderProtocol {
9399
get throws {
94100
guard let provider = googleProvider else {
95101
throw AuthServiceError
@@ -99,7 +105,7 @@ public final class AuthService {
99105
}
100106
}
101107

102-
private var safeFacebookProvider: FacebookProviderProtocol {
108+
private var safeFacebookProvider: any FacebookProviderProtocol {
103109
get throws {
104110
guard let provider = facebookProvider else {
105111
throw AuthServiceError
@@ -109,7 +115,7 @@ public final class AuthService {
109115
}
110116
}
111117

112-
private var safePhoneAuthProvider: PhoneAuthProviderProtocol {
118+
private var safePhoneAuthProvider: any PhoneAuthProviderProtocol {
113119
get throws {
114120
guard let provider = phoneAuthProvider else {
115121
throw AuthServiceError

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/Localizable.xcstrings

+7
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@
162162
},
163163
"AuthPickerTitle" : {
164164
"comment" : "Title for auth picker screen.",
165+
"extractionState" : "stale",
165166
"localizations" : {
166167
"en" : {
167168
"stringUnit" : {
@@ -404,6 +405,9 @@
404405
}
405406
}
406407
}
408+
},
409+
"Enter Password" : {
410+
407411
},
408412
"EnterYourEmail" : {
409413
"comment" : "Title for email entry screen, email text field placeholder. Use short/abbreviated translation for 'email' which is less than 15 chars.",
@@ -932,6 +936,9 @@
932936
}
933937
}
934938
}
939+
},
940+
"Submit" : {
941+
935942
},
936943
"TermsOfService" : {
937944
"comment" : "Text linked to a web page with the Terms of Service content.",

FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderSwift.swift

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import FacebookCore
33
import FacebookLogin
44
import FirebaseAuth
55
import FirebaseAuthSwiftUI
6+
import SwiftUI
67

78
let kFacebookEmailScope = "email"
89
let kFacebookProfileScope = "public_profile"
@@ -34,6 +35,10 @@ public class FacebookProviderSwift: FacebookProviderProtocol {
3435
shaNonce = CommonUtils.sha256Hash(of: rawNonce)
3536
}
3637

38+
@MainActor public var authButton: SignInWithFacebookButton {
39+
return SignInWithFacebookButton()
40+
}
41+
3742
@MainActor public func signInWithFacebook(isLimitedLogin: Bool) async throws -> AuthCredential {
3843
let trackingStatus = ATTrackingManager.trackingAuthorizationStatus
3944
let tracking: LoginTracking = trackingStatus != .authorized ? .limited :
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// AuthService+Google.swift
3+
// FirebaseUI
4+
//
5+
// Created by Morgan Chen on 4/16/25.
6+
//
7+
8+
import FirebaseAuthSwiftUI
9+
10+
public extension AuthService {
11+
12+
@discardableResult
13+
public func withGoogleSignIn() -> AuthService {
14+
let clientID = auth.app?.options.clientID ?? ""
15+
self.googleProvider = GoogleProviderSwift(clientID: clientID)
16+
return self
17+
}
18+
19+
}

FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderSwift.swift

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
@preconcurrency import FirebaseAuth
22
import FirebaseAuthSwiftUI
3+
import FirebaseCore
34
import GoogleSignIn
5+
import GoogleSignInSwift
6+
import SwiftUI
47

58
let kGoogleUserInfoEmailScope = "https://www.googleapis.com/auth/userinfo.email"
69
let kGoogleUserInfoProfileScope = "https://www.googleapis.com/auth/userinfo.profile"
@@ -16,12 +19,18 @@ public class GoogleProviderSwift: @preconcurrency GoogleProviderProtocol {
1619
let scopes: [String]
1720
let shortName = "Google"
1821
let providerId = "google.com"
19-
public init(scopes: [String]? = nil) {
22+
let clientID: String
23+
public init(scopes: [String]? = nil, clientID: String = FirebaseApp.app()!.options.clientID!) {
2024
self.scopes = scopes ?? kDefaultScopes
25+
self.clientID = clientID
2126
}
2227

23-
public func handleUrl(_ url: URL) -> Bool {
24-
return GIDSignIn.sharedInstance.handle(url)
28+
@MainActor public var authButton: GoogleSignInButton {
29+
return GoogleSignInButton {
30+
Task {
31+
try await self.signInWithGoogle(clientID: self.clientID)
32+
}
33+
}
2534
}
2635

2736
@MainActor public func signInWithGoogle(clientID: String) async throws -> AuthCredential {

FirebaseSwiftUI/FirebasePhoneAuthSwiftUI/Sources/Services/PhoneAuthProviderSwift.swift

+7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
@preconcurrency import FirebaseAuth
22
import FirebaseAuthSwiftUI
3+
import SwiftUI
34

45
public typealias VerificationID = String
56

67
public class PhoneAuthProviderSwift: @preconcurrency PhoneAuthProviderProtocol {
8+
9+
public var authButton: Button<Text> {
10+
// TODO: implement me
11+
return Button("Phone", action: { })
12+
}
13+
714
public init() {}
815

916
@MainActor public func verifyPhoneNumber(phoneNumber: String) async throws -> VerificationID {

Package.swift

+1
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ let package = Package(
277277
dependencies: [
278278
"FirebaseAuthSwiftUI",
279279
"GoogleSignIn",
280+
.product(name: "GoogleSignInSwift", package: "GoogleSignIn")
280281
],
281282
path: "FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources"
282283
),

samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj

+19-19
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
/* Begin PBXBuildFile section */
1010
4607CC9C2D9BFE29009EC3F5 /* FirebaseAuthSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4607CC9B2D9BFE29009EC3F5 /* FirebaseAuthSwiftUI */; };
1111
4607CC9E2D9BFE29009EC3F5 /* FirebaseGoogleSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4607CC9D2D9BFE29009EC3F5 /* FirebaseGoogleSwiftUI */; };
12-
4670DEA72D9EA9E100E0D36A /* FirebaseFacebookSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4670DEA62D9EA9E100E0D36A /* FirebaseFacebookSwiftUI */; };
13-
46C4EAB32DA801B200FC878B /* FirebasePhoneAuthSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 46C4EAB22DA801B200FC878B /* FirebasePhoneAuthSwiftUI */; };
1412
46CB7B252D773F2100F1FD0A /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 46CB7B242D773F2100F1FD0A /* GoogleService-Info.plist */; };
1513
46F89C392D64B04E000F8BC0 /* FirebaseAuthSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 46F89C382D64B04E000F8BC0 /* FirebaseAuthSwiftUI */; };
1614
46F89C4D2D64BB9B000F8BC0 /* FirebaseAuthSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 46F89C4C2D64BB9B000F8BC0 /* FirebaseAuthSwiftUI */; };
15+
8D808CB72DB0811900D2293F /* FirebaseFacebookSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 8D808CB62DB0811900D2293F /* FirebaseFacebookSwiftUI */; };
16+
8D808CB92DB081F900D2293F /* FirebasePhoneAuthSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 8D808CB82DB081F900D2293F /* FirebasePhoneAuthSwiftUI */; };
1717
/* End PBXBuildFile section */
1818

1919
/* Begin PBXContainerItemProxy section */
@@ -76,11 +76,11 @@
7676
isa = PBXFrameworksBuildPhase;
7777
buildActionMask = 2147483647;
7878
files = (
79-
4670DEA72D9EA9E100E0D36A /* FirebaseFacebookSwiftUI in Frameworks */,
79+
8D808CB72DB0811900D2293F /* FirebaseFacebookSwiftUI in Frameworks */,
8080
46F89C392D64B04E000F8BC0 /* FirebaseAuthSwiftUI in Frameworks */,
8181
46F89C4D2D64BB9B000F8BC0 /* FirebaseAuthSwiftUI in Frameworks */,
8282
4607CC9E2D9BFE29009EC3F5 /* FirebaseGoogleSwiftUI in Frameworks */,
83-
46C4EAB32DA801B200FC878B /* FirebasePhoneAuthSwiftUI in Frameworks */,
83+
8D808CB92DB081F900D2293F /* FirebasePhoneAuthSwiftUI in Frameworks */,
8484
4607CC9C2D9BFE29009EC3F5 /* FirebaseAuthSwiftUI in Frameworks */,
8585
);
8686
runOnlyForDeploymentPostprocessing = 0;
@@ -155,8 +155,8 @@
155155
46F89C4C2D64BB9B000F8BC0 /* FirebaseAuthSwiftUI */,
156156
4607CC9B2D9BFE29009EC3F5 /* FirebaseAuthSwiftUI */,
157157
4607CC9D2D9BFE29009EC3F5 /* FirebaseGoogleSwiftUI */,
158-
4670DEA62D9EA9E100E0D36A /* FirebaseFacebookSwiftUI */,
159-
46C4EAB22DA801B200FC878B /* FirebasePhoneAuthSwiftUI */,
158+
8D808CB62DB0811900D2293F /* FirebaseFacebookSwiftUI */,
159+
8D808CB82DB081F900D2293F /* FirebasePhoneAuthSwiftUI */,
160160
);
161161
productName = FirebaseSwiftUIExample;
162162
productReference = 46F89C082D64A86C000F8BC0 /* FirebaseSwiftUIExample.app */;
@@ -241,7 +241,7 @@
241241
mainGroup = 46F89BFF2D64A86C000F8BC0;
242242
minimizedProjectReferenceProxies = 1;
243243
packageReferences = (
244-
4607CC9A2D9BFE29009EC3F5 /* XCLocalSwiftPackageReference "../../../../firebaseUI-ios" */,
244+
8D808CB52DB07EBD00D2293F /* XCLocalSwiftPackageReference "../../../../FirebaseUI-iOS" */,
245245
);
246246
preferredProjectObjectVersion = 77;
247247
productRefGroup = 46F89C092D64A86C000F8BC0 /* Products */;
@@ -617,9 +617,9 @@
617617
/* End XCConfigurationList section */
618618

619619
/* Begin XCLocalSwiftPackageReference section */
620-
4607CC9A2D9BFE29009EC3F5 /* XCLocalSwiftPackageReference "../../../../firebaseUI-ios" */ = {
620+
8D808CB52DB07EBD00D2293F /* XCLocalSwiftPackageReference "../../../../FirebaseUI-iOS" */ = {
621621
isa = XCLocalSwiftPackageReference;
622-
relativePath = "../../../../firebaseUI-ios";
622+
relativePath = "../../../../FirebaseUI-iOS";
623623
};
624624
/* End XCLocalSwiftPackageReference section */
625625

@@ -632,16 +632,6 @@
632632
isa = XCSwiftPackageProductDependency;
633633
productName = FirebaseGoogleSwiftUI;
634634
};
635-
4670DEA62D9EA9E100E0D36A /* FirebaseFacebookSwiftUI */ = {
636-
isa = XCSwiftPackageProductDependency;
637-
package = 4607CC9A2D9BFE29009EC3F5 /* XCLocalSwiftPackageReference "../../../../firebaseUI-ios" */;
638-
productName = FirebaseFacebookSwiftUI;
639-
};
640-
46C4EAB22DA801B200FC878B /* FirebasePhoneAuthSwiftUI */ = {
641-
isa = XCSwiftPackageProductDependency;
642-
package = 4607CC9A2D9BFE29009EC3F5 /* XCLocalSwiftPackageReference "../../../../firebaseUI-ios" */;
643-
productName = FirebasePhoneAuthSwiftUI;
644-
};
645635
46F89C382D64B04E000F8BC0 /* FirebaseAuthSwiftUI */ = {
646636
isa = XCSwiftPackageProductDependency;
647637
productName = FirebaseAuthSwiftUI;
@@ -650,6 +640,16 @@
650640
isa = XCSwiftPackageProductDependency;
651641
productName = FirebaseAuthSwiftUI;
652642
};
643+
8D808CB62DB0811900D2293F /* FirebaseFacebookSwiftUI */ = {
644+
isa = XCSwiftPackageProductDependency;
645+
package = 8D808CB52DB07EBD00D2293F /* XCLocalSwiftPackageReference "../../../../FirebaseUI-iOS" */;
646+
productName = FirebaseFacebookSwiftUI;
647+
};
648+
8D808CB82DB081F900D2293F /* FirebasePhoneAuthSwiftUI */ = {
649+
isa = XCSwiftPackageProductDependency;
650+
package = 8D808CB52DB07EBD00D2293F /* XCLocalSwiftPackageReference "../../../../FirebaseUI-iOS" */;
651+
productName = FirebasePhoneAuthSwiftUI;
652+
};
653653
/* End XCSwiftPackageProductDependency section */
654654
};
655655
rootObject = 46F89C002D64A86C000F8BC0 /* Project object */;

samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift

+9-6
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,10 @@ import FirebaseCore
1212
import FirebaseFacebookSwiftUI
1313
import FirebaseGoogleSwiftUI
1414
import FirebasePhoneAuthSwiftUI
15+
import GoogleSignIn
1516
import SwiftData
1617
import SwiftUI
1718

18-
let googleProvider = GoogleProviderSwift()
19-
2019
class AppDelegate: NSObject, UIApplicationDelegate {
2120
func application(_ application: UIApplication,
2221
didFinishLaunchingWithOptions launchOptions: [
@@ -47,15 +46,17 @@ class AppDelegate: NSObject, UIApplicationDelegate {
4746
func application(_ app: UIApplication,
4847
open url: URL,
4948
options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
50-
ApplicationDelegate.shared.application(
49+
if ApplicationDelegate.shared.application(
5150
app,
5251
open: url,
5352
sourceApplication: options[UIApplication.OpenURLOptionsKey
5453
.sourceApplication] as? String,
5554
annotation: options[UIApplication.OpenURLOptionsKey.annotation]
56-
)
55+
) {
56+
return true
57+
}
5758

58-
return googleProvider.handleUrl(url)
59+
return GIDSignIn.sharedInstance.handle(url)
5960
}
6061
}
6162

@@ -94,10 +95,12 @@ struct ContentView: View {
9495
let phoneAuthProvider = PhoneAuthProviderSwift()
9596
authService = AuthService(
9697
configuration: configuration,
97-
googleProvider: googleProvider,
98+
googleProvider: nil,
9899
facebookProvider: facebookProvider,
99100
phoneAuthProvider: phoneAuthProvider
100101
)
102+
// Transition to this api
103+
.withGoogleSignIn()
101104
}
102105

103106
var body: some View {

0 commit comments

Comments
 (0)