Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ public struct AuthConfiguration {
let interactiveDismissEnabled: Bool
let shouldAutoUpgradeAnonymousUsers: Bool
let customStringsBundle: Bundle?
let tosUrl: URL
let privacyPolicyUrl: URL
let tosUrl: URL?
let privacyPolicyUrl: URL?
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: with the behavior in the view rendering an empty view if one but not both are set, we should document that both these properties need to be set in order to render a privacy and terms footer.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good catch, I've updated README here: 37903bd (#1254)

let emailLinkSignInActionCodeSettings: ActionCodeSettings?
let verifyEmailActionCodeSettings: ActionCodeSettings?

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")!,
tosUrl: URL? = nil,
privacyPolicyUrl: URL? = nil,
emailLinkSignInActionCodeSettings: ActionCodeSettings? = nil,
verifyEmailActionCodeSettings: ActionCodeSettings? = nil) {
self.shouldHideCancelButton = shouldHideCancelButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,12 +308,14 @@ public class StringUtils {
public var enterPhoneNumberLabel: String {
return localizedString(for: "Enter phone number")
}

/// Phone provider
/// found in:
/// - PhoneAuthButtonView
public var phoneNumberVerificationCodeLabel: String {
return localizedString(for: "Enter verification code")
}

/// Phone provider
/// found in:
/// - PhoneAuthButtonView
Expand All @@ -327,4 +329,25 @@ public class StringUtils {
public var verifyPhoneNumberAndSignInLabel: String {
return localizedString(for: "Verify phone number and sign-in")
}

/// Terms of Service label
/// found in:
/// - PrivacyTOCsView
public var termsOfServiceLabel: String {
return localizedString(for: "TermsOfService")
}

/// Terms of Service message
/// found in:
/// - PrivacyTOCsView
public var termsOfServiceMessage: String {
return localizedString(for: "TermsOfServiceMessage")
}

/// Privacy Policy
/// found in:
/// - PrivacyTOCsView
public var privacyPolicyLabel: String {
return localizedString(for: "PrivacyPolicy")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ extension AuthPickerView: View {
.foregroundColor(.blue)
}
}
PrivacyTOCsView(displayMode: .footer)
Text(authService.errorMessage).foregroundColor(.red)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// PrivacyTOCsView.swift
// FirebaseUI
//
// Created by Russell Wheatley on 12/05/2025.
//
import FirebaseCore
import SwiftUI

@MainActor
struct PrivacyTOCsView {
@Environment(AuthService.self) private var authService
enum DisplayMode {
case full, footer
}

let displayMode: DisplayMode

public init(displayMode: DisplayMode = .full) {
self.displayMode = displayMode
}

private func attributedMessage(tosURL: URL, privacyURL: URL) -> AttributedString {
let tosText = authService.string.termsOfServiceLabel
let privacyText = authService.string.privacyPolicyLabel

let format: String = displayMode == .full
? authService.string.termsOfServiceMessage
: "%@ %@"

let fullText = String(format: format, tosText, privacyText)

var attributed = AttributedString(fullText)

if let tosRange = attributed.range(of: tosText) {
attributed[tosRange].link = tosURL
attributed[tosRange].foregroundColor = .blue
}

if let privacyRange = attributed.range(of: privacyText) {
attributed[privacyRange].link = privacyURL
attributed[privacyRange].foregroundColor = .blue
}

return attributed
}
}

extension PrivacyTOCsView: View {
public var body: some View {
Group {
if let tosURL = authService.configuration.tosUrl,
let privacyURL = authService.configuration.privacyPolicyUrl {
Text(attributedMessage(tosURL: tosURL, privacyURL: privacyURL))
.multilineTextAlignment(displayMode == .full ? .leading : .trailing)
.font(.footnote)
.foregroundColor(.primary)
.padding()
} else {
EmptyView()
}
}
}
}

#Preview {
FirebaseOptions.dummyConfigurationForPreview()
let configuration = AuthConfiguration(
tosUrl: URL(string: "https://example.com/tos"),
privacyPolicyUrl: URL(string: "https://example.com/privacy")
)
let authService = AuthService(configuration: configuration)
return PrivacyTOCsView(displayMode: .footer)
.environment(authService)
}
10 changes: 6 additions & 4 deletions FirebaseSwiftUI/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ public struct AuthConfiguration {
// custom string bundle for string localizations
let customStringsBundle: Bundle?
// terms of service URL
let tosUrl: URL
let tosUrl: URL?
// privacy policy URL
let privacyPolicyUrl: URL
let privacyPolicyUrl: URL?
// action code settings for email sign in link
let emailLinkSignInActionCodeSettings: ActionCodeSettings?
// action code settings verifying email address
Expand All @@ -87,13 +87,15 @@ public struct AuthConfiguration {
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")!,
tosUrl: URL? = nil,
privacyPolicyUrl: URL? = nil,
emailLinkSignInActionCodeSettings: ActionCodeSettings? = nil,
verifyEmailActionCodeSettings: ActionCodeSettings? = nil)
}
```

> Note: Both `tosUrl` and `privacyPolicyUrl` have to be set for them to be rendered in the UI.

## Configuring providers

1. Ensure the provider is installed from step 1 (e.g. if configuring Google provider, you need to install `FirebaseGoogleSwiftUI` package).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ struct ContentView: View {
actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
let configuration = AuthConfiguration(
shouldAutoUpgradeAnonymousUsers: true,
tosUrl: URL(string: "https://example.com/tos"),
privacyPolicyUrl: URL(string: "https://example.com/privacy"),
emailLinkSignInActionCodeSettings: actionCodeSettings
)
authService = AuthService(
Expand Down