Getting started with C++17 mobile cross-platform development using Boden

Ashampoo Systems
Ashampoo Systems
Published in
9 min readSep 17, 2019

--

TL;DR

  • Love C++ and mobile apps? Learn how to write a simple cross-platform todo mobile app in idiomatic C++17 using the Boden Cross-Platform GUI Framework in this tutorial.
  • The resulting app will use native machine code and native widgets to manage your todos in a simple single view application.
  • Jump right to the boden-todomvc example repository on GitHub to have a look at the final code!

What will I learn?

In this tutorial you will learn how to write an app once in C++17 and then run it on an Android Phone and an iPhone from the same codebase.

Your app will:

  • Allow users to manage a list of todos in a simple way. Users can add new todos, mark existing todos as completed, and delete todos using a swipe gesture. The app won’t lose the todo list when closed.
  • It will look and behave natively since it’s going to use the native UI widgets of the platform.
  • It will be based on the MVC architecture, similar to what you would write in Objective-C/Swift on iOS or Java/Kotlin on Android.

Setting up the project

The first step is to set up a new app project with the Boden Framework. Don’t worry, this will be quick and easy.

First, clone the boden git repository from GitHub:

git clone --recursive https://github.com/AshampooSystems/boden.git

Make sure your system meets Boden’s requirements by following the Getting Started Guide on boden.io.

Finally, use the boden command line tool to generate a new project:

cd boden
python boden.py new -n todomvc

That’s it!

The tool will create a folder titled todomvc and generate the source and project files for a barebones Boden application, which we’ll modify and extend in the next part of this tutorial.

Setting up your development environment

Boden comes with support for Xcode on macOS and Android Studio on Linux, Windows, and macOS.

Change to the todomvc directory and use the boden command line tool to conveniently open the project in your IDE:

cd todomvc
python ../boden.py open

This will open Android Studio on Linux/Windows or Xcode on macOS.

In this tutorial, you will add several new source files to the project. You can simply add those files using the IDE. In this case, make sure the files are added to the source directory.

An alternative way to add files is to create them in the source directory using a text editor and then re-running python.py ../boden.py open. This will automatically regenerate the project files with the new source files added.

Planning the todo app

When the todo app is finished, users should be able to:

  • Write a new todo and add it to a list of items that need to be completed.
  • Mark an existing todo in the list as completed.
  • Delete a todo from the list.

The app should also persist the todo list to device storage so that todos won’t be lost when the app is closed and re-opened.

To keep things simple the app will be based on a single screen with a text field at the top and a scrollable list of todo items taking up the rest of the available screen space below. This is what it’s going to look like:

Planning the architecture

As mentioned above, this tutorial will stick to the well-known MVC pattern to structure the app’s architecture. Hence, the following components are needed to implement the full app:

  1. A data model of a todo. This is a simple data structure designed to store the information associated with a todo item in the list.
  2. A store component managing a list of todos and persisting that list to device storage.
  3. A list item view that presents a todo in the a view to the user.
  4. A data source accessing the store component to populate the list view.
  5. A view controller setting up the view, including a text field and a list view with the corresponding layout.

Creating the todo data model

The todo data model is a simple data structure consisting of a string containing the todo’s text and a bool indicating whether the todo is completed.

Create a new header file named Todo.h and add the following code:

Todo.h

Creating the store

The store is responsible for maintaining a list of todos in memory and on device storage. It can add and remove todos from the list, and it can load and save the list to disk as a JSON file.

Create a new header file named TodoStore.h and add the following code to declare the TodoStore class.

TodoStore.h

Create the corresponding source file named TodoStore.cpp .

In the first step, we’re going to include the necessary headers and implement the add() and remove() methods.

TodoStore.cpp

The add() method appends an uncompleted todo containing the given todo text at the end of the todos vector. The remove() method removes the todo at the given index from the vector. Both methods call save() to persist changes to the todo list immediately.

In the next step, we’re going to implement load() and save() to allow for storing and loading a list of todos from a JSON file.

Persisting the todo list

First, we’ll add a couple of new headers and using statements for JSON, path retrieval, and filesystem functions at the top of TodoStore.cpp .

TodoStore.cpp
  • We use nlohmann::json which is bundled with Boden to parse and serialize JSON data.
  • bdn::path is a Boden module providing functions for retrieving common platform-specific directory paths, which we’ll use to retrieve a path to the documents directory.
  • Finally, we use std::filesystem for creating directories and safely concatenating path components.

In the next step, we’re going to implement the load() and save() methods.

Add the following code at the end of TodoStore.cpp :

TodoStore.cpp

Here’s a short summary of what the functions do:

  • load() uses std::ifstream to load the todo.json file from disk. It then parses the contents of the file using nlohmann::json and pushes it to the todos JSON array.
  • save() uses std::ofstream and nlohmann::json to serialize the JSON data to the todo.json file.
  • todoFilePath() uses bdn::path::documentDirectoryPath() and std::filesystem to safely create the document directory path for the application. It then returns the full path of the todo.json file on device storage.

As a final step, we are going to add to_json() and from_json() to the Todo data structure defined above:

