With SwiftUI, Apple introduced a modern and declarative way to build user interfaces for iOS, macOS, watchOS, and tvOS applications. In this tutorial, we’ll leverage SwiftUI to create a simple yet powerful To-Do List app for iOS using Swift. By the end of this tutorial, you’ll have a solid understanding of SwiftUI’s key concepts and how to apply them to create real-world applications.

Step 1: Setting Up the Project Start by creating a new SwiftUI project in Xcode. Choose “App” as the template, and make sure to select SwiftUI as the user interface option. Name your project and choose a location to save it.

Step 2: Designing the User Interface The To-Do List app will consist of a list of tasks, with the ability to add new tasks and mark them as completed. Here’s a simple layout for our app:

  • NavigationView
    • List of tasks
      • ForEach loop to display tasks
      • Each task has a Text view for its title and a Button to mark it as completed
    • Button in the navigation bar to add a new task

Step 3: Implementing the Model Define a Task struct to represent individual tasks in our app. Each task will have a title and a boolean property to track its completion status.

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

Step 4: Creating the ViewModel Create a ViewModel class to manage the app’s data and logic. This class will hold an array of tasks and provide methods to add new tasks, toggle their completion status, and retrieve the list of tasks.

class TaskViewModel: ObservableObject {
    @Published var tasks: [Task] = []

    func addTask(title: String) {
        let task = Task(title: title)
        tasks.append(task)
    }

    func toggleTaskCompletion(task: Task) {
        if let index = tasks.firstIndex(where: { $0.id == task.id }) {
            tasks[index].isCompleted.toggle()
        }
    }
}

Step 5: Building the User Interface Now, let’s create the user interface using SwiftUI. We’ll use a List view to display the tasks and allow users to mark them as completed

struct TaskListView: View {
    @ObservedObject var viewModel: TaskViewModel
    @State private var newTaskTitle = ""

    var body: some View {
        NavigationView {
            List {
                ForEach(viewModel.tasks) { task in
                    HStack {
                        Text(task.title)
                        Spacer()
                        Button(action: { viewModel.toggleTaskCompletion(task: task) }) {
                            Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle")
                        }
                    }
                }
            }
            .navigationTitle("To-Do List")
            .navigationBarItems(trailing: Button(action: addTask) {
                Image(systemName: "plus")
            })
        }
    }

    private func addTask() {
        viewModel.addTask(title: newTaskTitle)
        newTaskTitle = ""
    }
}

Step 6: Connecting the ViewModel to the View Finally, instantiate the TaskViewModel and pass it to the TaskListView to connect the data model to the user interface.

@main
struct TodoApp: App {
    var body: some Scene {
        WindowGroup {
            let viewModel = TaskViewModel()
            TaskListView(viewModel: viewModel)
        }
    }
}

Congratulations! You’ve successfully built a To-Do List app with SwiftUI in Swift. You’ve learned how to design a user interface, manage data with a ViewModel, and connect the two using SwiftUI’s declarative syntax. This app serves as a foundation for exploring more advanced features of SwiftUI and building more complex applications in the future.