Building a To-Do App with Realm and SwiftUI: A Step-by-Step Guide

Why Realm?

Data persistence is crucial when building a seamless user experience. Realm offers a lightweight, object-oriented data model that simplifies the process of managing local data in your iOS app. With its ease of use, comprehensive documentation, and wide community support, Realm is an ideal choice for synchronizing database structures across platforms.

Setting Up Your SwiftUI Project

To get started, open Xcode and create a new SwiftUI project. Next, install the Realm SDK by adding the Realm repository URL in the Xcode menu. Make sure to check both the Realm and RealmSwift packages.

// Install Realm SDK
import RealmSwift

Creating a To-Do Model

Create a to-do model called Task with the Identifiable protocol. This will enable you to manage tasks efficiently.

struct Task: Identifiable {
    let id = UUID()
    var title: String
    var isCompleted: Bool
}

Designing the Main List View

Create a list view and a reusable item view called TaskRowView. This will display each task with a button to mark its completion status and a text for the task title.

struct TaskRowView: View {
    let task: Task

    var body: some View {
        HStack {
            Button(action: {
                // Mark task as completed
            }) {
                Text(task.title)
            }
            Spacer()
            Button(action: {
                // Delete task
            }) {
                Image(systemName: "trash")
            }
        }
    }
}

Adding New Tasks with AddTaskView

Create a new view file called AddTaskView, which will enable users to add new tasks dynamically. This view includes a text field to input new task titles and a button to submit the task.

struct AddTaskView: View {
    @State private var newTaskTitle = ""

    var body: some View {
        VStack {
            TextField("Enter new task title", text: $newTaskTitle)
            Button(action: {
                // Add new task
            }) {
                Text("Add Task")
            }
        }
    }
}

Creating a Realm Model

A Realm model is a regular Swift class that subclasses the Realm Object protocol and conforms to the Realm database schema. Create a Realm model called TaskObject, which will communicate with the Realm object protocol and the database.

class TaskObject: Object, Identifiable {
    @objc dynamic var id = UUID()
    @objc dynamic var title = ""
    @objc dynamic var isCompleted = false
}

Creating the Task View Model

The task view model enables communication between the views and the Realm database. It includes functions to insert new tasks, get the list of all tasks, and update task completion status.

class TaskViewModel {
    let realm = try! Realm()

    func addTask(_ task: Task) {
        try! realm.write {
            realm.add(TaskObject(from: task))
        }
    }

    func getTasks() -> [Task] {
        return realm.objects(TaskObject.self).map { Task(from: $0) }
    }

    func updateTaskCompletionStatus(_ task: Task, isCompleted: Bool) {
        try! realm.write {
            let taskObject = realm.object(ofType: TaskObject.self, forPrimaryKey: task.id)!
            taskObject.isCompleted = isCompleted
        }
    }
}

Updating the Main List and Adding a Form

Update the TaskListView and AddTaskView to integrate with the Realm database. The TaskListView will display tasks fetched from the Realm database, while the AddTaskView will enable users to add new tasks.

// Update TaskListView
struct TaskListView: View {
    @StateObject var viewModel = TaskViewModel()

    var body: some View {
        List(viewModel.getTasks()) { task in
            TaskRowView(task: task)
        }
    }
}

// Update AddTaskView
struct AddTaskView: View {
    @StateObject var viewModel = TaskViewModel()
    @State private var newTaskTitle = ""

    var body: some View {
        VStack {
            TextField("Enter new task title", text: $newTaskTitle)
            Button(action: {
                viewModel.addTask(Task(title: newTaskTitle, isCompleted: false))
            }) {
                Text("Add Task")
            }
        }
    }
}

The Task Detail View

Add a new view called TaskView to display task details. This view includes edit and delete functions, enabling users to update task titles and delete tasks from the Realm database.

struct TaskView: View {
    let task: Task
    @StateObject var viewModel = TaskViewModel()

    var body: some View {
        VStack {
            Text(task.title)
            Button(action: {
                // Edit task title
            }) {
                Text("Edit")
            }
            Button(action: {
                viewModel.updateTaskCompletionStatus(task, isCompleted:!task.isCompleted)
            }) {
                Text("Toggle Completion Status")
            }
            Button(action: {
                // Delete task
            }) {
                Text("Delete")
            }
        }
    }
}

Schema Migration

When modifying the database schema, schema migration is crucial. Update the TaskObject and Task models to add a new field called Due Date. Then, update the view model to store the due date value.

// Update TaskObject
class TaskObject: Object, Identifiable {
    @objc dynamic var id = UUID()
    @objc dynamic var title = ""
    @objc dynamic var isCompleted = false
    @objc dynamic var dueDate = Date()
}

// Update Task
struct Task: Identifiable {
    let id = UUID()
    var title: String
    var isCompleted: Bool
    var dueDate: Date
}

Setting Up the Migration

To set up the migration, update the AppDelegate file to specify the schema version as 2. This will enable the migration to work properly.

// Update AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    Realm.Configuration.defaultConfiguration = Realm.Configuration(schemaVersion: 2)
    return true
}

Leave a Reply