FirebaseUI ব্যবহার করে আপনার iOS অ্যাপে সাইন-ইন যুক্ত করুন

FirebaseUI for SwiftUI হলো Firebase Authentication উপর ভিত্তি করে নির্মিত একটি আধুনিক, SwiftUI-কেন্দ্রিক লাইব্রেরি যা আপনার অ্যাপের জন্য পূর্ব-নির্মিত সাইন-ইন ফ্লো প্রদান করে।

SwiftUI-এর জন্য FirebaseUI-এর নিম্নলিখিত সুবিধাগুলো রয়েছে:

  • স্বতন্ত্র ডিফল্ট UI : AuthPickerView ব্যবহার করে একটি সম্পূর্ণ সাইন-ইন প্রক্রিয়া যোগ করুন।
  • কাস্টমাইজযোগ্য : ডিফল্ট বাটনগুলো আপনার নিজস্ব লেআউটে রেন্ডার করুন, অথবা একটি সম্পূর্ণ কাস্টম অভিজ্ঞতা তৈরি করুন।
  • বেনামী অ্যাকাউন্ট সংযোগ : বেনামী ব্যবহারকারীদের প্রতিস্থাপন করার পরিবর্তে ঐচ্ছিকভাবে তাদের আপগ্রেড করুন।
  • অ্যাকাউন্ট ব্যবস্থাপনা : সাইন-আপ, পাসওয়ার্ড পুনরুদ্ধার এবং অ্যাকাউন্ট ব্যবস্থাপনার জন্য অন্তর্নির্মিত কার্যপ্রণালী।
  • একাধিক প্রদানকারী : ইমেল/পাসওয়ার্ড, ইমেল লিঙ্ক, ফোন প্রমাণীকরণ, অ্যাপল, গুগল, ফেসবুক, টুইটার, এবং স্ট্যান্ডার্ড OAuth2/OIDC প্রদানকারী।
  • আধুনিক প্রমাণীকরণ বৈশিষ্ট্য : মাল্টি-ফ্যাক্টর অথেন্টিকেশন (MFA) এবং async/await API-এর জন্য অন্তর্নির্মিত সমর্থন।

শুরু করার আগে

ইনস্টলেশন

FirebaseUI for SwiftUI একটি Swift প্যাকেজ হিসেবে সরবরাহ করা হয়। আপনার Xcode প্রোজেক্টে প্যাকেজটি ইনস্টল করতে, নিম্নলিখিতগুলি করুন:

  1. Xcode-এ, File > Add Package Dependencies- এ ক্লিক করুন।

  2. প্যাকেজ ইউআরএল লিখুন:

    https://github.com/firebase/FirebaseUI-iOS
    
  3. ডিপেন্ডেন্সি রুল মেনুতে, 'আপ টু নেক্সট মেজর ভার্সন' নির্বাচন করুন এবং সর্বনিম্ন ভার্সনটি সর্বশেষ রিলিজে সেট করুন।

  4. অ্যাড প্যাকেজ-এ ক্লিক করুন, তারপর আপনার প্রজেক্টে যোগ করার জন্য লাইব্রেরিগুলো নির্বাচন করুন।

    নিম্নলিখিত লাইব্রেরিটি সর্বদা প্রয়োজন। এতে মূল নির্ভরতাগুলির পাশাপাশি ইমেল-পাসওয়ার্ড সাইন-ইন এবং ইমেল লিঙ্ক সাইন-ইন এর সমর্থন অন্তর্ভুক্ত রয়েছে।

    • FirebaseAuthSwiftUI

    আপনি যদি অন্যান্য সাইন-ইন পদ্ধতিও সমর্থন করতে চান, তাহলে নিম্নলিখিত লাইব্রেরিগুলো থেকেও এক বা একাধিক নির্বাচন করুন:

    • FirebaseAppleSwiftUI (অ্যাপল দিয়ে সাইন ইন করুন)
    • FirebaseGoogleSwiftUI (গুগল দিয়ে সাইন ইন করুন)
    • FirebaseFacebookSwiftUI (ফেসবুক দিয়ে সাইন ইন করুন)
    • FirebasePhoneAuthSwiftUI (ফোন প্রমাণীকরণ)
    • FirebaseTwitterSwiftUI (টুইটার দিয়ে সাইন ইন করুন)
    • FirebaseOAuthSwiftUI (GitHub, Microsoft, Yahoo-এর মতো স্ট্যান্ডার্ড OAuth এবং OIDC প্রোভাইডারদের জন্য)
  5. নির্বাচিত লাইব্রেরিগুলো ইনস্টল করতে 'Add Package'-এ ক্লিক করুন।

  6. আপনার টপ-লেভেল View -এর ইনিশিয়ালাইজারে FirebaseUI-এর AuthService কনফিগার করুন এবং এনভায়রনমেন্ট ব্যবহার করে সার্ভিসটি চাইল্ড ভিউগুলোতে পাস করুন।

    import FirebaseAuthSwiftUI
    import SwiftUI
    
    struct ContentView: View {
      let authService: AuthService
    
      init() {
        let configuration = AuthConfiguration()
    
        authService = AuthService(configuration: configuration)
          .withEmailSignIn() // Or whatever sign-in methods you want to support.
                             // See the next section.
      }
    
      var body: some View {
        AuthPickerView { // AuthPickerView (the prebuilt View) or a custom View.
            Text("Welcome to your app!")
        }
        .environment(authService)
      }
    }
    

সাইন-ইন পদ্ধতি সেট আপ করুন

সমর্থিত প্রতিটি সাইন-ইন পদ্ধতির জন্য কিছু অতিরিক্ত সেটআপ ধাপের প্রয়োজন। নিচের বিভাগগুলো প্রসারিত করুন এবং স্বতন্ত্র প্রোভাইডারদের সেট আপ করার জন্য নির্দেশাবলী অনুসরণ করুন।

ইমেল ঠিকানা এবং পাসওয়ার্ড

  1. Firebase কনসোলের Security > Authentication > Sign-in method সেকশন থেকে Email/Password প্রোভাইডারটি সক্রিয় করুন।

  2. আপনার AuthService ইনস্ট্যান্সে প্রোভাইডারটি রেজিস্টার করুন:

    let authService = AuthService()
      .withEmailSignIn()
    

