diff --git a/.gitignore b/.gitignore index 613935b581..7b68a9a26f 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,7 @@ DerivedData *.hmap *.ipa *.xcuserstate - +.build/ # Third Party /sdk diff --git a/.swiftformat b/.swiftformat new file mode 100644 index 0000000000..2dfdf2f304 --- /dev/null +++ b/.swiftformat @@ -0,0 +1,5 @@ +# Formatting Options +--indent 2 +--maxwidth 100 +--wrapparameters afterfirst +--disable wrapMultilineStatementBraces \ No newline at end of file diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/.gitignore b/FirebaseSwiftUI/FirebaseAuthSwiftUI/.gitignore new file mode 100644 index 0000000000..0023a53406 --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthConfiguration.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthConfiguration.swift new file mode 100644 index 0000000000..c6ca7d76a0 --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthConfiguration.swift @@ -0,0 +1,24 @@ +import Foundation + +public final class AuthConfiguration { + let shouldHideCancelButton: Bool + let interactiveDismissEnabled: Bool + let shouldAutoUpgradeAnonymousUsers: Bool + let customStringsBundle: Bundle? + let tosUrl: URL + let privacyPolicyUrl: URL + + public init(shouldHideCancelButton: Bool = false, + interactiveDismissEnabled: Bool = true, + shouldAutoUpgradeAnonymousUsers: Bool = false, + customStringsBundle: Bundle? = nil, + tosUrl: URL = URL(string: "https://example.com/tos")!, + privacyPolicyUrl: URL = URL(string: "https://example.com/privacy")!) { + self.shouldHideCancelButton = shouldHideCancelButton + self.interactiveDismissEnabled = interactiveDismissEnabled + self.shouldAutoUpgradeAnonymousUsers = shouldAutoUpgradeAnonymousUsers + self.customStringsBundle = customStringsBundle + self.tosUrl = tosUrl + self.privacyPolicyUrl = privacyPolicyUrl + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift new file mode 100644 index 0000000000..8c229bdb44 --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Services/AuthService.swift @@ -0,0 +1,205 @@ +@preconcurrency import FirebaseAuth +import SwiftUI + +public protocol GoogleProviderProtocol { + func handleUrl(_ url: URL) -> Bool + @MainActor func signInWithGoogle(clientID: String) async throws -> AuthCredential +} + +public protocol FacebookProviderProtocol {} + +public enum AuthenticationProvider { + case email + case google +} + +public enum AuthenticationOperationType: String { + case signIn + case signUp + case deleteAccount +} + +public enum AuthenticationState { + case unauthenticated + case authenticating + case authenticated +} + +public enum AuthenticationFlow { + case login + case signUp +} + +@MainActor +final class AuthListenerManager { + private var authStateHandle: AuthStateDidChangeListenerHandle? + private let auth: Auth + private weak var authEnvironment: AuthService? + + init(auth: Auth, authEnvironment: AuthService) { + self.auth = auth + self.authEnvironment = authEnvironment + setupAuthenticationListener() + } + + deinit { + if let handle = authStateHandle { + auth.removeStateDidChangeListener(handle) + } + } + + private func setupAuthenticationListener() { + authStateHandle = auth.addStateDidChangeListener { [weak self] _, user in + self?.authEnvironment?.currentUser = user + self?.authEnvironment?.updateAuthenticationState() + } + } +} + +@MainActor +@Observable +public final class AuthService { + public let configuration: AuthConfiguration + public let auth: Auth + private var listenerManager: AuthListenerManager? + private let googleProvider: GoogleProviderProtocol? + private let facebookProvider: FacebookProviderProtocol? + public let string: StringUtils + + public init(configuration: AuthConfiguration = AuthConfiguration(), auth: Auth = Auth.auth(), + googleProvider: GoogleProviderProtocol? = nil, + facebookProvider: FacebookProviderProtocol? = nil) { + self.auth = auth + self.configuration = configuration + self.googleProvider = googleProvider + self.facebookProvider = facebookProvider + string = StringUtils(bundle: configuration.customStringsBundle ?? Bundle.module) + listenerManager = AuthListenerManager(auth: auth, authEnvironment: self) + } + + public var currentUser: User? + public var authenticationState: AuthenticationState = .unauthenticated + public var authenticationFlow: AuthenticationFlow = .login + + private var safeGoogleProvider: GoogleProviderProtocol { + get throws { + guard let provider = googleProvider else { + throw NSError( + domain: "AuthEnvironmentErrorDomain", + code: 1, + userInfo: [ + NSLocalizedDescriptionKey: "`GoogleProviderSwift` has not been configured", + ] + ) + } + return provider + } + } + + private var safeFacebookProvider: FacebookProviderProtocol { + get throws { + guard let provider = facebookProvider else { + throw NSError( + domain: "AuthEnvironmentErrorDomain", + code: 1, + userInfo: [ + NSLocalizedDescriptionKey: "`FacebookProviderSwift` has not been configured", + ] + ) + } + return provider + } + } + + func updateAuthenticationState() { + authenticationState = + (currentUser == nil || currentUser?.isAnonymous == true) + ? .unauthenticated + : .authenticated + } + + public func signOut() async throws { + try await auth.signOut() + updateAuthenticationState() + } + + public func signInWithGoogle() async throws { + authenticationState = .authenticating + do { + guard let clientID = auth.app?.options.clientID else { throw NSError( + domain: "AuthServiceErrorDomain", + code: 2, + userInfo: [ + NSLocalizedDescriptionKey: "OAuth client ID not found. Please make sure Google Sign-In is enabled in the Firebase console. You may have to download a new GoogleService-Info.plist file after enabling Google Sign-In.", + ] + ) } + let credential = try await safeGoogleProvider.signInWithGoogle(clientID: clientID) + + try await signIn(with: credential) + updateAuthenticationState() + } catch { + authenticationState = .unauthenticated + throw error + } + } + + func signIn(with credentials: AuthCredential) async throws { + authenticationState = .authenticating + do { + try await auth.signIn(with: credentials) + updateAuthenticationState() + } catch { + authenticationState = .unauthenticated + throw error + } + } + + func sendEmailVerification() async throws { + if currentUser != nil { + do { + // TODO: - can use set user action code settings? + try await currentUser!.sendEmailVerification() + } catch { + throw error + } + } + } + + func signIn(withEmail email: String, password: String) async throws { + let credential = EmailAuthProvider.credential(withEmail: email, password: password) + try await auth.signIn(with: credential) + } + + func createUser(withEmail email: String, password: String) async throws { + authenticationState = .authenticating + do { + try await auth.createUser(withEmail: email, password: password) + updateAuthenticationState() + } catch { + authenticationState = .unauthenticated + throw error + } + } + + func sendPasswordRecoveryEmail(to email: String) async throws { + do { + try await auth.sendPasswordReset(withEmail: email) + } catch { + throw error + } + } + + func sendEmailSignInLink(to email: String) async throws { + do { + // TODO: - how does user set action code settings? Needs configuring + let actionCodeSettings = ActionCodeSettings() + actionCodeSettings.handleCodeInApp = true + try await auth.sendSignInLink( + toEmail: email, + actionCodeSettings: actionCodeSettings + ) + } catch { + throw error + } + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/en.lproj/Localizable.strings b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/en.lproj/Localizable.strings new file mode 100644 index 0000000000..28bd21991c --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Strings/en.lproj/Localizable.strings @@ -0,0 +1,23 @@ +/* Error message displayed when there's no account matching the email address. Use short/abbreviated translation for 'email' which is less than 15 chars. */ +"UserNotFoundError" = "That email address doesn’t match an existing account."; + +/* Error message displayed when the email address is already in use. Use short/abbreviated translation for 'email' which is less than 15 chars. */ +"EmailAlreadyInUseError" = "The email address is already in use by another account."; + +/* Error message displayed when user enters an invalid email address. Use short/abbreviated translation for 'email' which is less than 15 chars. */ +"InvalidEmailError" = "That email address isn't correct."; + +/* Error message displayed when the password is too weak. */ +"WeakPasswordError" = "Password must be at least 6 characters long."; + +/* Error message displayed when many accounts have been created from same IP address. */ +"SignUpTooManyTimesError" = "Too many account requests are coming from your IP address. Try again in a few minutes."; + +/* Error message displayed when the email and password don't match. Use short/abbreviated translation for 'email' which is less than 15 chars. */ +"WrongPasswordError" = "The email and password you entered don't match."; + +/* Error message displayed when the account is disabled. Use short/abbreviated translation for 'email' which is less than 15 chars. */ +"AccountDisabledError" = "That email address is for an account that has been disabled."; + +/* Error message displayed when after re-authorization current user's email and re-authorized user's email doesn't match. Use short/abbreviated translation for 'email' which is less than 15 chars. */ +"EmailsDoNotMatchError" = "Emails don't match"; \ No newline at end of file diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/EmailUtils.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/EmailUtils.swift new file mode 100644 index 0000000000..24304f7f0f --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/EmailUtils.swift @@ -0,0 +1,10 @@ +import Foundation + +class EmailUtils { + static let emailRegex = ".+@([a-zA-Z0-9\\-]+\\.)+[a-zA-Z0-9]{2,63}" + + static func isValidEmail(_ email: String) -> Bool { + let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegex) + return emailPredicate.evaluate(with: email) + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift new file mode 100644 index 0000000000..d7a6ade5b8 --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift @@ -0,0 +1,58 @@ +import FirebaseAuth +import SwiftUI + +let kKeyNotFound = "Key not found" + +let kUsersNotFoundError = "UserNotFoundError" +let kEmailAlreadyInUseError = "EmailAlreadyInUseError" +let kInvalidEmailError = "InvalidEmailError" +let kWeakPasswordError = "WeakPasswordError" +let kSignUpTooManyTimesError = "SignUpTooManyTimesError" +let kWrongPasswordError = "WrongPasswordError" +let kAccountDisabledError = "AccountDisabledError" +let kEmailsDoNotMatchError = "EmailsDoNotMatchError" +let kUnknownError = "UnknownError" + +public class StringUtils { + let bundle: Bundle + init(bundle: Bundle) { + self.bundle = bundle + } + + public func localizedString(forKey key: String) -> String { + return bundle.localizedString(forKey: key, value: nil, table: nil) + } + + public func localizedErrorMessage(for error: Error) -> String { + let authError = error as NSError + let errorCode = AuthErrorCode(rawValue: authError.code) + switch errorCode { + case .emailAlreadyInUse: + return localizedString( + forKey: kEmailAlreadyInUseError + ) + case .invalidEmail: + return localizedString(forKey: kInvalidEmailError) + case .weakPassword: + return localizedString(forKey: kWeakPasswordError) + case .tooManyRequests: + return localizedString( + forKey: kSignUpTooManyTimesError + ) + case .wrongPassword: + return localizedString( + forKey: kWrongPasswordError + ) + case .userNotFound: + return localizedString( + forKey: kUsersNotFoundError + ) + case .userDisabled: + return localizedString( + forKey: kAccountDisabledError + ) + default: + return error.localizedDescription + } + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift new file mode 100644 index 0000000000..d1c6bcf22c --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift @@ -0,0 +1,46 @@ +import SwiftUI + +@MainActor +public struct AuthPickerView { + @Environment(AuthService.self) private var authService + let providerButtons: () -> Content + + public init(@ViewBuilder providerButtons: @escaping () -> Content) { + self.providerButtons = providerButtons + } + + private func switchFlow() { + authService.authenticationFlow = authService + .authenticationFlow == .login ? .signUp : .login + } +} + +extension AuthPickerView: View { + public var body: some View { + VStack { + if authService.authenticationState == .authenticated { + SignedInView() + } else { + Text(authService.authenticationFlow == .login ? "Login" : "Sign up") + VStack { Divider() } + EmailAuthView() + providerButtons() + VStack { Divider() } + HStack { + Text(authService + .authenticationFlow == .login ? "Don't have an account yet?" : + "Already have an account?") + Button(action: { + withAnimation { + switchFlow() + } + }) { + Text(authService.authenticationFlow == .signUp ? "Log in" : "Sign up") + .fontWeight(.semibold) + .foregroundColor(.blue) + } + } + } + } + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift new file mode 100644 index 0000000000..e28f5ecdef --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailAuthView.swift @@ -0,0 +1,141 @@ +// +// EmailPasswordView.swift +// FirebaseUI +// +// Created by Russell Wheatley on 20/03/2025. +// +import SwiftUI + +private enum FocusableField: Hashable { + case email + case password + case confirmPassword +} + +@MainActor +public struct EmailAuthView { + @Environment(AuthService.self) private var authService + + @State private var email = "" + @State private var password = "" + @State private var confirmPassword = "" + @State private var errorMessage = "" + + @FocusState private var focus: FocusableField? + + public init() {} + + private var isValid: Bool { + return if authService.authenticationFlow == .login { + !email.isEmpty && !password.isEmpty + } else { + !email.isEmpty && !password.isEmpty && password == confirmPassword + } + } + + private func signInWithEmailPassword() async { + do { + try await authService.signIn(withEmail: email, password: password) + } catch { + errorMessage = authService.string.localizedErrorMessage( + for: error + ) + } + } + + private func createUserWithEmailPassword() async { + do { + try await authService.createUser(withEmail: email, password: password) + } catch { + errorMessage = authService.string.localizedErrorMessage( + for: error + ) + } + } +} + +extension EmailAuthView: View { + public var body: some View { + VStack { + LabeledContent { + TextField("Email", text: $email) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .focused($focus, equals: .email) + .submitLabel(.next) + .onSubmit { + self.focus = .password + } + } label: { + Image(systemName: "at") + } + .padding(.vertical, 6) + .background(Divider(), alignment: .bottom) + .padding(.bottom, 4) + + LabeledContent { + SecureField("Password", text: $password) + .focused($focus, equals: .password) + .submitLabel(.go) + .onSubmit { + Task { await signInWithEmailPassword() } + } + } label: { + Image(systemName: "lock") + } + .padding(.vertical, 6) + .background(Divider(), alignment: .bottom) + .padding(.bottom, 8) + + if authService.authenticationFlow == .login { + NavigationLink(destination: PasswordRecoveryView() + .environment(authService)) { + Text("Forgotten Password?") + } + } + + if authService.authenticationFlow == .signUp { + LabeledContent { + SecureField("Confirm password", text: $confirmPassword) + .focused($focus, equals: .confirmPassword) + .submitLabel(.go) + .onSubmit { + Task { await createUserWithEmailPassword() } + } + } label: { + Image(systemName: "lock") + } + .padding(.vertical, 6) + .background(Divider(), alignment: .bottom) + .padding(.bottom, 8) + } + + Button(action: { + Task { + if authService.authenticationFlow == .login { await signInWithEmailPassword() } + else { await createUserWithEmailPassword() } + } + }) { + if authService.authenticationState != .authenticating { + Text(authService.authenticationFlow == .login ? "Log in with password" : "Sign up") + .padding(.vertical, 8) + .frame(maxWidth: .infinity) + } else { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + .padding(.vertical, 8) + .frame(maxWidth: .infinity) + } + } + .disabled(!isValid) + .padding([.top, .bottom], 8) + .frame(maxWidth: .infinity) + .buttonStyle(.borderedProminent) + NavigationLink(destination: EmailLinkView().environment(authService) + .environment(authService)) { + Text("Prefer Email link sign-in?") + } + Text(errorMessage).foregroundColor(.red) + } + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailLinkView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailLinkView.swift new file mode 100644 index 0000000000..4e933c3938 --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/EmailLinkView.swift @@ -0,0 +1,63 @@ +import SwiftUI + +public struct EmailLinkView { + @Environment(AuthService.self) private var authService + @State private var email = "" + @State private var errorMessage = "" + @State private var showModal = false + + public init() {} + + private func sendEmailLink() async { + do { + try await authService.sendEmailSignInLink(to: email) + showModal = true + } catch { + errorMessage = error.localizedDescription + } + } +} + +extension EmailLinkView: View { + public var body: some View { + VStack { + Text("Sign in with email link") + LabeledContent { + TextField("Email", text: $email) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .submitLabel(.next) + } label: { + Image(systemName: "at") + }.padding(.vertical, 6) + .background(Divider(), alignment: .bottom) + .padding(.bottom, 4) + Button(action: { + Task { + await sendEmailLink() + } + }) { + Text("Send email sign-in link") + .padding(.vertical, 8) + .frame(maxWidth: .infinity) + } + .disabled(!EmailUtils.isValidEmail(email)) + .padding([.top, .bottom], 8) + .frame(maxWidth: .infinity) + .buttonStyle(.borderedProminent) + Text(errorMessage).foregroundColor(.red) + }.sheet(isPresented: $showModal) { + VStack { + Text("Instructions") + .font(.headline) + Text("Please check your email for email sign-in link.") + .padding() + Button("Dismiss") { + showModal = false + } + .padding() + } + .padding() + } + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/PasswordRecoveryView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/PasswordRecoveryView.swift new file mode 100644 index 0000000000..71808e19da --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/PasswordRecoveryView.swift @@ -0,0 +1,62 @@ +import SwiftUI + +public struct PasswordRecoveryView { + @Environment(AuthService.self) private var authService + @State private var email = "" + @State private var errorMessage = "" + @State private var showModal = false + + public init() {} + + private func sendPasswordRecoveryEmail() async { + do { + try await authService.sendPasswordRecoveryEmail(to: email) + showModal = true + } catch { + errorMessage = error.localizedDescription + } + } +} + +extension PasswordRecoveryView: View { + public var body: some View { + VStack { + Text("Password Recovery") + LabeledContent { + TextField("Email", text: $email) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .submitLabel(.next) + } label: { + Image(systemName: "at") + }.padding(.vertical, 6) + .background(Divider(), alignment: .bottom) + .padding(.bottom, 4) + Button(action: { + Task { + await sendPasswordRecoveryEmail() + } + }) { + Text("Password Recovery") + .padding(.vertical, 8) + .frame(maxWidth: .infinity) + } + .disabled(!EmailUtils.isValidEmail(email)) + .padding([.top, .bottom], 8) + .frame(maxWidth: .infinity) + .buttonStyle(.borderedProminent) + }.sheet(isPresented: $showModal) { + VStack { + Text("Instructions") + .font(.headline) + Text("Please check your email for password recovery instructions.") + .padding() + Button("Dismiss") { + showModal = false + } + .padding() + } + .padding() + } + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/SignedInView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/SignedInView.swift new file mode 100644 index 0000000000..1499201ecf --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/SignedInView.swift @@ -0,0 +1,22 @@ +import SwiftUI + +public struct SignedInView { + @Environment(AuthService.self) private var authService +} + +extension SignedInView: View { + public var body: some View { + VStack { + Text("Signed in") + Text("User: \(authService.currentUser?.email ?? "Unknown")") + Button("Sign out") { + Task { + try? await authService.signOut() + } + } + if authService.currentUser?.isEmailVerified == false { + VerifyEmailView() + } + } + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/VerifyEmailView.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/VerifyEmailView.swift new file mode 100644 index 0000000000..88e0773a28 --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/VerifyEmailView.swift @@ -0,0 +1,47 @@ +import SwiftUI + +public struct VerifyEmailView { + @Environment(AuthService.self) private var authService + @State private var errorMessage = "" + @State private var showModal = false + + private func sendEmailVerification() async { + do { + try await authService.sendEmailVerification() + showModal = true + } catch { + errorMessage = error.localizedDescription + } + } +} + +extension VerifyEmailView: View { + public var body: some View { + VStack { + Button(action: { + Task { + await sendEmailVerification() + } + }) { + Text("Verify email address?") + .padding(.vertical, 8) + .frame(maxWidth: .infinity) + } + .padding([.top, .bottom], 8) + .frame(maxWidth: .infinity) + .buttonStyle(.borderedProminent) + }.sheet(isPresented: $showModal) { + VStack { + Text("Instructions") + .font(.headline) + Text("Please check your email for verification link.") + .padding() + Button("Dismiss") { + showModal = false + } + .padding() + } + .padding() + } + } +} diff --git a/FirebaseSwiftUI/FirebaseAuthSwiftUI/Tests/FirebaseAuthSwiftUITests/FirebaseAuthSwiftUITests.swift b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Tests/FirebaseAuthSwiftUITests/FirebaseAuthSwiftUITests.swift new file mode 100644 index 0000000000..81d1f47565 --- /dev/null +++ b/FirebaseSwiftUI/FirebaseAuthSwiftUI/Tests/FirebaseAuthSwiftUITests/FirebaseAuthSwiftUITests.swift @@ -0,0 +1,6 @@ +@testable import FirebaseAuthSwiftUI +import Testing + +@Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. +} diff --git a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/.gitignore b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/.gitignore new file mode 100644 index 0000000000..0023a53406 --- /dev/null +++ b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/FirebaseFacebookSwiftUI.swift b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/FirebaseFacebookSwiftUI.swift new file mode 100644 index 0000000000..08b22b80fc --- /dev/null +++ b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/FirebaseFacebookSwiftUI.swift @@ -0,0 +1,2 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book diff --git a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderSwift.swift b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderSwift.swift new file mode 100644 index 0000000000..8b263676f9 --- /dev/null +++ b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Services/FacebookProviderSwift.swift @@ -0,0 +1,18 @@ +// @preconcurrency import FirebaseAuth +import FirebaseAuthSwiftUI + +public enum FacebookLoginType { + case classic + case limitedLogin +} + +public class FacebookProviderSwift: FacebookProviderProtocol { + let shortName = "Facebook" + let providerId = "facebook.com" + var loginType: FacebookLoginType + public init(loginType: FacebookLoginType = FacebookLoginType.classic) { + self.loginType = loginType + } + + public func signInWithFacebook() {} +} diff --git a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Views/FacebookButtonView.swift b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Views/FacebookButtonView.swift new file mode 100644 index 0000000000..ccb47ee2ac --- /dev/null +++ b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources/Views/FacebookButtonView.swift @@ -0,0 +1,34 @@ +import FirebaseAuthSwiftUI +import SwiftUI + +@MainActor +public struct FacebookButtonView { + @Environment(AuthService.self) private var authService + @State private var errorMessage = "" + + public init() {} + + private func signInWithFacebook() async {} +} + +extension FacebookButtonView: View { + public var body: some View { + Button(action: { + Task { + try await signInWithFacebook() + } + }) { + if authService.authenticationState != .authenticating { + Text(authService.authenticationFlow == .login ? "Login with Google" : "Sign-up with Google") + .padding(.vertical, 8) + .frame(maxWidth: .infinity) + } else { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + .padding(.vertical, 8) + .frame(maxWidth: .infinity) + } + } + Text(errorMessage).foregroundColor(.red) + } +} diff --git a/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Tests/FirebaseFacebookSwiftUITests/FirebaseFacebookSwiftUITests.swift b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Tests/FirebaseFacebookSwiftUITests/FirebaseFacebookSwiftUITests.swift new file mode 100644 index 0000000000..0b2e3b1e3e --- /dev/null +++ b/FirebaseSwiftUI/FirebaseFacebookSwiftUI/Tests/FirebaseFacebookSwiftUITests/FirebaseFacebookSwiftUITests.swift @@ -0,0 +1,6 @@ +@testable import FirebaseFacebookSwiftUI +import Testing + +@Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. +} diff --git a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/.gitignore b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/.gitignore new file mode 100644 index 0000000000..0023a53406 --- /dev/null +++ b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderSwift.swift b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderSwift.swift new file mode 100644 index 0000000000..90b65cd49f --- /dev/null +++ b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Services/GoogleProviderSwift.swift @@ -0,0 +1,63 @@ +@preconcurrency import FirebaseAuth +import FirebaseAuthSwiftUI +import GoogleSignIn + +let kGoogleUserInfoEmailScope = "https://www.googleapis.com/auth/userinfo.email" +let kGoogleUserInfoProfileScope = "https://www.googleapis.com/auth/userinfo.profile" +let kDefaultScopes = [kGoogleUserInfoEmailScope, kGoogleUserInfoProfileScope] + +public class GoogleProviderSwift: @preconcurrency GoogleProviderProtocol { + let scopes: [String] + let shortName = "Google" + let providerId = "google.com" + public init(scopes: [String]? = nil) { + self.scopes = scopes ?? kDefaultScopes + } + + public func handleUrl(_ url: URL) -> Bool { + return GIDSignIn.sharedInstance.handle(url) + } + + @MainActor public func signInWithGoogle(clientID: String) async throws -> AuthCredential { + guard let presentingViewController = await (UIApplication.shared.connectedScenes + .first as? UIWindowScene)?.windows.first?.rootViewController else { + throw NSError( + domain: "GoogleProviderSwiftErrorDomain", + code: 1, + userInfo: [ + NSLocalizedDescriptionKey: "Root View controller is not available to present Google sign-in View.", + ] + ) + } + + let config = GIDConfiguration(clientID: clientID) + GIDSignIn.sharedInstance.configuration = config + + return try await withCheckedThrowingContinuation { continuation in + GIDSignIn.sharedInstance.signIn( + withPresenting: presentingViewController + ) { result, error in + if let error = error { + continuation.resume(throwing: error) + return + } + + guard let user = result?.user, + let idToken = user.idToken?.tokenString else { + continuation.resume(throwing: NSError( + domain: "GoogleProviderSwiftErrorDomain", + code: 2, + userInfo: [ + NSLocalizedDescriptionKey: "Failed to retrieve user or idToken.", + ] + )) + return + } + + let credential = GoogleAuthProvider.credential(withIDToken: idToken, + accessToken: user.accessToken.tokenString) + continuation.resume(returning: credential) + } + } + } +} diff --git a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Views/GoogleButtonView.swift b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Views/GoogleButtonView.swift new file mode 100644 index 0000000000..d9790dcbcb --- /dev/null +++ b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources/Views/GoogleButtonView.swift @@ -0,0 +1,42 @@ +import FirebaseAuthSwiftUI +import SwiftUI + +@MainActor +public struct GoogleButtonView { + @Environment(AuthService.self) private var authService + @State private var errorMessage = "" + + public init() {} + + private func signInWithGoogle() async { + do { + try await authService.signInWithGoogle() + } catch { + errorMessage = authService.string.localizedErrorMessage( + for: error + ) + } + } +} + +extension GoogleButtonView: View { + public var body: some View { + Button(action: { + Task { + try await signInWithGoogle() + } + }) { + if authService.authenticationState != .authenticating { + Text(authService.authenticationFlow == .login ? "Login with Google" : "Sign-up with Google") + .padding(.vertical, 8) + .frame(maxWidth: .infinity) + } else { + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .white)) + .padding(.vertical, 8) + .frame(maxWidth: .infinity) + } + } + Text(errorMessage).foregroundColor(.red) + } +} diff --git a/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Tests/FirebaseGoogleSwiftUITests/FirebaseGoogleSwiftUITests.swift b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Tests/FirebaseGoogleSwiftUITests/FirebaseGoogleSwiftUITests.swift new file mode 100644 index 0000000000..3f9a362a3a --- /dev/null +++ b/FirebaseSwiftUI/FirebaseGoogleSwiftUI/Tests/FirebaseGoogleSwiftUITests/FirebaseGoogleSwiftUITests.swift @@ -0,0 +1,6 @@ +@testable import FirebaseGoogleSwiftUI +import Testing + +@Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. +} diff --git a/Package.swift b/Package.swift index 937282f068..d4e4cd78ac 100644 --- a/Package.swift +++ b/Package.swift @@ -20,7 +20,7 @@ import PackageDescription let package = Package( name: "FirebaseUI", defaultLocalization: "en", - platforms: [.iOS(.v13)], + platforms: [.iOS(.v17)], products: [ .library( name: "FirebaseAnonymousAuthUI", @@ -62,17 +62,29 @@ let package = Package( name: "FirebaseStorageUI", targets: ["FirebaseStorageUI"] ), + .library( + name: "FirebaseAuthSwiftUI", + targets: ["FirebaseAuthSwiftUI"] + ), + .library( + name: "FirebaseGoogleSwiftUI", + targets: ["FirebaseGoogleSwiftUI"] + ), + .library( + name: "FirebaseFacebookSwiftUI", + targets: ["FirebaseFacebookSwiftUI"] + ), ], dependencies: [ .package( - name: "Facebook", + name: "Facebook", url: "https://github.com/facebook/facebook-ios-sdk.git", - "17.0.0"..<"18.0.0" + "17.0.0" ..< "18.0.0" ), .package( - name: "Firebase", + name: "Firebase", url: "https://github.com/firebase/firebase-ios-sdk.git", - "8.0.0"..<"12.0.0" + "8.0.0" ..< "12.0.0" ), .package( name: "GoogleSignIn", @@ -82,7 +94,7 @@ let package = Package( .package( name: "GoogleUtilities", url: "https://github.com/google/GoogleUtilities.git", - "7.4.1"..<"9.0.0" + "7.4.1" ..< "9.0.0" ), .package( name: "SDWebImage", @@ -183,7 +195,7 @@ let package = Package( name: "FirebaseGoogleAuthUI", dependencies: [ "FirebaseAuthUI", - "GoogleSignIn" + "GoogleSignIn", ], path: "FirebaseGoogleAuthUI/Sources", exclude: ["Info.plist"], @@ -241,5 +253,46 @@ let package = Package( .headerSearchPath("../../"), ] ), + .target( + name: "FirebaseAuthSwiftUI", + dependencies: [ + .product(name: "FirebaseAuth", package: "Firebase"), + ], + path: "FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources", + resources: [ + .process("Strings"), + ] + ), + .testTarget( + name: "FirebaseAuthSwiftUITests", + dependencies: ["FirebaseAuthSwiftUI"], + path: "FirebaseSwiftUI/FirebaseAuthSwiftUI/Tests/" + ), + .target( + name: "FirebaseGoogleSwiftUI", + dependencies: [ + "FirebaseAuthSwiftUI", + "GoogleSignIn", + ], + path: "FirebaseSwiftUI/FirebaseGoogleSwiftUI/Sources" + ), + .testTarget( + name: "FirebaseGoogleSwiftUITests", + dependencies: ["FirebaseGoogleSwiftUI"], + path: "FirebaseSwiftUI/FirebaseGoogleSwiftUI/Tests/" + ), + .target( + name: "FirebaseFacebookSwiftUI", + dependencies: [ + "FirebaseAuthSwiftUI", + .product(name: "FacebookLogin", package: "Facebook"), + ], + path: "FirebaseSwiftUI/FirebaseFacebookSwiftUI/Sources" + ), + .testTarget( + name: "FirebaseFacebookSwiftUITests", + dependencies: ["FirebaseFacebookSwiftUI"], + path: "FirebaseSwiftUI/FirebaseFacebookSwiftUI/Tests/" + ), ] ) diff --git a/format-swift.sh b/format-swift.sh new file mode 100755 index 0000000000..0aec197f4a --- /dev/null +++ b/format-swift.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +swiftformat ./FirebaseSwiftUI + +swiftformat ./samples/swiftui/FirebaseSwiftUIExample + + +swiftformat ./Package.swift \ No newline at end of file diff --git a/samples/swift/FirebaseUI-demo-swift.xcodeproj/project.pbxproj b/samples/swift/FirebaseUI-demo-swift.xcodeproj/project.pbxproj index 476e1ed983..83f23d6129 100644 --- a/samples/swift/FirebaseUI-demo-swift.xcodeproj/project.pbxproj +++ b/samples/swift/FirebaseUI-demo-swift.xcodeproj/project.pbxproj @@ -36,6 +36,8 @@ C39BC0511DB812330060F6AF /* FUICustomPasswordVerificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C39BC04F1DB812330060F6AF /* FUICustomPasswordVerificationViewController.swift */; }; C39BC0521DB812330060F6AF /* FUICustomPasswordVerificationViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C39BC0501DB812330060F6AF /* FUICustomPasswordVerificationViewController.xib */; }; C3F23ECD1D80F3300020509F /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = C3F23ECC1D80F3300020509F /* GoogleService-Info.plist */; }; + D7FF20C65AA51946FA2E77C4 /* Pods_FirebaseUI_demo_swiftTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B1B266F262E24512C267E455 /* Pods_FirebaseUI_demo_swiftTests.framework */; }; + DB34EAFE145DDD05F2A53E21 /* Pods_FirebaseUI_demo_swift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A6651780F4BD5F5177BD9E6D /* Pods_FirebaseUI_demo_swift.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -49,6 +51,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 1DF85F38940228B710FA2D36 /* Pods-FirebaseUI-demo-swiftTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FirebaseUI-demo-swiftTests.release.xcconfig"; path = "Target Support Files/Pods-FirebaseUI-demo-swiftTests/Pods-FirebaseUI-demo-swiftTests.release.xcconfig"; sourceTree = ""; }; + 23633FE989279E7D3A893CB5 /* Pods-FirebaseUI-demo-swift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FirebaseUI-demo-swift.release.xcconfig"; path = "Target Support Files/Pods-FirebaseUI-demo-swift/Pods-FirebaseUI-demo-swift.release.xcconfig"; sourceTree = ""; }; 89B2924622568B1C00CEF7D7 /* twtrsymbol.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = twtrsymbol.png; sourceTree = ""; }; 8D5F93AF1D9B192D00D5A2E4 /* StorageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageViewController.swift; sourceTree = ""; }; 8DABC9851D3D82D600453807 /* FirebaseUI-demo-swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "FirebaseUI-demo-swift.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -59,6 +63,9 @@ 8DABC99D1D3D82D600453807 /* FirebaseUI-demo-swiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FirebaseUI-demo-swiftTests.swift"; sourceTree = ""; }; 8DABC99F1D3D82D600453807 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8DD51E361D873B0D00E2CA51 /* UIStoryboardExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIStoryboardExtension.swift; sourceTree = ""; }; + A6651780F4BD5F5177BD9E6D /* Pods_FirebaseUI_demo_swift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FirebaseUI_demo_swift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B1B266F262E24512C267E455 /* Pods_FirebaseUI_demo_swiftTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FirebaseUI_demo_swiftTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BF8F7C7E13D6D81A1E57351C /* Pods-FirebaseUI-demo-swiftTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FirebaseUI-demo-swiftTests.debug.xcconfig"; path = "Target Support Files/Pods-FirebaseUI-demo-swiftTests/Pods-FirebaseUI-demo-swiftTests.debug.xcconfig"; sourceTree = ""; }; C302C1D51D91CC7B00ADBD41 /* FUIAuthViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FUIAuthViewController.swift; sourceTree = ""; }; C302C1D71D91CC7B00ADBD41 /* ChatCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatCollectionViewCell.swift; sourceTree = ""; }; C302C1D81D91CC7B00ADBD41 /* ChatViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatViewController.swift; sourceTree = ""; }; @@ -166,6 +173,7 @@ C39BC04F1DB812330060F6AF /* FUICustomPasswordVerificationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FUICustomPasswordVerificationViewController.swift; sourceTree = ""; }; C39BC0501DB812330060F6AF /* FUICustomPasswordVerificationViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FUICustomPasswordVerificationViewController.xib; sourceTree = ""; }; C3F23ECC1D80F3300020509F /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + D1B02880ECAE7C8AB98C6332 /* Pods-FirebaseUI-demo-swift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FirebaseUI-demo-swift.debug.xcconfig"; path = "Target Support Files/Pods-FirebaseUI-demo-swift/Pods-FirebaseUI-demo-swift.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -173,6 +181,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + DB34EAFE145DDD05F2A53E21 /* Pods_FirebaseUI_demo_swift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -180,12 +189,22 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + D7FF20C65AA51946FA2E77C4 /* Pods_FirebaseUI_demo_swiftTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 77499FA4EF7D4F9C7DE36B95 /* Frameworks */ = { + isa = PBXGroup; + children = ( + A6651780F4BD5F5177BD9E6D /* Pods_FirebaseUI_demo_swift.framework */, + B1B266F262E24512C267E455 /* Pods_FirebaseUI_demo_swiftTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 8D5F93B11D9B193C00D5A2E4 /* Storage */ = { isa = PBXGroup; children = ( @@ -202,6 +221,7 @@ 8DABC99C1D3D82D600453807 /* FirebaseUI-demo-swiftTests */, 8DABC9861D3D82D600453807 /* Products */, 9C43BF8CA810E7C909775084 /* Pods */, + 77499FA4EF7D4F9C7DE36B95 /* Frameworks */, ); sourceTree = ""; }; @@ -240,6 +260,10 @@ 9C43BF8CA810E7C909775084 /* Pods */ = { isa = PBXGroup; children = ( + D1B02880ECAE7C8AB98C6332 /* Pods-FirebaseUI-demo-swift.debug.xcconfig */, + 23633FE989279E7D3A893CB5 /* Pods-FirebaseUI-demo-swift.release.xcconfig */, + BF8F7C7E13D6D81A1E57351C /* Pods-FirebaseUI-demo-swiftTests.debug.xcconfig */, + 1DF85F38940228B710FA2D36 /* Pods-FirebaseUI-demo-swiftTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -305,9 +329,11 @@ isa = PBXNativeTarget; buildConfigurationList = 8DABC9A21D3D82D600453807 /* Build configuration list for PBXNativeTarget "FirebaseUI-demo-swift" */; buildPhases = ( + A711A34CF80CD0C969A9E286 /* [CP] Check Pods Manifest.lock */, 8DABC9811D3D82D600453807 /* Sources */, 8DABC9821D3D82D600453807 /* Frameworks */, 8DABC9831D3D82D600453807 /* Resources */, + 53A915D8CB2EA7687E61107D /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -322,6 +348,7 @@ isa = PBXNativeTarget; buildConfigurationList = 8DABC9A51D3D82D600453807 /* Build configuration list for PBXNativeTarget "FirebaseUI-demo-swiftTests" */; buildPhases = ( + 665BFB596CE5C127F3D5D0E5 /* [CP] Check Pods Manifest.lock */, 8DABC9951D3D82D600453807 /* Sources */, 8DABC9961D3D82D600453807 /* Frameworks */, 8DABC9971D3D82D600453807 /* Resources */, @@ -348,6 +375,7 @@ TargetAttributes = { 8DABC9841D3D82D600453807 = { CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = YYX2P3XVJ7; LastSwiftMigration = 1020; SystemCapabilities = { com.apple.BackgroundModes = { @@ -496,6 +524,149 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 53A915D8CB2EA7687E61107D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-FirebaseUI-demo-swift/Pods-FirebaseUI-demo-swift-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/AppAuth/AppAuth.framework", + "${BUILT_PRODUCTS_DIR}/AppCheckCore/AppCheckCore.framework", + "${BUILT_PRODUCTS_DIR}/BoringSSL-GRPC/openssl_grpc.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseAnonymousAuthUI/FirebaseAnonymousAuthUI.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseAppCheckInterop/FirebaseAppCheckInterop.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseAuth/FirebaseAuth.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseAuthInterop/FirebaseAuthInterop.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseAuthUI/FirebaseAuthUI.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCore/FirebaseCore.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreExtension/FirebaseCoreExtension.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseCoreInternal/FirebaseCoreInternal.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseDatabase/FirebaseDatabase.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseDatabaseUI/FirebaseDatabaseUI.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseEmailAuthUI/FirebaseEmailAuthUI.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseFacebookAuthUI/FirebaseFacebookAuthUI.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseFirestore/FirebaseFirestore.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseFirestoreInternal/FirebaseFirestoreInternal.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseFirestoreUI/FirebaseFirestoreUI.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseGoogleAuthUI/FirebaseGoogleAuthUI.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseOAuthUI/FirebaseOAuthUI.framework", + "${BUILT_PRODUCTS_DIR}/FirebasePhoneAuthUI/FirebasePhoneAuthUI.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseSharedSwift/FirebaseSharedSwift.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseStorage/FirebaseStorage.framework", + "${BUILT_PRODUCTS_DIR}/FirebaseStorageUI/FirebaseStorageUI.framework", + "${BUILT_PRODUCTS_DIR}/GTMAppAuth/GTMAppAuth.framework", + "${BUILT_PRODUCTS_DIR}/GTMSessionFetcher/GTMSessionFetcher.framework", + "${BUILT_PRODUCTS_DIR}/GoogleSignIn/GoogleSignIn.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/PromisesObjC/FBLPromises.framework", + "${BUILT_PRODUCTS_DIR}/RecaptchaInterop/RecaptchaInterop.framework", + "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework", + "${BUILT_PRODUCTS_DIR}/abseil/absl.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-C++/grpcpp.framework", + "${BUILT_PRODUCTS_DIR}/gRPC-Core/grpc.framework", + "${BUILT_PRODUCTS_DIR}/leveldb-library/leveldb.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/FBAEMKit/FBAEMKit.framework/FBAEMKit", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/FBSDKCoreKit/FBSDKCoreKit.framework/FBSDKCoreKit", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/FBSDKCoreKit_Basics/FBSDKCoreKit_Basics.framework/FBSDKCoreKit_Basics", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/FBSDKLoginKit/FBSDKLoginKit.framework/FBSDKLoginKit", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppAuth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppCheckCore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/openssl_grpc.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAnonymousAuthUI.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAppCheckInterop.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuthInterop.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseAuthUI.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreExtension.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseCoreInternal.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseDatabase.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseDatabaseUI.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseEmailAuthUI.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseFacebookAuthUI.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseFirestore.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseFirestoreInternal.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseFirestoreUI.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseGoogleAuthUI.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseOAuthUI.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebasePhoneAuthUI.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseSharedSwift.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseStorage.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FirebaseStorageUI.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMAppAuth.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GTMSessionFetcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleSignIn.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBLPromises.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RecaptchaInterop.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/absl.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpcpp.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/grpc.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/leveldb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBAEMKit.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKCoreKit_Basics.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FBSDKLoginKit.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-FirebaseUI-demo-swift/Pods-FirebaseUI-demo-swift-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 665BFB596CE5C127F3D5D0E5 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-FirebaseUI-demo-swiftTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + A711A34CF80CD0C969A9E286 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-FirebaseUI-demo-swift-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 8DABC9811D3D82D600453807 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -747,12 +918,13 @@ }; 8DABC9A31D3D82D600453807 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D1B02880ECAE7C8AB98C6332 /* Pods-FirebaseUI-demo-swift.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; BITCODE_GENERATION_MODE = ""; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "FirebaseUI-demo-swift/FirebaseUI-demo-swift.entitlements"; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = YYX2P3XVJ7; HEADER_SEARCH_PATHS = ( "$(inherited)", "${PODS_ROOT}/Firebase/Core/Sources", @@ -776,7 +948,7 @@ INFOPLIST_FILE = "FirebaseUI-demo-swift/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.google.firebase.firebaseui.FirebaseUI-demo-swift"; + PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firebase.auth.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -786,12 +958,13 @@ }; 8DABC9A41D3D82D600453807 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 23633FE989279E7D3A893CB5 /* Pods-FirebaseUI-demo-swift.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; BITCODE_GENERATION_MODE = ""; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "FirebaseUI-demo-swift/FirebaseUI-demo-swift.entitlements"; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = YYX2P3XVJ7; HEADER_SEARCH_PATHS = ( "$(inherited)", "${PODS_ROOT}/Firebase/Core/Sources", @@ -815,7 +988,7 @@ INFOPLIST_FILE = "FirebaseUI-demo-swift/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.google.firebase.firebaseui.FirebaseUI-demo-swift"; + PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firebase.auth.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_VERSION = 6.0; @@ -824,6 +997,7 @@ }; 8DABC9A61D3D82D600453807 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = BF8F7C7E13D6D81A1E57351C /* Pods-FirebaseUI-demo-swiftTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = "FirebaseUI-demo-swiftTests/Info.plist"; @@ -837,6 +1011,7 @@ }; 8DABC9A71D3D82D600453807 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 1DF85F38940228B710FA2D36 /* Pods-FirebaseUI-demo-swiftTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; INFOPLIST_FILE = "FirebaseUI-demo-swiftTests/Info.plist"; diff --git a/samples/swift/FirebaseUI-demo-swift/Info.plist b/samples/swift/FirebaseUI-demo-swift/Info.plist index 18bb33403a..99fb722e04 100644 --- a/samples/swift/FirebaseUI-demo-swift/Info.plist +++ b/samples/swift/FirebaseUI-demo-swift/Info.plist @@ -2,8 +2,6 @@ - FacebookClientToken - aaaaa CFBundleDevelopmentRegion en CFBundleExecutable @@ -47,6 +45,8 @@ 1 FacebookAppID {your-app-id} + FacebookClientToken + aaaaa FacebookDisplayName {your-app-name} LSApplicationQueriesSchemes diff --git a/samples/swift/Podfile.lock b/samples/swift/Podfile.lock index f6f3feaded..1bdd71d8b3 100644 --- a/samples/swift/Podfile.lock +++ b/samples/swift/Podfile.lock @@ -1503,24 +1503,24 @@ SPEC CHECKSUMS: FBSDKCoreKit: 94d7461d0cecf441b1ba7c41acfff41daa8ccd41 FBSDKCoreKit_Basics: 151b43db8b834d3f0e02f95d36a44ffd36265e45 FBSDKLoginKit: 5c1cd53c91a2282b3a4fe6e6d3dcf2b8b0d33d55 - FirebaseAnonymousAuthUI: beac8a00da8c765f14e01bfcd13d9fde8b991e1f + FirebaseAnonymousAuthUI: eb2d9ea96dc1121f47130619abc93a290479bbc1 FirebaseAppCheckInterop: 2376d3ec5cb4267facad4fe754ab4f301a5a519b FirebaseAuth: 77e25aa24f3e1c626c5babd3338551fc1669ee0e FirebaseAuthInterop: a6973d72aa242ea88ffb6be9c9b06c65455071da - FirebaseAuthUI: d2c84c671a24b9d669b4d2483cfd261c2c6e5bab + FirebaseAuthUI: 29804b552165df676c3882669b5ef99717696fa8 FirebaseCore: 3227e35f4197a924206fbcdc0349325baf4f5de4 FirebaseCoreExtension: 206c1b399f0d103055207c16f299b28e3dbd1949 FirebaseCoreInternal: d6c17dafc8dc33614733a8b52df78fcb4394c881 FirebaseDatabase: b014c0068fa691dec5f4a868357e685df882cca5 FirebaseDatabaseUI: 6b629132ecc79e624b76f5b370921eea30c06503 - FirebaseEmailAuthUI: f81d35790f1647484af81fb8b96ee81bc3eb9fe7 - FirebaseFacebookAuthUI: 65d7e20aef4d9107fc3edf9274f144cde706fecd + FirebaseEmailAuthUI: 52589b1e800fa0f64983d6a5b7638fd24c3b5d7a + FirebaseFacebookAuthUI: 47c2e83010d9b2dacf55880ac7cd9d937c5cf35b FirebaseFirestore: e8528981558d50872e24119b9448b18e86cab9f6 FirebaseFirestoreInternal: 953e31e70db09d928b927b65761171c680d2ea9b FirebaseFirestoreUI: 5e367973b79caec884a4e16269c97f991ddaf5de - FirebaseGoogleAuthUI: 11ead69cc92ac3f86f94692d0e96cabe22f61121 + FirebaseGoogleAuthUI: c4dd51dbfa267af8d3eda87b35ffb01eb9b23062 FirebaseOAuthUI: 784f4f756adebbfaae892a873c0b98085b2f12d6 - FirebasePhoneAuthUI: 9f80bfb5fa883ea5a0a33ebad25f28c2ac8111e7 + FirebasePhoneAuthUI: d8266993e3ff28bc9daf423e29325993f951fecc FirebaseSharedSwift: a45efd84d60ebbfdcdbaebc66948af3630459e62 FirebaseStorage: d35da127dd49edcbd07b8c07cf651a70161558b2 FirebaseStorageUI: 646d8e65d699ba733c16b66e7fd2fae4d33b4b78 @@ -1539,4 +1539,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 2c24841f482dcea2f8eb8b3a4c3bfeda6423a0b3 -COCOAPODS: 1.16.2 +COCOAPODS: 1.15.2 diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..b1162abbf9 --- /dev/null +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample.xcodeproj/project.pbxproj @@ -0,0 +1,644 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 4607CC9C2D9BFE29009EC3F5 /* FirebaseAuthSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4607CC9B2D9BFE29009EC3F5 /* FirebaseAuthSwiftUI */; }; + 4607CC9E2D9BFE29009EC3F5 /* FirebaseGoogleSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4607CC9D2D9BFE29009EC3F5 /* FirebaseGoogleSwiftUI */; }; + 4670DEA72D9EA9E100E0D36A /* FirebaseFacebookSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4670DEA62D9EA9E100E0D36A /* FirebaseFacebookSwiftUI */; }; + 46CB7B252D773F2100F1FD0A /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 46CB7B242D773F2100F1FD0A /* GoogleService-Info.plist */; }; + 46F89C392D64B04E000F8BC0 /* FirebaseAuthSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 46F89C382D64B04E000F8BC0 /* FirebaseAuthSwiftUI */; }; + 46F89C4D2D64BB9B000F8BC0 /* FirebaseAuthSwiftUI in Frameworks */ = {isa = PBXBuildFile; productRef = 46F89C4C2D64BB9B000F8BC0 /* FirebaseAuthSwiftUI */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 46F89C1B2D64A86D000F8BC0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 46F89C002D64A86C000F8BC0 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 46F89C072D64A86C000F8BC0; + remoteInfo = FirebaseSwiftUIExample; + }; + 46F89C252D64A86D000F8BC0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 46F89C002D64A86C000F8BC0 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 46F89C072D64A86C000F8BC0; + remoteInfo = FirebaseSwiftUIExample; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 46CB7B242D773F2100F1FD0A /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 46F89C082D64A86C000F8BC0 /* FirebaseSwiftUIExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FirebaseSwiftUIExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 46F89C1A2D64A86D000F8BC0 /* FirebaseSwiftUIExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FirebaseSwiftUIExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 46F89C242D64A86D000F8BC0 /* FirebaseSwiftUIExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FirebaseSwiftUIExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 4607CC8A2D9BE8D9009EC3F5 /* Exceptions for "FirebaseSwiftUIExample" folder in "FirebaseSwiftUIExample" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 46F89C072D64A86C000F8BC0 /* FirebaseSwiftUIExample */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 46F89C0A2D64A86C000F8BC0 /* FirebaseSwiftUIExample */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 4607CC8A2D9BE8D9009EC3F5 /* Exceptions for "FirebaseSwiftUIExample" folder in "FirebaseSwiftUIExample" target */, + ); + path = FirebaseSwiftUIExample; + sourceTree = ""; + }; + 46F89C1D2D64A86D000F8BC0 /* FirebaseSwiftUIExampleTests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = FirebaseSwiftUIExampleTests; + sourceTree = ""; + }; + 46F89C272D64A86D000F8BC0 /* FirebaseSwiftUIExampleUITests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = FirebaseSwiftUIExampleUITests; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 46F89C052D64A86C000F8BC0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4670DEA72D9EA9E100E0D36A /* FirebaseFacebookSwiftUI in Frameworks */, + 46F89C392D64B04E000F8BC0 /* FirebaseAuthSwiftUI in Frameworks */, + 46F89C4D2D64BB9B000F8BC0 /* FirebaseAuthSwiftUI in Frameworks */, + 4607CC9E2D9BFE29009EC3F5 /* FirebaseGoogleSwiftUI in Frameworks */, + 4607CC9C2D9BFE29009EC3F5 /* FirebaseAuthSwiftUI in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 46F89C172D64A86D000F8BC0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 46F89C212D64A86D000F8BC0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 46A94CD12D7ECB4800F872B2 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 46F89BFF2D64A86C000F8BC0 = { + isa = PBXGroup; + children = ( + 46CB7B242D773F2100F1FD0A /* GoogleService-Info.plist */, + 46F89C0A2D64A86C000F8BC0 /* FirebaseSwiftUIExample */, + 46F89C1D2D64A86D000F8BC0 /* FirebaseSwiftUIExampleTests */, + 46F89C272D64A86D000F8BC0 /* FirebaseSwiftUIExampleUITests */, + 46A94CD12D7ECB4800F872B2 /* Frameworks */, + 46F89C092D64A86C000F8BC0 /* Products */, + ); + sourceTree = ""; + }; + 46F89C092D64A86C000F8BC0 /* Products */ = { + isa = PBXGroup; + children = ( + 46F89C082D64A86C000F8BC0 /* FirebaseSwiftUIExample.app */, + 46F89C1A2D64A86D000F8BC0 /* FirebaseSwiftUIExampleTests.xctest */, + 46F89C242D64A86D000F8BC0 /* FirebaseSwiftUIExampleUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 46F89C072D64A86C000F8BC0 /* FirebaseSwiftUIExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = 46F89C2E2D64A86D000F8BC0 /* Build configuration list for PBXNativeTarget "FirebaseSwiftUIExample" */; + buildPhases = ( + 46F89C042D64A86C000F8BC0 /* Sources */, + 46F89C052D64A86C000F8BC0 /* Frameworks */, + 46F89C062D64A86C000F8BC0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 46F89C0A2D64A86C000F8BC0 /* FirebaseSwiftUIExample */, + ); + name = FirebaseSwiftUIExample; + packageProductDependencies = ( + 46F89C382D64B04E000F8BC0 /* FirebaseAuthSwiftUI */, + 46F89C4C2D64BB9B000F8BC0 /* FirebaseAuthSwiftUI */, + 4607CC9B2D9BFE29009EC3F5 /* FirebaseAuthSwiftUI */, + 4607CC9D2D9BFE29009EC3F5 /* FirebaseGoogleSwiftUI */, + 4670DEA62D9EA9E100E0D36A /* FirebaseFacebookSwiftUI */, + ); + productName = FirebaseSwiftUIExample; + productReference = 46F89C082D64A86C000F8BC0 /* FirebaseSwiftUIExample.app */; + productType = "com.apple.product-type.application"; + }; + 46F89C192D64A86D000F8BC0 /* FirebaseSwiftUIExampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 46F89C312D64A86D000F8BC0 /* Build configuration list for PBXNativeTarget "FirebaseSwiftUIExampleTests" */; + buildPhases = ( + 46F89C162D64A86D000F8BC0 /* Sources */, + 46F89C172D64A86D000F8BC0 /* Frameworks */, + 46F89C182D64A86D000F8BC0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 46F89C1C2D64A86D000F8BC0 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 46F89C1D2D64A86D000F8BC0 /* FirebaseSwiftUIExampleTests */, + ); + name = FirebaseSwiftUIExampleTests; + packageProductDependencies = ( + ); + productName = FirebaseSwiftUIExampleTests; + productReference = 46F89C1A2D64A86D000F8BC0 /* FirebaseSwiftUIExampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 46F89C232D64A86D000F8BC0 /* FirebaseSwiftUIExampleUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 46F89C342D64A86D000F8BC0 /* Build configuration list for PBXNativeTarget "FirebaseSwiftUIExampleUITests" */; + buildPhases = ( + 46F89C202D64A86D000F8BC0 /* Sources */, + 46F89C212D64A86D000F8BC0 /* Frameworks */, + 46F89C222D64A86D000F8BC0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 46F89C262D64A86D000F8BC0 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 46F89C272D64A86D000F8BC0 /* FirebaseSwiftUIExampleUITests */, + ); + name = FirebaseSwiftUIExampleUITests; + packageProductDependencies = ( + ); + productName = FirebaseSwiftUIExampleUITests; + productReference = 46F89C242D64A86D000F8BC0 /* FirebaseSwiftUIExampleUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 46F89C002D64A86C000F8BC0 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1620; + LastUpgradeCheck = 1620; + TargetAttributes = { + 46F89C072D64A86C000F8BC0 = { + CreatedOnToolsVersion = 16.2; + }; + 46F89C192D64A86D000F8BC0 = { + CreatedOnToolsVersion = 16.2; + TestTargetID = 46F89C072D64A86C000F8BC0; + }; + 46F89C232D64A86D000F8BC0 = { + CreatedOnToolsVersion = 16.2; + TestTargetID = 46F89C072D64A86C000F8BC0; + }; + }; + }; + buildConfigurationList = 46F89C032D64A86C000F8BC0 /* Build configuration list for PBXProject "FirebaseSwiftUIExample" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 46F89BFF2D64A86C000F8BC0; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 4607CC9A2D9BFE29009EC3F5 /* XCLocalSwiftPackageReference "../../../../firebaseUI-ios" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 46F89C092D64A86C000F8BC0 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 46F89C072D64A86C000F8BC0 /* FirebaseSwiftUIExample */, + 46F89C192D64A86D000F8BC0 /* FirebaseSwiftUIExampleTests */, + 46F89C232D64A86D000F8BC0 /* FirebaseSwiftUIExampleUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 46F89C062D64A86C000F8BC0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 46CB7B252D773F2100F1FD0A /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 46F89C182D64A86D000F8BC0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 46F89C222D64A86D000F8BC0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 46F89C042D64A86C000F8BC0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 46F89C162D64A86D000F8BC0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 46F89C202D64A86D000F8BC0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 46F89C1C2D64A86D000F8BC0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 46F89C072D64A86C000F8BC0 /* FirebaseSwiftUIExample */; + targetProxy = 46F89C1B2D64A86D000F8BC0 /* PBXContainerItemProxy */; + }; + 46F89C262D64A86D000F8BC0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 46F89C072D64A86C000F8BC0 /* FirebaseSwiftUIExample */; + targetProxy = 46F89C252D64A86D000F8BC0 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 46F89C2C2D64A86D000F8BC0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 46F89C2D2D64A86D000F8BC0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 46F89C2F2D64A86D000F8BC0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"FirebaseSwiftUIExample/Preview Content\""; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = FirebaseSwiftUIExample/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firebase.auth.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 46F89C302D64A86D000F8BC0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"FirebaseSwiftUIExample/Preview Content\""; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = FirebaseSwiftUIExample/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 17.6; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.firebase.auth.example; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 46F89C322D64A86D000F8BC0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.invertase.testing.FirebaseSwiftUIExampleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FirebaseSwiftUIExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FirebaseSwiftUIExample"; + }; + name = Debug; + }; + 46F89C332D64A86D000F8BC0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.invertase.testing.FirebaseSwiftUIExampleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/FirebaseSwiftUIExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/FirebaseSwiftUIExample"; + }; + name = Release; + }; + 46F89C352D64A86D000F8BC0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.invertase.testing.FirebaseSwiftUIExampleUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = FirebaseSwiftUIExample; + }; + name = Debug; + }; + 46F89C362D64A86D000F8BC0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = YYX2P3XVJ7; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.invertase.testing.FirebaseSwiftUIExampleUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = FirebaseSwiftUIExample; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 46F89C032D64A86C000F8BC0 /* Build configuration list for PBXProject "FirebaseSwiftUIExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 46F89C2C2D64A86D000F8BC0 /* Debug */, + 46F89C2D2D64A86D000F8BC0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 46F89C2E2D64A86D000F8BC0 /* Build configuration list for PBXNativeTarget "FirebaseSwiftUIExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 46F89C2F2D64A86D000F8BC0 /* Debug */, + 46F89C302D64A86D000F8BC0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 46F89C312D64A86D000F8BC0 /* Build configuration list for PBXNativeTarget "FirebaseSwiftUIExampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 46F89C322D64A86D000F8BC0 /* Debug */, + 46F89C332D64A86D000F8BC0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 46F89C342D64A86D000F8BC0 /* Build configuration list for PBXNativeTarget "FirebaseSwiftUIExampleUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 46F89C352D64A86D000F8BC0 /* Debug */, + 46F89C362D64A86D000F8BC0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 4607CC9A2D9BFE29009EC3F5 /* XCLocalSwiftPackageReference "../../../../firebaseUI-ios" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "../../../../firebaseUI-ios"; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 4607CC9B2D9BFE29009EC3F5 /* FirebaseAuthSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = FirebaseAuthSwiftUI; + }; + 4607CC9D2D9BFE29009EC3F5 /* FirebaseGoogleSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = FirebaseGoogleSwiftUI; + }; + 4670DEA62D9EA9E100E0D36A /* FirebaseFacebookSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + package = 4607CC9A2D9BFE29009EC3F5 /* XCLocalSwiftPackageReference "../../../../firebaseUI-ios" */; + productName = FirebaseFacebookSwiftUI; + }; + 46F89C382D64B04E000F8BC0 /* FirebaseAuthSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = FirebaseAuthSwiftUI; + }; + 46F89C4C2D64BB9B000F8BC0 /* FirebaseAuthSwiftUI */ = { + isa = XCSwiftPackageProductDependency; + productName = FirebaseAuthSwiftUI; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 46F89C002D64A86C000F8BC0 /* Project object */; +} diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Assets.xcassets/AccentColor.colorset/Contents.json b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000..eb87897008 --- /dev/null +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..2305880107 --- /dev/null +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Assets.xcassets/Contents.json b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift new file mode 100644 index 0000000000..8a712be32c --- /dev/null +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/FirebaseSwiftUIExampleApp.swift @@ -0,0 +1,54 @@ +// +// FirebaseSwiftUIExampleApp.swift +// FirebaseSwiftUIExample +// +// Created by Russell Wheatley on 18/02/2025. +// + +import FirebaseAuth +import FirebaseAuthSwiftUI +import FirebaseCore +import FirebaseGoogleSwiftUI +import SwiftData +import SwiftUI + +let googleProvider = GoogleProviderSwift() + +class AppDelegate: NSObject, UIApplicationDelegate { + func application(_: UIApplication, + open url: URL, + options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { + return googleProvider.handleUrl(url) + } +} + +@main +struct FirebaseSwiftUIExampleApp: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + + init() { + FirebaseApp.configure() + } + + var body: some Scene { + WindowGroup { + NavigationView { + ContentView() + } + } + } +} + +struct ContentView: View { + let authService: AuthService + + init() { + authService = AuthService(googleProvider: googleProvider) + } + + var body: some View { + AuthPickerView { + GoogleButtonView() + }.environment(authService) + } +} diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Info.plist b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Info.plist new file mode 100644 index 0000000000..6a7dac1526 --- /dev/null +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Info.plist @@ -0,0 +1,17 @@ + + + + + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + com.googleusercontent.apps.406099696497-134k3722m01rtrsklhf3b7k8sqa5r7in + + + + + diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Preview Content/Preview Assets.xcassets/Contents.json b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExample/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleTests/FirebaseSwiftUIExampleTests.swift b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleTests/FirebaseSwiftUIExampleTests.swift new file mode 100644 index 0000000000..7d58ebed9c --- /dev/null +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleTests/FirebaseSwiftUIExampleTests.swift @@ -0,0 +1,15 @@ +// +// FirebaseSwiftUIExampleTests.swift +// FirebaseSwiftUIExampleTests +// +// Created by Russell Wheatley on 18/02/2025. +// + +@testable import FirebaseSwiftUIExample +import Testing + +struct FirebaseSwiftUIExampleTests { + @Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + } +} diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/FirebaseSwiftUIExampleUITests.swift b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/FirebaseSwiftUIExampleUITests.swift new file mode 100644 index 0000000000..299be7c8d5 --- /dev/null +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/FirebaseSwiftUIExampleUITests.swift @@ -0,0 +1,45 @@ +// +// FirebaseSwiftUIExampleUITests.swift +// FirebaseSwiftUIExampleUITests +// +// Created by Russell Wheatley on 18/02/2025. +// + +import XCTest + +final class FirebaseSwiftUIExampleUITests: XCTestCase { + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the + // class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - + // required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the + // class. + } + + @MainActor + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + @MainActor + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/FirebaseSwiftUIExampleUITestsLaunchTests.swift b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/FirebaseSwiftUIExampleUITestsLaunchTests.swift new file mode 100644 index 0000000000..4ed3df1bfa --- /dev/null +++ b/samples/swiftui/FirebaseSwiftUIExample/FirebaseSwiftUIExampleUITests/FirebaseSwiftUIExampleUITestsLaunchTests.swift @@ -0,0 +1,32 @@ +// +// FirebaseSwiftUIExampleUITestsLaunchTests.swift +// FirebaseSwiftUIExampleUITests +// +// Created by Russell Wheatley on 18/02/2025. +// + +import XCTest + +final class FirebaseSwiftUIExampleUITestsLaunchTests: XCTestCase { + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + @MainActor + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/samples/swiftui/FirebaseSwiftUIExample/GoogleService-Info.plist b/samples/swiftui/FirebaseSwiftUIExample/GoogleService-Info.plist new file mode 100644 index 0000000000..f325ead98d --- /dev/null +++ b/samples/swiftui/FirebaseSwiftUIExample/GoogleService-Info.plist @@ -0,0 +1,38 @@ + + + + + CLIENT_ID + 406099696497-134k3722m01rtrsklhf3b7k8sqa5r7in.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.406099696497-134k3722m01rtrsklhf3b7k8sqa5r7in + ANDROID_CLIENT_ID + 406099696497-17qn06u8a0dc717u8ul7s49ampk13lul.apps.googleusercontent.com + API_KEY + AIzaSyDooSUGSf63Ghq02_iIhtnmwMDs4HlWS6c + GCM_SENDER_ID + 406099696497 + PLIST_VERSION + 1 + BUNDLE_ID + io.flutter.plugins.firebase.auth.example + PROJECT_ID + flutterfire-e2e-tests + STORAGE_BUCKET + flutterfire-e2e-tests.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:406099696497:ios:58cbc26aca8e5cf83574d0 + DATABASE_URL + https://flutterfire-e2e-tests-default-rtdb.europe-west1.firebasedatabase.app + + \ No newline at end of file