Todo.h

And create theTodo.cpp source file containing the corresponding implementation:

Todo.cpp

Implementing the todo item view

TodoItemView defines how the list item is going to be displayed on screen. It will be used by the list view data source to provide item views to the list view.

It inherits from bdn::ui::CoreLess to indicate that it is a composite view without a native “view core”. This means that it does not wrap a native platform widget, but rather aggregates existing platform-independent views into one.

bdn::ui::ContainerView is going to be the actual base class of TodoItemView. This allows the class to manage child views in the view hierarchy.

Here’s what the TodoItemView.h header file looks like:

TodoItemView.h
  • bdn::Property is used to make the Todo data structure’s members available as data bindings.
  • using CoreLess<ContainerView>::CoreLess declares that the TodoItemView class should reuseCoreLess‘s constructor.
  • void init() override is required to implement the view’s custom initialization, where we’re going to set up its child views and layout.

In the next step, we’ll create a file named TodoItemView.cpp and add the corresponding implementation:

TodoItemView.cpp
  • We override void init() to initialize the item view’s child views and layout.
  • The layout is defined by setting the stylesheet property to a suitable layout definition for the item view.
  • The rest of the implementation adds a checkbox for marking a todo as completed and a label for displaying a todo’s text accompanied with the corresponding property bindings and child layouts.

Implementing the list view data source

In the next step, we are going to implement TodoListDataSource, which will be used by the todo app’s list view to retrieve instances of TodoItemView.

The data source can be seen as a view model, fetching data from TodoStore, populating a TodoItemView with it, and then delivering it to a ListView.

Create a new file named TodoListDataSource.h and add the following declaration:

TodoListDataSource.h

Now, create the corresponding source file named TodoListDataSource.cpp :

TodoListDataSource.cpp

TodoListDataSource inherits from bdn::ui::ListViewDataSource, which declares a couple of pure virtual functions that need to be overridden:

  • numberOfRows() returns the number of rows to display in the list view.
  • viewForRowIndex() returns a reusable item view to be displayed at the given row index.
  • heightForRowIndex() returns the height of the view at the given row index.

Let’s a have a closer look at the implementation of viewForRowIndex().

viewForRowIndex() is called by the framework with the listView, rowIndex, and reusableView arguments. If a reusable view is not provided by the framework (reusableView == nullptr), the implementation is responsible for instantiating a new item view:

if (!reusableView) {
// Create a new TodoItemView instance if none is
// provided by the framework
reusableView = std::make_shared<TodoItemView>();
}

The rest of the code populates the item view with data from the store and installs a listener on the completed property to update the store when the user clicks the item’s checkbox:

auto item = std::dynamic_pointer_cast<TodoItemView>(reusableView);

item->text = _store->todos.at(rowIndex).at("text");
item->completed = _store->todos.at(rowIndex).at("completed");

std::weak_ptr<View> weakItem(item);

item->completed.onChange().unsubscribeAll();
item->completed.onChange() += [list=listView.get(), weakItem, this](const auto &property) {
if (auto rowIndex = list->rowIndexForView(weakItem.lock())) {
_store->todos.at(*rowIndex).at("completed") = property.get();
_store->save();
}
};
return reusableView;

Setting up the app’s main view

Finally, we’re going to implement the app’s MainViewController class.

The main view controller is instantiated by the framework upon app launch. It’s responsible for setting up the application’s window and principal view hierarchy.

MainViewController.h and MainViewController.cpp have already been generated by the boden project generator tool. We’re now going to change and extend these files in order to set up the todo app’s main view.

This is the final MainViewController declaration:

MainViewController.h

MainViewController manages the following components:

  • The application’s window.
  • The text field users will use to enter the text for a new todo item.
  • The list view for presenting todo items.
  • A container view that contains the text field and the list view and acts as the content view of the window.
  • An instance of TodoListDataSource which we created above to provide the list view with item views populated with data from the TodoStore.
  • An instance of TodoStore itself responsible for managing the data model.

That’s it for the header.

Now, let’s have a look at the implementation in MainViewController.cpp. The application’s main components are set up in the constructor of MainViewController. First, we create the app’s window and set its title and layout:

MainViewController.cpp

In the next step, we set up the main container, text field, and list view:

MainViewController.cpp

Finally, we create an instance of TodoStore, call load() to load existing todos from disk, and create and wire an instance of TodoListDataSource.

MainViewController.cpp

Wrapping up

In this tutorial, you’ve learned how to write a cross-platform todo list mobile app in C++17 using the Boden Cross-Platform GUI Framework.

If you want to play around with the project without writing the app yourself, take a look at the final code at the boden-todomvc example repository on GitHub.

If you want to learn more about Boden, check out our documentation at boden.io.

In the next tutorial, we’ll complete the app by setting it up for deployment to the Google Play Store and Apple iOS App Store.

What do you think?

What do you think about the tutorial and about the Boden framework? Feel free to offer your perspective and ideas in the comments section below.

If you enjoyed this article, feel free to hit that clap button 👏 to help others find it.

--

--