পাসওয়ার্ডবিহীন ইমেল লিঙ্কের মাধ্যমে সাইন-ইন করতে:

  1. Firebase কনসোলের Security > Authentication > Sign-in method সেকশন থেকে Email/Password চালু করুন, তারপর email link sign-in চালু করুন। মনে রাখবেন, email link sign-in ব্যবহার করার জন্য email অথবা password sign-in অবশ্যই চালু থাকতে হবে।

  2. লিঙ্ক ডোমেইনটিকে অনুমোদিত ডোমেইন তালিকায় যুক্ত করুন।

  3. ActionCodeSettings কনফিগার করুন, এটিকে AuthConfiguration এ পাস করুন এবং প্রোভাইডারটি রেজিস্টার করুন:

    let actionCodeSettings = ActionCodeSettings()
    actionCodeSettings.handleCodeInApp = true
    actionCodeSettings.url = URL(string: "https://yourapp.firebaseapp.com")
    
    guard let bundleID = Bundle.main.bundleIdentifier else {
      fatalError("Missing bundle identifier for email link authentication setup.")
    }
    actionCodeSettings.setIOSBundleID(bundleID)
    
    let configuration = AuthConfiguration(
      emailLinkSignInActionCodeSettings: actionCodeSettings
    )
    
    let authService = AuthService(configuration: configuration)
      .withEmailLinkSignIn()
    
  4. যে AppDelegate এ আপনি FirebaseApp.configure() কল করেন, সেই একই AppDelegate-এর application(_:open:options:) মেথডে এমন লজিক যোগ করুন যা খোলা URL-টি একটি Firebase Authentication সাইন-ইন লিঙ্ক হলে true রিটার্ন করে। এই লজিকটি নির্দেশ করে যে লিঙ্কটি FirebaseUI দ্বারা পরিচালিত হয়েছে এবং অন্য কোনো লিঙ্ক হ্যান্ডলার দ্বারা এটি প্রসেস করা উচিত নয়।

    import FacebookCore
    import FirebaseAuth
    import UIKit
    
    class AppDelegate: NSObject, UIApplicationDelegate {
      func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
      ) -> Bool {
        FirebaseApp.configure()
        return true
      }
    
      func application(
        _ application: UIApplication,
        open url: URL,
        options: [UIApplication.OpenURLOptionsKey: Any] = [:]
      ) -> Bool {
        if Auth.auth().canHandle(url) {
          return true
        }
        return false
      }
    }
    
  5. আপনি যদি কাস্টম ভিউ তৈরি করেন, তাহলে লিঙ্কটি আপনার অ্যাপ খোলার সময় authService.handleSignInLink(url:) কল করুন।

আপেল

অ্যাপল দিয়ে সাইন ইন ব্যবহার করতে:

  1. অ্যাপল দিয়ে সাইন ইন কনফিগার করুন:

    1. Apple-এর ডেভেলপার সাইটের Certificates, Identifiers & Profiles পেজে আপনার অ্যাপের জন্য Sign In with Apple সক্রিয় করুন।
    2. ‘ওয়েবের জন্য অ্যাপল দিয়ে সাইন ইন কনফিগার করুন’ -এর প্রথম বিভাগে বর্ণিত পদ্ধতি অনুযায়ী আপনার ওয়েবসাইটটিকে আপনার অ্যাপের সাথে সংযুক্ত করুন। অনুরোধ করা হলে, নিম্নলিখিত URL-টিকে একটি রিটার্ন URL হিসেবে নিবন্ধন করুন:
      https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
      আপনি আপনার Firebase কনসোল সেটিংস পৃষ্ঠা থেকে আপনার Firebase প্রজেক্ট আইডি পেতে পারেন। কাজ শেষ হলে, আপনার নতুন সার্ভিস আইডিটি লিখে রাখুন, যা আপনার পরবর্তী বিভাগে প্রয়োজন হবে।
    3. একটি 'সাইন ইন উইথ অ্যাপল' প্রাইভেট কী তৈরি করুন । পরবর্তী বিভাগে আপনার নতুন প্রাইভেট কী এবং কী আইডি প্রয়োজন হবে।
    4. আপনি যদি Firebase Authentication এর এমন কোনো ফিচার ব্যবহার করেন যা ব্যবহারকারীদের ইমেল পাঠায়, যেমন—ইমেল লিঙ্ক সাইন-ইন, ইমেল অ্যাড্রেস ভেরিফিকেশন, অ্যাকাউন্ট পরিবর্তনের প্রত্যাহার এবং অন্যান্য, তাহলে Apple প্রাইভেট ইমেল রিলে সার্ভিসটি কনফিগার করুন এবং noreply@ YOUR_FIREBASE_PROJECT_ID .firebaseapp.com (অথবা আপনার নিজস্ব ইমেল টেমপ্লেট ডোমেইন) রেজিস্টার করুন, যাতে Apple, Firebase Authentication দ্বারা পাঠানো ইমেলগুলো বেনামী Apple ইমেল অ্যাড্রেসে রিলে করতে পারে।
  2. Firebase কনসোলের Security > Authentication > Sign-in method সেকশন থেকে Apple সক্রিয় করুন।

    • পূর্ববর্তী বিভাগে আপনার তৈরি করা সার্ভিস আইডিটি উল্লেখ করুন।
    • OAuth কোড ফ্লো কনফিগারেশন বিভাগে , আপনার Apple Team ID এবং পূর্ববর্তী বিভাগে তৈরি করা প্রাইভেট কী ও কী আইডি উল্লেখ করুন।
  3. Xcode-এ, প্রজেক্ট এডিটরের Signing & Capabilities সেকশনটি খুলুন এবং Sign in with Apple ক্যাপাবিলিটিটি যোগ করুন।

  4. আপনার AuthService ইনস্ট্যান্সে প্রোভাইডারটি রেজিস্টার করুন:

    let authService = AuthService()
      .withAppleSignIn()
    

গুগল

