Todo App - iOS
The todo application uses Suas to add, remove and move a list of todo items. In our Todo app, the list of todos is represented as a TodoListView
view. This todo list contains a table view. (Check the Todo application source code on GitHub)
Below is the view used in our todo application.
class TodoListView: UIView, Component {
let tableView = UITableView()
var state: TodoList = TodoReducer().initialState {
didSet {
// When state changes, we reload the table
tableView.reloadData()
}
}
convenience init() {
self.init(frame: CGRect(x: 0, y: 0, width: 300, height: 500))
addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
tableView.frame = frame
// Add a listener to the store
// Add store. [Weak self] to prevent retain cycles
store.addListener(forStateType: TodoList.self) { [weak self] newState in
// Notice that we capture self weakly to prevent strong memory cycles
self?.state = newState
}.linkLifeCycleTo(object: self)
}
}
extension TodoListView: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// the number of cells is the number of todos
return state.todos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "TodoCell")
// Use the states to populate the cell
let todoItem = state.todos[indexPath.row]
cell.textLabel?.text = todoItem.title
cell.textLabel?.textColor = todoItem.isCompleted ? UIColor.gray : UIColor.black
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Toggle a cell completion status by dispatching actions
store.dispatch(action: ToggleTodo(index: indexPath.row))
}
}
First, our TodoListView
component defines its state of TodoList
type.
var state: TodoList = TodoReducer().initialState {
didSet {
// When state changes, we reload the table
tableView.reloadData()
}
}
In the init
function, the component adds a listener to the state by calling store.addListener
. When the TodoList
state in the store changes, the store
notifies this listener.
When the listener is notified, we update the state in the TodoListView
which invalidates the table so that it reads the new todo elements from the state.
In the UITableView
data source functions we use the view's state to populate the view:
- We use the count of the todo to specify how many cells the table has
- The todo item title and completion status is used to populate the cell
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// the number of cells is the number of todos
return state.todos.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "TodoCell")
// Use the states to populate the cell
let todoItem = state.todos[indexPath.row]
cell.textLabel?.text = todoItem.title
cell.textLabel?.textColor = todoItem.isCompleted ? UIColor.gray : UIColor.black
return cell
}
In our todo app, we want to mark an item as completed when we tap on that todo item cell. To achieve that we dispatch the ToggleTodo
action to the store.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// When state changes, we reload the table
store.dispatch(action: ToggleTodo(index: indexPath.row))
}
What's Next
Todo application source code on GitHub
Learn more about Suas listeners
Suas listeners
Using the StateSelector
Adding a listener with a filter
Other sample apps
List of sample applications
Counter App Example
Todo app with settings example
Search Cities Example
Updated about 6 years ago