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