গুগল সাইন-ইন ব্যবহার করতে:

  1. Firebase কনসোলের Security > Authentication > Sign-in method সেকশন থেকে Google প্রোভাইডারটি সক্রিয় করুন।

  2. আপনার প্রোজেক্টের GoogleService-Info.plist ফাইলের একটি নতুন কপি ডাউনলোড করে আপনার Xcode প্রোজেক্টে কপি করুন। বিদ্যমান সংস্করণগুলোকে নতুনটি দিয়ে ওভাররাইট করুন।

  3. আপনার Xcode প্রোজেক্টে কাস্টম URL স্কিম যোগ করুন:

    1. আপনার প্রোজেক্ট কনফিগারেশন খুলুন: বাম দিকের ট্রি ভিউতে প্রোজেক্টের নামে ক্লিক করুন। TARGETS সেকশন থেকে আপনার অ্যাপটি নির্বাচন করুন, তারপর Info ট্যাবটি নির্বাচন করুন এবং URL Types সেকশনটি এক্সপ্যান্ড করুন।

    2. + বোতামে ক্লিক করুন এবং আপনার রিভার্সড ক্লায়েন্ট আইডির জন্য একটি ইউআরএল স্কিম যোগ করুন। এই মানটি খুঁজে পেতে, GoogleService-Info.plist কনফিগারেশন ফাইলটি খুলুন এবং REVERSED_CLIENT_ID কী-টি খুঁজুন। সেই কী-টির মান কপি করে কনফিগারেশন পেজের ইউআরএল স্কিমস বক্সে পেস্ট করুন। অন্য ফিল্ডগুলো অপরিবর্তিত রাখুন।

      সম্পূর্ণ হলে, আপনার কনফিগারেশনটি নিচের মতো দেখতে হবে (তবে এতে আপনার অ্যাপ্লিকেশনের জন্য নির্দিষ্ট মানগুলো থাকবে):

  4. আপনার AuthService ইনস্ট্যান্সে প্রোভাইডারটি রেজিস্টার করুন:

    let authService = AuthService()
      .withGoogleSignIn()
    

ফেসবুক

ফেসবুক লগইন ব্যবহার করতে:

  1. Meta for Developers সাইটের নির্দেশাবলী অনুসরণ করে Facebook Login for iOS SDK সেট আপ করুন। শেষ ধাপ, "Add Facebook Login to Your Code" এড়িয়ে যান।

  2. Firebase কনসোলের Security > Authentication > Sign-in method সেকশন থেকে Facebook প্রোভাইডারটি এনাবল করুন। এর জন্য আপনার Meta for Developers সাইট থেকে Facebook App ID এবং App Secret প্রয়োজন হবে।

  3. আপনার AuthService ইনস্ট্যান্সে প্রোভাইডারটি রেজিস্টার করুন:

    let authService = AuthService()
     .withFacebookSignIn()
    

ফোন নম্বর

ফোন প্রমাণীকরণ ব্যবহার করতে:

  1. Firebase কনসোলের Security > Authentication > Sign-in method সেকশন থেকে Phone provider-টি সক্রিয় করুন।

  2. 'সাইলেন্ট নোটিফিকেশন গ্রহণ শুরু করুন' বিভাগে দেওয়া নির্দেশনা অনুযায়ী আপনার অ্যাপের জন্য APN কনফিগার করুন।

  3. আপনার Xcode প্রোজেক্টে কাস্টম URL স্কিম যোগ করুন:

    1. আপনার প্রোজেক্ট কনফিগারেশন খুলুন: বাম দিকের ট্রি ভিউতে প্রোজেক্টের নামে ক্লিক করুন। TARGETS সেকশন থেকে আপনার অ্যাপটি নির্বাচন করুন, তারপর Info ট্যাবটি নির্বাচন করুন এবং URL Types সেকশনটি এক্সপ্যান্ড করুন।

    2. + বোতামে ক্লিক করুন এবং আপনার এনকোডেড অ্যাপ আইডি একটি ইউআরএল স্কিম হিসেবে যোগ করুন। এই মানটি খুঁজে পেতে, Firebase কনসোলে সেটিংস > সাধারণ খুলুন।

      সম্পূর্ণ হলে, আপনার কনফিগারেশনটি নিচের মতো দেখতে হবে (তবে এতে আপনার অ্যাপ্লিকেশনের জন্য নির্দিষ্ট মানগুলো থাকবে):

  4. যে AppDelegate-এ আপনি FirebaseApp.configure() কল করেন, সেই একই AppDelegate APNs টোকেন হ্যান্ডলার যোগ করুন:

    import FacebookCore
    import FirebaseAuth
    import UIKit
    
    class AppDelegate: NSObject, UIApplicationDelegate {
      func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
      ) -> Bool {
        FirebaseApp.configure()
        return true
      }
    
      func application(
        _ application: UIApplication,
        didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
      ) {
        #if DEBUG
        Auth.auth().setAPNSToken(deviceToken, type: .sandbox)
        #else
        Auth.auth().setAPNSToken(deviceToken, type: .prod)
        #endif
      }
    }
    
  5. আপনার AuthService ইনস্ট্যান্সে প্রোভাইডারটি রেজিস্টার করুন:

    let authService = AuthService()
      .withPhoneSignIn()
    

টুইটার (X)

টুইটার লগইন ব্যবহার করতে:

  1. X ডেভেলপারস সাইটের নির্দেশাবলী অনুসরণ করে X API ক্রেডেনশিয়াল তৈরি করুন।

  2. Firebase কনসোলের Security > Authentication > Sign-in method সেকশন থেকে Twitter প্রোভাইডারটি সক্রিয় করুন। এর জন্য আপনার X Developer কনসোল থেকে API key এবং API secret প্রয়োজন হবে।

  3. আপনার AuthService ইনস্ট্যান্সে প্রোভাইডারটি রেজিস্টার করুন:

    let authService = AuthService()
      .withTwitterSignIn()
    

স্ট্যান্ডার্ড OAuth2 এবং OIDC প্রদানকারী

FirebaseUI, GitHub, Microsoft, এবং Yahoo-এর মতো বিল্ট-ইন OAuth প্রোভাইডারদের পাশাপাশি Firebase Authentication-এ কনফিগার করা কাস্টম OIDC প্রোভাইডারদেরও সাপোর্ট করে।

 let authService = AuthService()
  .withOAuthSignIn(OAuthProviderSwift.github())
  .withOAuthSignIn(OAuthProviderSwift.microsoft())
  .withOAuthSignIn(OAuthProviderSwift.yahoo())

কাস্টম OIDC প্রোভাইডারদের জন্য, প্রথমে Firebase Authentication-এ প্রোভাইডারটি কনফিগার করুন, তারপর আপনার প্রোভাইডার আইডি এবং বাটন কনফিগারেশন দিয়ে একটি OAuthProviderSwift তৈরি করুন:

