TL;DR:
- Set up Firebase Authentication in your SwiftUI app using Email/Password and Google Sign-In.
- Enable persistent login with session handling and Firestore user syncing.
- Support password reset and updates securely through Firebase’s built-in methods.
- Store and manage user profiles in Firestore using a clean MVVM architecture.
- Build a modular and scalable auth system that’s production-ready and SwiftUI-friendly.
Introduction:
At Creole Studios, we’re always exploring ways to build secure, scalable, and user-friendly digital experiences — and one of the most crucial parts of any modern app is authentication. Whether you’re building a startup MVP or a full-fledged iOS product, having a robust auth system is non-negotiable.
In this blog, our mobile development team walks you through setting up a complete Firebase Authentication system in SwiftUI, following a clean MVVM architecture. From Email/Password login and Google Sign-In, to persistent sessions, password reset, and Firestore integration — we’ve bundled everything you need into a production-ready solution.
This step-by-step guide is crafted with SwiftUI developers in mind, using the same practical approach we follow when building secure mobile apps for our clients across the globe.
Let’s get started on creating a reliable authentication flow that just works.
Demo Preview
Here’s a quick walkthrough of the app in action — showing login, Google Sign-In, password reset, and Firestore integration
Firebase Setup (Quick Overview)
- Create a Firebase project at console.firebase.google.com
- Enable Authentication → Google and Email/Password
- Add Firestore under Build > Firestore Database
- Download GoogleService-Info.plist and add it to your Xcode project
- Update your Info.plist with the reversed client ID:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.YOUR_CLIENT_ID</string>
</array>
</dict>
</array>
Also enable Keychain Sharing in the Signing & Capabilities section of your target.
Entry Point: ContentView.swift
import SwiftUI
struct ContentView: View {
@EnvironmentObject var viewModel:AuthViewModel
var body: some View {
Group{
if viewModel.userSession != nil {
ProfileView()
} else {
LoginView()
}
}
}
}
struct ContentView_Previews: PreviewProvider{
static var previews: some View{
ContentView()}
}
Core Logic: AuthViewModel.swift
At the heart of your authentication system is the AuthViewModel, which:
- Handles user login/registration
- Authenticates with Google
- Sends password reset emails
- Syncs user data with Firestore
- Keeps the user logged in
Here’s a breakdown of the key features:
Email/Password Sign-In
func signIn(withEmail email: String, password: String) async throws {
self.isLoading = true
do {
let result = try await Auth.auth().signIn(withEmail: email, password: password)
self.userSession = result.user
self.isLoading = false
await fetchUser()
} catch {
self.isLoading = false
self.Error = error.localizedDescription
}
}
Authenticates the user and sets the session state.
User Registration
func createUser(withEmail email: String,password: String, fullname: String) async throws {
self.isLoading = true
do{
let result = try await Auth.auth().createUser(withEmail: email, password: password)
self.userSession = result.user
let user = User(id: result.user.uid, fullname: fullname, email: email,photoURL: URL(string: ""))
let encodeUser = try Firestore.Encoder().encode(user)
try await Firestore.firestore().collection("users").document(user.id).setData(encodeUser)
self.isLoading = false
await fetchUser()
} catch{
self.Error = error.localizedDescription
self.isLoading = false
}
}
Registers a new user and stores their data in Firestore.
Persistent Login
Handled automatically by:
func fetchUser() async {
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let snapshot = try? await Firestore.firestore().collection("users").document(uid).getDocument() else { return }
self.currentUser = try? snapshot.data(as: User.self)
}
Firebase persists the session across app restarts.
Google Sign-In
func signInWithGoogle() async -> Bool {
guard let clientID = FirebaseApp.app()?.options.clientID else {
fatalError("No client ID found in Firebase configuration")
}
let config = GIDConfiguration(clientID: clientID)
GIDSignIn.sharedInstance.configuration = config
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let window = windowScene.windows.first,
let rootViewController = window.rootViewController else {
return false
}
do {
let userAuthentication = try await GIDSignIn.sharedInstance.signIn(withPresenting: rootViewController)
let user = userAuthentication.user
guard let idToken = user.idToken else { throw AuthenticationError.tokenError(message: "ID token missing") }
let accessToken = user.accessToken
let credential = GoogleAuthProvider.credential(withIDToken: idToken.tokenString,
accessToken: accessToken.tokenString)
let result = try await Auth.auth().signIn(with:credential)
let googleuser = User(id: result.user.uid, fullname: result.user.displayName ?? "" , email: result.user.email ?? "",photoURL: result.user.photoURL ?? URL(string: ""))
let encodeUser = try Firestore.Encoder().encode(googleuser)
try await Firestore.firestore().collection("users").document(googleuser.id).setData(encodeUser)
self.isLoading = false
await fetchUser()
self.userSession = result.user
return true
}
catch {
self.Error = error.localizedDescription
return false
}
}
This is fully async and SwiftUI-friendly. It signs in with Google, gets Firebase credentials, and stores user data in Firestore.
Password Reset and Update
func resetPassword(email: String) async throws{
self.isLoading = true
do {
try await Auth.auth().sendPasswordReset(withEmail: email)
showMessage = "Link send successfully"
self.isLoading = false
} catch {
showMessage = error.localizedDescription
self.Error = error.localizedDescription
self.isLoading = false
print("Error sending password reset email: \(error.localizedDescription)")
}
}
func updatePassword(password:String) async throws{
self.isLoading = true
do{
try await userSession?.updatePassword(to: password)
self.isLoading = false
} catch {
self.Error = error.localizedDescription
self.isLoading = false
print("Error sending password reset email: \(error.localizedDescription)")
}
}
Securely handles forgotten passwords and password changes.
User Model
Make sure your User.swift looks like this:
struct User: Identifiable, Codable {
let id: String
let fullname: String
let email: String
let photoURL:URL?
var initials: String{
let formatter = PersonNameComponentsFormatter()
if let components = formatter.personNameComponents(from: fullname){
formatter.style = .abbreviated
return formatter.string(from: components)
}
return ""
}
}
Folder Structure:
GitHub Project Link
Want to see the full code in action?
👉 Check out the GitHub Repository Includes the full SwiftUI project with Firebase auth and Firestore setup.
⭐ ️ Start the repo if it helps you — and feel free to contribute!
Final Thoughts
With the setup we’ve outlined, you’ve now built a fully functional, production-ready authentication system in SwiftUI powered by Firebase. You’ve implemented:
- Email/Password Sign-In and Sign-Up
- Google Sign-In Integration
- Persistent login sessions
- Password reset & update functionality
- Firestore-backed user data syncing
- Clean MVVM architecture for maintainability
At Creole Studios, we specialize in crafting secure, scalable mobile apps with seamless user experiences — and authentication is at the heart of it all. Our mobile development team has deep experience working with SwiftUI, Firebase, and modern architectural patterns to build iOS solutions that perform flawlessly in real-world scenarios.
If you’re a startup, enterprise, or entrepreneur looking to build or improve your iOS application, Creole Studios is the right technology partner to help you bring your vision to life — with speed, quality, and long-term reliability.
Let’s connect to build your next SwiftUI app the right way.