For any iOS Developer, reaching the point where you master creating interfaces is a huge milestone. However, a modern application rarely lives isolated on the device. Sooner or later, you need to store data in the cloud, authenticate users, send notifications, or monitor crashes. This is where the big question arises: which backend do I choose?
Knowing how to use Firebase with SwiftUI has become one of the most in-demand skills in Swift programming. Firebase, Google’s Backend-as-a-Service (BaaS) platform, eliminates the need to write and maintain your own server infrastructure, allowing you to focus on what you do best: creating amazing experiences in SwiftUI for the Apple ecosystem.
In this extensive tutorial, I will guide you step by step on how to integrate, configure, and use Firebase in your Xcode projects, ensuring that your Swift code works flawlessly not only on iOS but also on macOS and watchOS.
1. Why are Firebase and SwiftUI the perfect match?
Historically, integrating external frameworks into Apple development could be a headache. But the evolution of Swift programming and the maturity of modern SDKs have changed the landscape.
- Real-Time Synchronization: Services like Cloud Firestore fit perfectly with the reactive nature of SwiftUI. When data changes in the cloud, your
@Publishedproperties update, and your view is magically redrawn. - Swift Package Manager (SPM): Gone are the days of fighting with CocoaPods. Today, integrating Firebase in Xcode is native, fast, and secure.
- Multiplatform Support: The Firebase SDK is designed to support multiple architectures, making it easy to bring your iOS app to the Mac or Apple Watch while sharing almost all your business logic.
2. Setting the Stage: Configuration in the Firebase Console
Before writing a single line of Swift code, we need to create the “brain” of our app in the cloud.
Step 2.1: Create the Project
- Go to the Firebase Console.
- Click on “Add project” and give it a name (e.g., “MySwiftUIApp”).
- You can enable or disable Google Analytics depending on your needs (for this tutorial, it’s optional).
- Once the project is created, click on the iOS icon in the central dashboard to add an Apple app.
Step 2.2: Register the App and Download the Plist
Firebase needs to know exactly which app has permission to connect to its database.
- Bundle ID: Enter the exact Bundle Identifier of your project in Xcode (for example,
com.yourcompany.MySwiftUIApp). This step is critical; if it doesn’t match, nothing will work. - Download the
GoogleService-Info.plistfile.
Drag this downloaded file directly into your project in Xcode (right below your Info.plist file or in the root of the project). Make sure to check the “Copy items if needed” box and that your main Target is selected.
3. Installing the Firebase SDK in Xcode
As an up-to-date iOS Developer, you will use Swift Package Manager (SPM). It is the recommended method by both Apple and Google.
- Open your project in Xcode.
- Go to File > Add Packages… (or Add Package Dependencies).
- In the search bar in the top right corner, paste the official Firebase repository URL:
https://github.com/firebase/firebase-ios-sdk - Select the Up to Next Major Version rule and click Add Package.
Xcode will download the source code (this might take a couple of minutes). When it finishes, it will ask you to choose which Firebase modules you want to include. For this tutorial, select FirebaseAnalytics, FirebaseAuth, and FirebaseFirestore.
4. Initializing Firebase in your SwiftUI App
With SwiftUI, the old AppDelegate is no longer the default entry point. We now use the App protocol. For Firebase to work correctly, it must be initialized as soon as the application launches.
Open the main file of your project (usually named [YourAppName]App.swift) and add the initialization. There are several ways to do this, but the most compatible one for multiplatform apps (iOS, macOS, watchOS) is using a delegate adaptor.
To keep the code clean and compatible with modern Swift programming, we will use the delegate adaptor for iOS/macOS, as Firebase recommends this route to handle push notifications and deep links in the future.
import SwiftUI
import FirebaseCore
// We create an AppDelegate to handle initialization
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// Initialize Firebase
FirebaseApp.configure()
return true
}
}
@main
struct MySwiftUIApp: App {
// We inject the AppDelegate into the SwiftUI lifecycle
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Note for watchOS: If you are building for the Apple Watch,
UIApplicationDelegatedoes not exist. Instead, you will need to useWKExtensionDelegateor simply callFirebaseApp.configure()inside aninit()method in your@mainstruct.
5. Cloud Firestore: Real-Time Database
Cloud Firestore is the crown jewel of Firebase. It is a flexible, scalable NoSQL database, perfect for learning how to use Firebase with SwiftUI reactively.
Let’s create a real-world example: a To-Do List application that syncs in real-time across your iPhone and your Mac.
5.1. The Data Model
First, we define our data model in Swift. We will use Codable so that Firestore can automatically transform our objects into NoSQL documents and vice versa.
import Foundation
import FirebaseFirestore
struct Task: Identifiable, Codable {
@DocumentID var id: String? // Firebase will assign this ID automatically
var title: String
var isCompleted: Bool
var creationDate: Date
}
5.2. The ViewModel (Business Logic)
Like a good iOS Developer, we will separate the logic from the view using the MVVM (Model-View-ViewModel) pattern.
import Foundation
import FirebaseFirestore
import Combine
class TasksViewModel: ObservableObject {
@Published var tasks: [Task] = []
private var db = Firestore.firestore()
private var listenerRegistration: ListenerRegistration?
// Start listening for real-time data
func subscribeToTasks() {
// We access the "tasks" collection ordered by date
listenerRegistration = db.collection("tasks")
.order(by: "creationDate", descending: true)
.addSnapshotListener { [weak self] (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(String(describing: error))")
return
}
// We map the Firestore documents to our Swift structure
self?.tasks = documents.compactMap { document -> Task? in
try? document.data(as: Task.self)
}
}
}
// Stop listening to save resources
func unsubscribe() {
listenerRegistration?.remove()
}
// Add a new task to Firebase
func addTask(title: String) {
let newTask = Task(title: title, isCompleted: false, creationDate: Date())
do {
let _ = try db.collection("tasks").addDocument(from: newTask)
} catch {
print("Error saving the task: \(error.localizedDescription)")
}
}
// Update the status of a task
func toggleCompleted(task: Task) {
guard let id = task.id else { return }
db.collection("tasks").document(id).updateData([
"isCompleted": !task.isCompleted
]) { error in
if let error = error {
print("Error updating task: \(error.localizedDescription)")
}
}
}
}
5.3. The Interface in SwiftUI
Now, we connect our ViewModel with a SwiftUI view. Watch how the UI will automatically update thanks to @StateObject and @Published whenever someone adds a task from any device.
import SwiftUI
struct ContentView: View {
@StateObject private var viewModel = TasksViewModel()
@State private var newTaskTitle: String = ""
var body: some View {
NavigationView {
VStack {
// Form to add tasks
HStack {
TextField("New task...", text: $newTaskTitle)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
guard !newTaskTitle.isEmpty else { return }
viewModel.addTask(title: newTaskTitle)
newTaskTitle = "" // Clear the field
}) {
Image(systemName: "plus.circle.fill")
.font(.title2)
}
}
.padding()
// Task list
List(viewModel.tasks) { task in
HStack {
Text(task.title)
.strikethrough(task.isCompleted, color: .gray)
.foregroundColor(task.isCompleted ? .gray : .primary)
Spacer()
Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
.foregroundColor(task.isCompleted ? .green : .gray)
.onTapGesture {
viewModel.toggleCompleted(task: task)
}
}
}
}
.navigationTitle("My Tasks")
.onAppear {
viewModel.subscribeToTasks()
}
.onDisappear {
viewModel.unsubscribe()
}
}
}
}
6. Multiplatform Considerations (iOS, macOS, watchOS)
The Swift programming code we just wrote for the data layer and the model is 100% universal. You can create a macOS or watchOS target in Xcode and reuse the TasksViewModel.swift file without changing a single comma.
However, a good iOS Developer knows that the user experience (UX) differs:
- macOS: Navigation based on
NavigationView(orNavigationSplitViewin iOS 16+) works wonderfully, but make sure to adjust margins (paddings) so it doesn’t feel like an oversized iPhone app on the Mac screen. - watchOS: Avoid long text fields (
TextField) whenever possible. On watchOS, you might prefer tasks to only be markable as completed, delegating the creation of new tasks to the companion iPhone app, or using Voice Dictation. Also, remember that watchOS apps go into the background quickly; manage Firestore’slistenerRegistrationcarefully so as not to drain the Apple Watch’s battery.
Conclusion
Learning how to use Firebase with SwiftUI unlocks a completely new level in your career as an iOS Developer. In less than half an hour, we have set up a project in Xcode, integrated a professional backend SDK via Swift Package Manager, and created a to-do list application with real-time database synchronization.
Modern Swift programming and SwiftUI‘s declarative API make connecting to Firebase’s asynchronous data flows feel like magic. Whether you are building a quick prototype, an MVP for a startup, or a full enterprise application across iOS, macOS, and watchOS, the combination of these technologies is one of the most powerful tools at your disposal today.
If you have any questions about this article, please contact me and I will be happy to help you . You can contact me on my X profile or on my Instagram profile.