let lineProvider = OAuthProviderSwift(
  providerId: "oidc.line",
  buttonLabel: "Sign in with LINE",
  displayName: "LINE",
  iconSystemName: "person.crop.circle.badge.checkmark",
  buttonBackgroundColor: .green,
  buttonForegroundColor: .white
)

let authService = AuthService()
  .withOAuthSignIn(lineProvider)

পূর্ব-নির্মিত প্রমাণীকরণ ভিউ ব্যবহার করে

SwiftUI-এর জন্য FirebaseUI, AuthPickerView প্রদান করে, যা একটি পূর্ব-নির্মিত ও সুনির্দিষ্ট অথেনটিকেশন UI এবং এটি আপনার জন্য সম্পূর্ণ অথেনটিকেশন প্রক্রিয়াটি পরিচালনা করে। আপনার অ্যাপে অথেনটিকেশন যুক্ত করার এটিই সবচেয়ে সহজ উপায়।

উদাহরণ

একাধিক প্রোভাইডার এবং কনফিগারেশন অপশন সহ AuthPickerView ব্যবহারের একটি উদাহরণ এখানে দেওয়া হলো:

import FirebaseAppleSwiftUI
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import SwiftUI

struct ContentView: View {
  let authService: AuthService

  init() {
    // Create configuration with options
    let configuration = AuthConfiguration(
      tosUrl: URL(string: "https://example.com/tos"),
      privacyPolicyUrl: URL(string: "https://example.com/privacy"),
      shouldAutoUpgradeAnonymousUsers: true
    )

    // Initialize AuthService with multiple providers
    authService = AuthService(configuration: configuration)
      .withEmailSignIn()
      .withAppleSignIn()
      .withGoogleSignIn()
  }

  var body: some View {
    AuthPickerView {
      authenticatedContent
    }
    .environment(authService)
  }

  var authenticatedContent: some View {
    NavigationStack {
      VStack(spacing: 20) {
        if authService.authenticationState == .authenticated {
          Text("Authenticated")
          
          Button("Manage Account") {
            authService.isPresented = true
          }
          .buttonStyle(.bordered)
          
          Button("Sign Out") {
            Task {
              try? await authService.signOut()
            }
          }
          .buttonStyle(.borderedProminent)
        } else {
          Text("Not Authenticated")
          
          Button("Sign In") {
            authService.isPresented = true
          }
          .buttonStyle(.borderedProminent)
        }
      }
      .navigationTitle("My App")
    }
    .onChange(of: authService.authenticationState) { _, newValue in
      // Automatically show auth UI when not authenticated
      if newValue != .authenticating {
        authService.isPresented = (newValue == .unauthenticated)
      }
    }
  }
}

সাধারণ কাস্টমাইজেশন

যদিও AuthConfiguration এর সমস্ত প্যারামিটার ঐচ্ছিক, বেশিরভাগ অ্যাপ ন্যূনতম নিম্নলিখিত সেটিংসগুলো কাস্টমাইজ করে থাকে:

let configuration = AuthConfiguration(
    logo: ImageResource.exampleLogoAsset,
    customStringsBundle: .main,
    tosUrl: URL(string: "https://example.com/tos"),
    privacyPolicyUrl: URL(string: "https://example.com/privacy"),
)
  • logo : প্রমাণীকরণ পত্রে প্রদর্শিত লোগো চিত্র। আপনার নিজস্ব চিত্র সম্পদ অন্তর্ভুক্ত করার তথ্যের জন্য, আপনার Xcode প্রকল্পে চিত্র যুক্ত করা দেখুন।

  • customStringsBundle : নির্দিষ্ট বান্ডেল থেকে কাস্টম স্ট্রিং ব্যবহার করুন। এটি লোকালইজেশনের জন্য ব্যবহৃত হয়, তবে AuthPickerView দ্বারা ব্যবহৃত ডিফল্ট স্ট্রিংগুলো কাস্টমাইজ করার জন্যও এটি ব্যবহার করা যায়। উদাহরণস্বরূপ, অথেনটিকেশন শীটের শীর্ষে প্রদর্শিত বার্তা সেট করতে, নিম্নলিখিত বিষয়বস্তু সহ Localizable.strings তৈরি করুন:

    "Sign in with Firebase" = "Sign in to use ExampleApp";
    

প্রি-বিল্ট ভিউতে কী কী অন্তর্ভুক্ত আছে

যখন আপনি AuthPickerView ব্যবহার করেন, তখন আপনি যা পান তা হলো:

  1. শীট উপস্থাপনা : প্রমাণীকরণ UI একটি মোডাল শীট হিসাবে প্রদর্শিত হয়।
  2. অন্তর্নির্মিত নেভিগেশন : সাইন-ইন, পাসওয়ার্ড পুনরুদ্ধার, এমএফএ, ইমেল লিঙ্ক এবং ফোন যাচাইকরণ স্ক্রিনগুলির মধ্যে স্বয়ংক্রিয় নেভিগেশন
  3. প্রমাণীকরণ অবস্থা ব্যবস্থাপনা : authService.authenticationState এর উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে প্রমাণীকরণ UI এবং আপনার কন্টেন্টের মধ্যে পরিবর্তন করে।
  4. isPresented এর মাধ্যমে নিয়ন্ত্রণ : authService.isPresented = true/false সেট করে অথোরাইজেশন শীটটি কখন প্রদর্শিত হবে তা নিয়ন্ত্রণ করুন।

মতামতপূর্ণ আচরণ

ডিফল্ট AuthPickerView বেশ কিছু জটিল পরিস্থিতি কীভাবে সামলাতে হবে সে বিষয়ে নিজস্ব মতামত পোষণ করে:

১. অ্যাকাউন্ট বিরোধ নিষ্পত্তি

