New in SwiftUI 4 : TextField in Alert by building ToDo app

DevTechie Inc
Dec 7, 2022


Photo by dlxmedia.hu on Unsplash

SwiftUI’s Alert View supports TextField input field now. It’s as easy as adding the TextField view inside the AlertView. We will explore this feature by building a simple ToDo list app.

Let’s start with ToDo model.

struct Todo: Identifiable {
    var id = UUID()
    var title: String
    var subtitle: String
}
Let’s add some example data.

extension Todo {
    static var example: [Todo] {
        [
            Todo(title: "Wash Car", subtitle: "Wash your car today!"),
            Todo(title: "Create blog entry", subtitle: "Add article to blog"),
        ]
    }
}
Now, we will add a view model for our list view.

class TodoListViewModel: ObservableObject {
    @Published var todos = [Todo]()
    @Published var showNew = false
    
    init() {
        self.todos = Todo.example
    }
    
    func addNew(todo: Todo) {
        todos.append(todo)
    }
    
    func remove(todo: Todo) {
        todos.removeAll { t in
            t.id == todo.id
        }
    }
}
We also need view model for new todo item:

class NewTodoViewModel: ObservableObject {
    @Published var title = ""
    @Published var subtitle = ""
}
Next, we will add view for the list:

struct TextFieldInAlert: View {
    
    @StateObject var todoListVM = TodoListViewModel()
    
    var body: some View {
        NavigationView {
            List(todoListVM.todos) { todo in
                Text(todo.title)
            }
            .navigationTitle("DevTechie Todos")
        }
    }
}

Now we are ready to add new todo item to the list. We will start by adding view model into the view.

@StateObject var newTodo = NewTodoViewModel()

In order to add new item, we will use alert view with TextField. Here is how the signature looks like:

.alert("<<title>>", isPresented: <<binding to show>>,
       actions: {
   <<TextFields>>
   <<Buttons>>
})
Here is how it will look for our simplest app:

.alert("Add New Item", isPresented: $todoListVM.showNew,
       actions: {    TextField("Title", text: $newTodo.title, axis: .vertical)
    TextField("Subtitle", text: $newTodo.subtitle, axis: .vertical)    Button ("Save") {
        todoListVM.todos.append(Todo(title: newTodo.title, subtitle: newTodo.subtitle))
        newTodo.title = ""
        newTodo.subtitle = ""
    }    Button ("Cancel", role: .cancel) {
        newTodo.title = ""
        newTodo.subtitle = ""
    }
})
Complete app:

struct TextFieldInAlert: View {
    
    @StateObject var todoListVM = TodoListViewModel()
    
    @StateObject var newTodo = NewTodoViewModel()
    
    var body: some View {
        NavigationView {
            List(todoListVM.todos) { todo in
                Text(todo.title)
            }
            .navigationTitle("DevTechie Todos")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        todoListVM.showNew.toggle()
                    } label: {
                        Label("Add", systemImage: "plus.circle")
                    }
                    .alert("Add New Item", isPresented: $todoListVM.showNew,
                           actions: {
                        
                        TextField("Title", text: $newTodo.title, axis: .vertical)
                        TextField("Subtitle", text: $newTodo.subtitle, axis: .vertical)
                        Button ("Save") {
                            todoListVM.todos.append(Todo(title: newTodo.title, subtitle: newTodo.subtitle))
                            newTodo.title = ""
                            newTodo.subtitle = ""
                        }
                        Button ("Cancel", role: .cancel) {
                            newTodo.title = ""
                            newTodo.subtitle = ""
                        }
                    })
                }
            }
        }
    }
}

class NewTodoViewModel: ObservableObject { @Published var title = "" @Published var subtitle = "" }
class TodoListViewModel: ObservableObject { @Published var todos = [Todo]() @Published var showNew = false init() { self.todos = Todo.example } func addNew(todo: Todo) { todos.append(todo) } func remove(todo: Todo) { todos.removeAll { t in t.id == todo.id } } }
struct Todo: Identifiable { var id = UUID() var title: String var subtitle: String }
extension Todo { static var example: [Todo] { [ Todo(title: "Wash Car", subtitle: "Wash your car today!"), Todo(title: "Create blog entry", subtitle: "Add article to blog"), ] } }
Our final view looks like this:

With that we have reached the end of this article. Thank you once again for reading. Don’t forget follow 😍. Also subscribe our newsletter at https://www.devtechie.com