Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Todo App

Creating a simple Todo app using Akka's untyped actors can be an excellent way to demonstrate how to manage state, handle concurrency, and build a basic application using Akka's actor model. Below, I will walk you through the process of building a Todo app with the following functionalities:

  1. Add a new Todo item.
  2. Remove a Todo item.
  3. Mark a Todo item as completed.
  4. List all Todo items.

1. Setting Up the Project

Start by setting up your Scala project with the necessary Akka dependencies in your build.sbt:

libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.6.x"

2. Defining the Messages

First, define the messages that actors will use to communicate. Messages are immutable case classes:

// Messages
case class AddTodo(id: Int, task: String)
case class RemoveTodo(id: Int)
case class CompleteTodo(id: Int)
case object GetTodos

3. Creating the Todo Actor

The TodoActor will manage the list of Todo items. It will handle messages to add, remove, complete, and retrieve Todo items:

import akka.actor.{Actor, ActorSystem, Props}

// Todo Item case class
case class TodoItem(id: Int, task: String, completed: Boolean = false)

class TodoActor extends Actor {
  var todos: Map[Int, TodoItem] = Map()

  def receive: Receive = {
    case AddTodo(id, task) =>
      todos += id -> TodoItem(id, task)
      println(s"Added Todo: $task")

    case RemoveTodo(id) =>
      todos.get(id) match {
        case Some(todo) =>
          todos -= id
          println(s"Removed Todo: ${todo.task}")
        case None =>
          println(s"Todo with id $id not found.")
      }

    case CompleteTodo(id) =>
      todos.get(id) match {
        case Some(todo) =>
          todos += id -> todo.copy(completed = true)
          println(s"Completed Todo: ${todo.task}")
        case None =>
          println(s"Todo with id $id not found.")
      }

    case GetTodos =>
      if (todos.isEmpty) {
        println("No Todos available.")
      } else {
        todos.values.foreach { todo =>
          val status = if (todo.completed) "Completed" else "Pending"
          println(s"${todo.id}: ${todo.task} [$status]")
        }
      }
  }
}

4. Creating the Main Application

Now, let's create a main application that will instantiate the ActorSystem, create the TodoActor, and interact with it:

object TodoApp extends App {
  // Create Actor System
  val system = ActorSystem("TodoSystem")

  // Create TodoActor
  val todoActor = system.actorOf(Props[TodoActor], "todoActor")

  // Interacting with the TodoActor
  todoActor ! AddTodo(1, "Buy milk")
  todoActor ! AddTodo(2, "Go to the gym")
  todoActor ! GetTodos

  todoActor ! CompleteTodo(1)
  todoActor ! GetTodos

  todoActor ! RemoveTodo(2)
  todoActor ! GetTodos

  // Shutdown the Actor System
  system.terminate()
}

5. Running the Application

To run the application, simply execute the TodoApp object in your Scala environment. The output will be:

Added Todo: Buy milk
Added Todo: Go to the gym
1: Buy milk [Pending]
2: Go to the gym [Pending]
Completed Todo: Buy milk
1: Buy milk [Completed]
2: Go to the gym [Pending]
Removed Todo: Go to the gym
1: Buy milk [Completed]
No Todos available.