যখন কোনো অ্যাকাউন্ট সংক্রান্ত দ্বন্দ্ব দেখা দেয় (যেমন, এমন কোনো ক্রেডেনশিয়াল দিয়ে সাইন ইন করা যা ইতিমধ্যেই অন্য কোনো অ্যাকাউন্টের সাথে লিঙ্ক করা আছে), তখন AuthPickerView স্বয়ংক্রিয়ভাবে এর সমাধান করে:

  • অ্যানোনিমাস আপগ্রেড সংক্রান্ত দ্বন্দ্ব : যদি shouldAutoUpgradeAnonymousUsers সক্রিয় থাকে এবং অ্যানোনিমাস আপগ্রেডের সময় কোনো দ্বন্দ্ব দেখা দেয়, তাহলে সিস্টেম স্বয়ংক্রিয়ভাবে অ্যানোনিমাস ব্যবহারকারীকে সাইন আউট করে দেয় এবং নতুন ক্রেডেনশিয়াল দিয়ে সাইন ইন করে।
  • অন্যান্য দ্বন্দ্ব : পরিচয় প্রকাশকারী অ্যাকাউন্টগুলোর মধ্যে ক্রেডেনশিয়াল দ্বন্দ্বের ক্ষেত্রে, সিস্টেমটি অপেক্ষাধীন ক্রেডেনশিয়ালটি সংরক্ষণ করে এবং সফলভাবে সাইন-ইন করার পর সেটিকে সংযুক্ত করার চেষ্টা করে।

এটি NavigationStack স্তরে প্রয়োগ করা AccountConflictModifier দ্বারা পরিচালিত হয়।

২. মাল্টি-ফ্যাক্টর অথেন্টিকেশন (MFA)

যখন আপনার কনফিগারেশনে MFA সক্রিয় করা থাকে:

  • সাইন ইন করার সময় কখন এমএফএ (MFA) প্রয়োজন তা স্বয়ংক্রিয়ভাবে শনাক্ত করে।
  • উপযুক্ত MFA রেজোলিউশন স্ক্রিন (SMS বা TOTP) উপস্থাপন করে।
  • এমএফএ তালিকাভুক্তি এবং ব্যবস্থাপনা কার্যক্রম পরিচালনা করে
  • এসএমএস-ভিত্তিক এবং সময়-ভিত্তিক ওয়ান-টাইম পাসওয়ার্ড (TOTP) উভয় পদ্ধতিই সমর্থন করে।
৩. ত্রুটি পরিচালনা

ডিফল্ট ভিউগুলোতে অন্তর্নির্মিত ত্রুটি পরিচালনা ব্যবস্থা অন্তর্ভুক্ত রয়েছে:

  • অ্যালার্ট ডায়ালগে ব্যবহারকারী-বান্ধব ত্রুটি বার্তা প্রদর্শন করে।
  • অভ্যন্তরীণভাবে পরিচালিত ত্রুটিগুলি (যেমন, বাতিলকরণ ত্রুটি, স্বয়ংক্রিয়ভাবে পরিচালিত দ্বন্দ্ব) স্বয়ংক্রিয়ভাবে ফিল্টার করে বাদ দেয়।
  • StringUtils এর মাধ্যমে স্থানীয় ত্রুটি বার্তা ব্যবহার করে
  • reportError এনভায়রনমেন্ট কী-এর মাধ্যমে ত্রুটিগুলো প্রচারিত হয়।

যখন ইমেল লিঙ্ক সাইন-ইন কনফিগার করা থাকে:

  • অ্যাপ স্টোরেজে ইমেল ঠিকানা স্বয়ংক্রিয়ভাবে সংরক্ষণ করে।
  • ইমেইল থেকে ডিপ লিঙ্ক নেভিগেশন পরিচালনা করে
  • সম্পূর্ণ ইমেল যাচাইকরণ প্রক্রিয়াটি পরিচালনা করে।
  • ইমেল লিঙ্কের মাধ্যমে বেনামী ব্যবহারকারীদের আপগ্রেড সমর্থন করে।
৫. বেনামী ব্যবহারকারীর স্বয়ংক্রিয় আপগ্রেড

কখন shouldAutoUpgradeAnonymousUsers সক্রিয় করা হয়:

  • নতুন সাইন-ইন ক্রেডেনশিয়ালের সাথে বেনামী অ্যাকাউন্টগুলি স্বয়ংক্রিয়ভাবে লিঙ্ক করার চেষ্টা করে।
  • বেনামী সেশন প্রতিস্থাপন না করে আপগ্রেড করার মাধ্যমে ব্যবহারকারীর ডেটা সুরক্ষিত রাখে।
  • আপগ্রেড সংক্রান্ত দ্বন্দ্বগুলি সুন্দরভাবে সামাল দেয়
৬. ডিফল্ট ভিউতে পুনঃপ্রমাণীকরণ

অ্যাকাউন্ট মুছে ফেলা, পাসওয়ার্ড আপডেট করা বা এমএফএ ফ্যাক্টর বাতিল করার মতো সংবেদনশীল ক্রিয়াকলাপগুলির জন্য সাম্প্রতিক প্রমাণীকরণ প্রয়োজন। ডিফল্ট ভিউ ব্যবহার করার সময়, ব্যবহারকারীর সাইন-ইন প্রদানকারীর উপর ভিত্তি করে পুনঃপ্রমাণীকরণ স্বয়ংক্রিয়ভাবে সম্পন্ন হয়।

যখন কোনো সংবেদনশীল অপারেশনের জন্য পুনঃপ্রমাণীকরণের প্রয়োজন হয়, তখন ডিফল্ট ভিউগুলো স্বয়ংক্রিয়ভাবে:

  • OAuth প্রোভাইডার (গুগল, অ্যাপল, ফেসবুক, টুইটার, ইত্যাদি) : ব্যবহারকারীকে নিশ্চিত করতে বলে একটি অ্যালার্ট প্রদর্শন করে, তারপর স্বয়ংক্রিয়ভাবে নতুন ক্রেডেনশিয়াল সংগ্রহ করে অপারেশনটি সম্পন্ন করে।

  • ইমেল/পাসওয়ার্ড : ব্যবহারকারীকে এগিয়ে যাওয়ার আগে তার পাসওয়ার্ড প্রবেশ করানোর জন্য একটি শীট দেখানো হবে।

  • ইমেল লিঙ্ক : একটি যাচাইকরণ ইমেল পাঠানোর জন্য অনুরোধ জানিয়ে একটি সতর্কবার্তা দেখান, তারপর ইমেল চেক করার নির্দেশনাসহ একটি শীট উপস্থাপন করুন। ব্যবহারকারী পুনঃপ্রমাণীকরণ সম্পন্ন করতে তাদের ইমেলের লিঙ্কে ট্যাপ করেন।

  • ফোন : যাচাইকরণের প্রয়োজন জানিয়ে একটি সতর্কবার্তা দেখান, তারপর এসএমএস কোড যাচাইকরণের জন্য একটি শীট উপস্থাপন করুন।

সফলভাবে পুনঃপ্রমাণীকরণের পর অপারেশনটি স্বয়ংক্রিয়ভাবে পুনরায় চেষ্টা করে। AuthPickerView অথবা বিল্ট-ইন অ্যাকাউন্ট ম্যানেজমেন্ট ভিউ (যেমন UpdatePasswordView , SignedInView ইত্যাদি) ব্যবহার করার ক্ষেত্রে কোনো অতিরিক্ত কোডের প্রয়োজন হয় না।

উন্নত: কাস্টম অথেন্টিকেশন ভিউ তৈরি করা

যদি আপনার UI বা নেভিগেশন ফ্লো-এর উপর আরও বেশি নিয়ন্ত্রণের প্রয়োজন হয়, তাহলে আপনি অথেনটিকেশন লজিকের জন্য AuthService ব্যবহার করার পাশাপাশি নিজের কাস্টম অথেনটিকেশন ভিউ তৈরি করতে পারেন।

আপনি ফায়ারবেসইউআই (FirebaseUI)-এর উপাদানগুলোকে নিজস্ব লজিকের সাথে বিভিন্ন উপায়ে মেশাতে পারেন। নিম্নলিখিত বিভাগগুলিতে আপনি ব্যবহার করতে পারেন এমন কিছু কাস্টমাইজেশন পদ্ধতির উদাহরণ রয়েছে।

পদ্ধতি ১: registerProvider() ব্যবহার করে কাস্টম বাটন

বাটনের চেহারার উপর সম্পূর্ণ নিয়ন্ত্রণ পেতে, আপনি আপনার নিজস্ব কাস্টম AuthProviderUI ইমপ্লিমেন্টেশন তৈরি করতে পারেন, যা যেকোনো প্রোভাইডারকে র‍্যাপ করে এবং আপনার কাস্টম বাটন ভিউ রিটার্ন করে।

একটি কাস্টম প্রোভাইডার UI তৈরি করা

উদাহরণস্বরূপ, কীভাবে একটি কাস্টম টুইটার বাটন তৈরি করতে হয় তা এখানে দেওয়া হলো:

import FirebaseAuthSwiftUI
import FirebaseTwitterSwiftUI
import SwiftUI

// Step 1: Create your custom button view
struct CustomTwitterButton: View {
  let provider: TwitterProviderSwift
  @Environment(AuthService.self) private var authService
  @Environment(\.mfaHandler) private var mfaHandler

  var body: some View {
    Button {
      Task {
        do {
          let outcome = try await authService.signIn(provider)

          // Handle MFA if required
          if case let .mfaRequired(mfaInfo) = outcome,
             let onMFA = mfaHandler {
            onMFA(mfaInfo)
          }
        } catch {
          // Do Something Else
        }
      }
    } label: {
      HStack { // Your custom icon
        Text("Sign in with Twitter")
          .fontWeight(.semibold)
      }
      .frame(maxWidth: .infinity)
      .padding()
      .background(
        LinearGradient(
          colors: [Color.blue, Color.cyan],
          startPoint: .leading,
          endPoint: .trailing
        )
      )
      .foregroundColor(.white)
      .cornerRadius(12)
      .shadow(radius: 4)
    }
  }
}

// Step 2: Create a custom AuthProviderUI wrapper
class CustomTwitterProviderAuthUI: AuthProviderUI {
  private let typedProvider: TwitterProviderSwift
  var provider: AuthProviderSwift { typedProvider }
  let id: String = "twitter.com"

  init(provider: TwitterProviderSwift = TwitterProviderSwift()) {
    typedProvider = provider
  }

  @MainActor func authButton() -> AnyView {
    AnyView(CustomTwitterButton(provider: typedProvider))
  }
}

// Step 3: Use it in your app
struct ContentView: View {
  let authService: AuthService

  init() {
    let configuration = AuthConfiguration()
    authService = AuthService(configuration: configuration)

    // Register your custom provider UI
    authService.registerProvider(
      providerWithButton: CustomTwitterProviderAuthUI()
    )
    authService.isPresented = true
  }

  var body: some View {
    AuthPickerView {
      usersApp
    }
    .environment(authService)
  }

  var usersApp: some View {
    NavigationStack {
      VStack {
        Button {
          authService.isPresented = true
        } label: {
          Text("Authenticate")
        }
      }
    }
  }
}

সরলীকৃত কাস্টম বাটনের উদাহরণ

আপনি যেকোনো প্রোভাইডারের জন্য আরও সহজ কাস্টম বাটনও তৈরি করতে পারেন:

import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import FirebaseAppleSwiftUI
import SwiftUI

// Custom Google Provider UI
class CustomGoogleProviderAuthUI: AuthProviderUI {
  private let typedProvider: GoogleProviderSwift
  var provider: AuthProviderSwift { typedProvider }
  let id: String = "google.com"
  
  init() {
    typedProvider = GoogleProviderSwift()
  }
  
  @MainActor func authButton() -> AnyView {
    AnyView(CustomGoogleButton(provider: typedProvider))
  }
}

struct CustomGoogleButton: View {
  let provider: GoogleProviderSwift
  @Environment(AuthService.self) private var authService
  
  var body: some View {
    Button {
      Task {
        try? await authService.signIn(provider)
      }
    } label: {
      HStack {
        Image(systemName: "g.circle.fill")
        Text("My Custom Google Button")
      }
      .frame(maxWidth: .infinity)
      .padding()
      .background(Color.purple) // Your custom color
      .foregroundColor(.white)
      .cornerRadius(10)
    }
  }
}

// Then use it
struct ContentView: View {
  let authService: AuthService
  
  init() {
    let configuration = AuthConfiguration()
    authService = AuthService(configuration: configuration)
      .withAppleSignIn() // Use default Apple button
    
    // Use custom Google button
    authService.registerProvider(
      providerWithButton: CustomGoogleProviderAuthUI()
    )
  }
  
  var body: some View {
    AuthPickerView {
      Text("App Content")
    }
    .environment(authService)
  }
}

এই পদ্ধতিটি গুগল, অ্যাপল, টুইটার, ফেসবুক, ফোন এবং OAuth প্রোভাইডারসহ সকল প্রোভাইডারের জন্য কাজ করে। শুধু আপনার নিজস্ব বাটন ভিউ তৈরি করুন এবং সেটিকে AuthProviderUI সাথে সঙ্গতিপূর্ণ একটি ক্লাসের মধ্যে রাখুন।

পদ্ধতি ২: কাস্টম ভিউ সহ ডিফল্ট বাটন

আপনি AuthService.renderButtons() ব্যবহার করে AuthPickerView বাইপাস করতে পারেন এবং একই সাথে আপনার নিজস্ব লেআউট ও নেভিগেশন প্রদান করে ডিফল্ট অথেনটিকেশন বাটনগুলো রেন্ডার করতে পারেন:

import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import FirebaseAppleSwiftUI
import SwiftUI

struct CustomAuthView: View {
  @Environment(AuthService.self) private var authService

  var body: some View {
    VStack(spacing: 30) {
      // Your custom logo/branding
      Image("app-logo")
        .resizable()
        .frame(width: 150, height: 150)
      
      Text("Welcome to My App")
        .font(.largeTitle)
        .fontWeight(.bold)
      
      Text("Sign in to continue")
        .font(.subheadline)
        .foregroundStyle(.secondary)
      
      // Render default auth buttons
      authService.renderButtons(spacing: 12)
        .padding()
    }
    .padding()
  }
}

struct ContentView: View {
  init() {
    let configuration = AuthConfiguration()
    
    authService = AuthService(configuration: configuration)
      .withGoogleSignIn()
      .withAppleSignIn()
  }
  
  let authService: AuthService

  var body: some View {
    NavigationStack {
      if authService.authenticationState == .authenticated {
        Text("Authenticated!")
      } else {
        CustomAuthView()
      }
    }
    .environment(authService)
  }
}

পদ্ধতি ৩: কাস্টম নেভিগেশন সহ কাস্টম ভিউ

সম্পূর্ণ কার্যপ্রবাহের উপর পূর্ণ নিয়ন্ত্রণ পেতে, আপনি AuthPickerView বাইপাস করে আপনার নিজস্ব নেভিগেশন সিস্টেম তৈরি করতে পারেন:

import FirebaseAuth
import FirebaseAuthSwiftUI
import FirebaseGoogleSwiftUI
import SwiftUI

enum CustomAuthRoute {
  case signIn
  case phoneVerification
  case mfaResolution
}

struct ContentView: View {
  private let authService: AuthService
  @State private var navigationPath: [CustomAuthRoute] = []
  @State private var errorMessage: String?

  init() {
    let configuration = AuthConfiguration()
    self.authService = AuthService(configuration: configuration)
      .withGoogleSignIn()
      .withPhoneSignIn()
  }

  var body: some View {
    NavigationStack(path: $navigationPath) {
      Group {
        if authService.authenticationState == .authenticated {
          authenticatedView
        } else {
          customSignInView
        }
      }
      .navigationDestination(for: CustomAuthRoute.self) { route in
        switch route {
        case .signIn:
          customSignInView
        case .phoneVerification:
          customPhoneVerificationView
        case .mfaResolution:
          customMFAView
        }
      }
    }
    .environment(authService)
    .alert("Error", isPresented: .constant(errorMessage != nil)) {
      Button("OK") {
        errorMessage = nil
      }
    } message: {
      Text(errorMessage ?? "")
    }
  }

  var customSignInView: some View {
    VStack(spacing: 20) {
      Text("Custom Sign In")
        .font(.title)
      
      Button("Sign in with Google") {
        Task {
          do {
            let provider = GoogleProviderSwift(clientID: Auth.auth().app?.options.clientID ?? "")
            let outcome = try await authService.signIn(provider)
            
            // Handle MFA if required
            if case .mfaRequired = outcome {
              navigationPath.append(.mfaResolution)
            }
          } catch {
            errorMessage = error.localizedDescription
          }
        }
      }
      .buttonStyle(.borderedProminent)
      
      Button("Phone Sign In") {
        navigationPath.append(.phoneVerification)
      }
      .buttonStyle(.bordered)
    }
    .padding()
  }

  var customPhoneVerificationView: some View {
    Text("Custom Phone Verification View")
    // Implement your custom phone auth UI here
  }

  var customMFAView: some View {
    Text("Custom MFA Resolution View")
    // Implement your custom MFA UI here
  }

  var authenticatedView: some View {
    VStack(spacing: 20) {
      Text("Welcome!")
      Text("Email: \(authService.currentUser?.email ?? "N/A")")
      
      Button("Sign Out") {
        Task {
          try? await authService.signOut()
        }
      }
      .buttonStyle(.borderedProminent)
    }
  }
}

কাস্টম ভিউয়ের জন্য গুরুত্বপূর্ণ বিবেচ্য বিষয়সমূহ

কাস্টম ভিউ তৈরি করার সময়, আপনাকে এমন বেশ কিছু বিষয় নিজে থেকেই সামলাতে হয় যা AuthPickerView স্বয়ংক্রিয়ভাবে করে থাকে:

  1. অ্যাকাউন্ট দ্বন্দ্ব : AuthServiceError.accountConflict ব্যবহার করে আপনার নিজস্ব দ্বন্দ্ব নিরসন কৌশল প্রয়োগ করুন।
  2. এমএফএ হ্যান্ডলিং : .mfaRequired এর জন্য SignInOutcome চেক করুন এবং এমএফএ রেজোলিউশন ম্যানুয়ালি পরিচালনা করুন।
  3. বেনামী ব্যবহারকারীর আপগ্রেড : যদি shouldAutoUpgradeAnonymousUsers সক্রিয় থাকে, তাহলে বেনামী অ্যাকাউন্টগুলির লিঙ্কিং পরিচালনা করুন।
  4. নেভিগেশন অবস্থা : বিভিন্ন প্রমাণীকরণ স্ক্রিনের (ফোন যাচাইকরণ, পাসওয়ার্ড পুনরুদ্ধার, ইত্যাদি) মধ্যে নেভিগেশন পরিচালনা করুন।
  5. লোডিং অবস্থা : অ্যাসিঙ্ক অথেন্টিকেশন অপারেশন চলাকালীন authService.authenticationState পর্যবেক্ষণ করে লোডিং সূচক দেখান।
  6. পুনঃপ্রমাণীকরণ : সংবেদনশীল অপারেশনগুলির জন্য পুনঃপ্রমাণীকরণ ত্রুটিগুলি পরিচালনা করুন (নীচে কাস্টম ভিউতে পুনঃপ্রমাণীকরণ দেখুন)

কাস্টম ভিউতে পুনঃপ্রমাণীকরণ

কাস্টম ভিউ তৈরি করার সময়, নির্দিষ্ট ত্রুটি শনাক্ত করে এবং আপনার নিজস্ব ফ্লো প্রয়োগ করে পুনঃপ্রমাণীকরণ পরিচালনা করুন। সংবেদনশীল অপারেশনগুলো চার ধরনের পুনঃপ্রমাণীকরণ ত্রুটি দেখায়, যার প্রতিটিতে প্রাসঙ্গিক তথ্য থাকে।

বাস্তবায়ন প্যাটার্ন

OAuth প্রদানকারীগণ (গুগল, অ্যাপল, ফেসবুক, টুইটার, ইত্যাদি):

ত্রুটিটি ধরুন এবং reauthenticate(context:) কল করুন, যা স্বয়ংক্রিয়ভাবে OAuth প্রবাহটি পরিচালনা করে:

do {
  try await authService.deleteUser()
} catch let error as AuthServiceError {
  if case .oauthReauthenticationRequired(let context) = error {
    try await authService.reauthenticate(context: context)
    try await authService.deleteUser() // Retry operation
  }
}

ইমেল/পাসওয়ার্ড:

ত্রুটিটি ধরুন, পাসওয়ার্ডের জন্য অনুরোধ করুন, ক্রেডেনশিয়াল তৈরি করুন এবং reauthenticate(with:) :

do {
  try await authService.updatePassword(to: newPassword)
} catch let error as AuthServiceError {
  if case .emailReauthenticationRequired(let context) = error {
    // Show your password prompt UI
    let password = await promptUserForPassword()
    let credential = EmailAuthProvider.credential(
      withEmail: context.email,
      password: password
    )
    try await authService.reauthenticate(with: credential)
    try await authService.updatePassword(to: newPassword) // Retry
  }
}

ফোন:

ত্রুটিটি ধরুন, ফোন যাচাই করুন, ক্রেডেনশিয়াল তৈরি করুন, এবং reauthenticate(with:) :

do {
  try await authService.deleteUser()
} catch let error as AuthServiceError {
  if case .phoneReauthenticationRequired(let context) = error {
    // Send verification code
    let verificationId = try await authService.verifyPhoneNumber(
      phoneNumber: context.phoneNumber
    )
    // Show your SMS code input UI
    let code = await promptUserForSMSCode()
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationId,
      verificationCode: code
    )
    try await authService.reauthenticate(with: credential)
    try await authService.deleteUser() // Retry
  }
}

ইমেল লিঙ্ক:

ত্রুটিটি শনাক্ত করুন, যাচাইকরণ ইমেল পাঠান এবং আগত URL-টি পরিচালনা করুন:

do {
  try await authService.updatePassword(to: newPassword)
} catch let error as AuthServiceError {
  if case .emailLinkReauthenticationRequired(let context) = error {
    // Send verification email
    try await authService.sendEmailSignInLink(
      email: context.email,
      isReauth: true
    )
    // Show your "Check your email" UI
    await showCheckEmailUI()
    // When user taps the link, it opens your app with a URL
    // Handle it in your URL handler:
    // try await authService.handleSignInLink(url: url)
    // The handleSignInLink method automatically completes reauthentication
    try await authService.updatePassword(to: newPassword) // Retry
  }
}

সমস্ত পুনঃপ্রমাণীকরণ কনটেক্সট অবজেক্টে ব্যবহারকারীর দেখার জন্য টেক্সটের একটি .displayMessage প্রপার্টি অন্তর্ভুক্ত থাকে।

কাস্টম OAuth প্রদানকারী

আপনি অন্তর্নির্মিত পরিষেবাগুলি ছাড়াও অন্যান্য পরিষেবার জন্য নিজস্ব OAuth প্রদানকারী তৈরি করতে পারেন:

⚠️ গুরুত্বপূর্ণ: OIDC (OpenID Connect) প্রোভাইডার ব্যবহার করার আগে, সেগুলোকে অবশ্যই আপনার Firebase প্রোজেক্টের Authentication সেটিংসে কনফিগার করতে হবে। Firebase কনসোলে, Authentication → Sign-in method- এ যান এবং প্রয়োজনীয় ক্রেডেনশিয়াল (Client ID, Client Secret, Issuer URL) সহ আপনার OIDC প্রোভাইডারটি যুক্ত করুন। এছাড়াও, আপনাকে অবশ্যই আপনার প্রোভাইডারের ডেভেলপার কনসোলে Firebase দ্বারা প্রদত্ত OAuth রিডাইরেক্ট URI-টি রেজিস্টার করতে হবে। বিস্তারিত সেটআপ নির্দেশাবলীর জন্য Firebase OIDC ডকুমেন্টেশন দেখুন।

import FirebaseAuthSwiftUI
import FirebaseOAuthSwiftUI
import SwiftUI

struct ContentView: View {
  let authService: AuthService

  init() {
    let configuration = AuthConfiguration()
    
    authService = AuthService(configuration: configuration)
      .withOAuthSignIn(
        OAuthProviderSwift(
          providerId: "oidc.line",  // LINE OIDC provider
          scopes: ["profile", "openid", "email"],  // LINE requires these scopes
          displayName: "Sign in with LINE",
          buttonIcon: Image("line-logo"),
          buttonBackgroundColor: .green,
          buttonForegroundColor: .white
        )
      )
      .withOAuthSignIn(
        OAuthProviderSwift(
          providerId: "oidc.custom-provider",
          scopes: ["profile", "openid"],
          displayName: "Sign in with Custom",
          buttonIcon: Image(systemName: "person.circle"),
          buttonBackgroundColor: .purple,
          buttonForegroundColor: .white
        )
      )
  }

  var body: some View {
    AuthPickerView {
      Text("App Content")
    }
    .environment(authService)
  }
}