Cross platform Data Sync: a simple example

Cross platform Data Sync: a simple example

Cross platform data sync can be simple: In this tutorial we will show you how you can easily sync data across devices.

Built for fast and effortless data access on and across embedded devices from Mobile to IoT, ObjectBox keeps data in sync between devices for you. The Database and Data Snyc works across platforms (iOS, Android, Linux, Rasbian, Windows, MacOS) and supports a variety of languages with easy native APIs (Swift, Java, Kotlin, C / C++, Flutter / Dart, Golang).

For example, you can sync between an Industrial IoT sensor app in Go and a C++ monitoring application – and a mobile Android app written in Kotlin or Java – and of course an iOS app written in Swift – and… you get the drift 😉

ObjectBox is a high-performance embedded database for Edge Computing with integrated Data Sync. The ObjectBox database is quick to set up and free and easy to use. Our powerful and intuitive APIs are a great match for multiplatform development environments.

Syncing data across devices – a task-list app example

In this tutorial, we are going to sync data across three instances of an example task-list app (written in C++, Go and Java).

With the task-list app, users can create simple text-based tasks and mark them as done. It stores tasks together with their creation dates. There is also a parameter to store the date when the task was completed. It acts as a filter for users to only see unfinished tasks. 

This app is a standard cross platform ObjectBox example that is available for all language bindings. Here are the repositories of each example app that we will be looking at today:

Overview of the example code 

In this section, we’ll quickly review how the the task-list example app uses ObjectBox Sync. For a more detailed description, check out the Sync docs. If you want to see how each of these steps were incorporated into the example code, go to the next section.

Note: The basic use of the database and its sync features is the same for all programming languages. If you haven’t used the ObjectBox DB yet, please refer to the corresponding documentation: C/C++ Docs, Java/Kotlin/Dart Docs, Go Docs, Swift Docs.

For sync to work in any app, we generally only need four things:

  1. The sync-enabled library — this is not the same as the general ObjectBox library and has to be downloaded separately.
  2. Database objects enabled for sync — for this we need include the sync annotation in the ObjectBox schema file.
  3. ObjectBox Sync Server — please apply for a free Sync Trial here to get your own copy of the Sync Server (available for Linux and Docker). Note that this will only have to be started once and in this tutorial we’ll show you how to run the server on Linux. If you are using Docker, follow the steps outlined here.
  4. Start a Sync Client in the app — as one can see from the Sync Client docs, creating and starting a sync client is just a matter of a couple of lines of code.

Important: When syncing between different apps, please make sure that the UIDs in the model JSON file (e.g. objectbox-default.json) are the same everywhere.

    How to run the examples

    Here you’ll find requirements and step-by-step guides for running the task-list example app in each of the three languages.

    C++ example app

    Requirements

    New to C++? Check out our beginner C++ ObjectBox installation tutorial.

    • WSL Ubuntu
    • CMake
    • Git
    • C++
    • Clang

      Step-by-step guide

      1.Start by creating a CMakelists.txt file:

      Now configure and build the project via CMake: Configure (Clang), CMake: Build.

      2. Sync-enabled objects: note the first line in tasklist.fbs.

      3. [if not running a server already] Start the ObjectBox Sync Server on Linux by running ./sync-server --model build/_deps/objectbox-src/examples/cpp-gen/objectbox-model.json --unsecured-no-authentication

      where sync-server is the path to your sync server executable. You can find more information about the server in the Sync Server docs.

      4. Sync Client: launch [objectbox-c-examples-cpp-gen-sync], and the Sync Client will start automatically. You can see how it was implemented in main.cpp.

      As this is just an example, we opted for no authentication to make things simple. This is not what you would use in production. We currently offer two authentication methods: shared secret and Google Sign-In. Here is the relevant Sync docs section on authentication options that explains how to use these.

      5. Let’s add a first task, called “task-cpp” (new task-cpp-1), to check if our C++ app syncs correctly. The output should look like this:

      Output of the C++ tasklist example app, showing a newly added task

      6. You can finally open the Admin UI to check if the task appears there. This is most easily done by opening http://127.0.0.1:9980/ in any web browser. For a more detailed description of what this can do, check out the Admin UI docs.

      Go example app

      Requirements

      • WSL Ubuntu
      • Go (see how to configure it for VS Code here)
      • Git

      Step-by-step guide

      1. First, clone the objectbox-go repository to your VS Code project. Make sure the current directory is objectbox-go.

      2. Sync-enabled objects. There are two versions of the task-list example: with and without sync. To run the one with sync, we need to enable our Task object for syncing. To do this, simply put the sync annotation on a new line in examples/tasks/internal/model/task.go:

      Then run the generator: go generate examples/tasks/internal/model/task.go to update the schema.

      3. [if not running a server already] Now start the ObjectBox Sync Server: ./sync-server --model=examples/tasks/internal/model/objectbox-model.json --unsecured-no-authentication,

      where sync-server is the path to your sync server file. You can find more information about the server in the Sync Server docs.

      4. Run go run examples/tasks/main.go. The Sync Client will start within the app; check main.go to see how this was implemented.

      As this is just an example, we opted for no authentication to make things simple. This is not what you would use in production. We currently offer two authentication methods: shared secret and Google Sign-In. Here is the relevant Sync docs section on authentication options that explains how to use these.

      5. Now we can add our first task (new task-go) – if it synced correctly, you should already see that from the output of the app. In particular, there will be a message from the change listener (“received 1 changes”):

      Output of the Go task-list example app after adding a first task

      6. Lastly, open the Admin UI to check if the task appears there. This is most easily done by opening http://127.0.0.1:9980/ in any web browser. For a more detailed description of what this can do, check out the Admin UI docs.

      Admin UI showing a task created with the Go example app

      Java (Android) example app

      Requirements

      • Java
      • Android Studio

      Step-by-step guide

        1. First of all, open Android Studio and clone the objectbox-examples repository via File → New → Project from Version Control. Use this URL: https://github.com/objectbox/objectbox-examples.git
        2. Sync-enabled objects: check out Task.java to see how this was done (note the @Sync annotation).
        3. [if not running a server already] Start the ObjectBox Sync Server

      ./sync-server --model android-app-sync/objectbox-models/default.json --unsecured-no-authentication,

      where sync-server is the path to your sync server file. You can find more information about the server in the Sync Server docs.

      1. Now you can run “android-app-sync” on a device of your choice. The Sync Client will start in the app. 

      As this is just an example, we opted for no authentication to make things simple. This is not what you would use in production. We currently offer two authentication methods: shared secret and Google Sign-In (only for Java, Kotlin, Dart, C & Go). Here is the relevant Sync docs section on authentication options that explains how to use these.

      5. Add a new task called “task-java”.

      6. Finally, open the Admin UI to check if the task appears there. This is most easily done by opening http://127.0.0.1:9980/ in any web browser. For a more detailed description of what this can do, check out the Admin UI docs.

      Next Steps

      How easy was that? cool Now that you’ve run your first ObjectBox Sync example, why not build something yourself? Use any combination of the supported languages to build your own cross platform app.

      We’re eager to see your use case examples! Don’t hesitate to share your results with us by posting on Social Media and tagging @objectbox_io, or simply sending us an email on contact[at]objectbox.io. 

       

      If you want to learn more about how ObjectBox can be used in IoT, here is an overview of different use cases

      ObjectBox Database Java 3.1 – Flex type

      ObjectBox Database Java 3.1 – Flex type

      We are happy to announce version 3.1 of ObjectBox for Java and Kotlin. The major feature of this version is the new Flex type. For a long time, ObjectBox worked on rigid data schemas, and we think that this is a good thing. Knowing what your data looks like is a feature – similar to programming languages that are statically typed. Fixed schemas make data handling more predictable and robust. Nevertheless, sometimes there are use cases which require flexible data structures. ObjectBox 3.1 allows exactly this.

      Flex properties

      Expanding on the string and flexible map support in 3.0.0, this release adds support for Flex properties where the type must not be known at compile time. To add a Flex property to an entity use Object in Java and Any? in Kotlin. Then at runtime store any of the supported types.

      For example, assume a customer entity with a tag property:

      Then set a String tag on one customer, and an Integer tag on another customer and just put them:

      When getting the customer from its box the original type is restored. For simplicity the below example just casts the tag to the expected type:

      A Flex property can be not justString or Integer. Supported types are all integers (Byte, Short, Integer, Long), floating point numbers (Float, Double), String and byte arrays.

      It can also hold a List<Object> or a Map<String, Object> of those types. Lists and maps can be nested.

      Behind the scenes Flex properties use a FlexBuffer converter to store the property value, so some limitations apply. See the FlexObjectConverter class documentation for details.

      Query for map keys and values

      If the Flex property contains integers or strings, or a list or map of those types, it’s also possible to do queries. For example, take this customer entity with a properties String to String map:

      Why is properties not of type Object? ObjectBox supports using Map<String, String> (or Map<String, Object>) directly and will still create a Flex property behind the scenes.

      Then put a customer with a premium property:

      To query for any customers that have a premium key in their properties map, use the containsElement condition:

      Or to only match customers where the map key has a specific value, here a specific premium tier, use the containsKeyValue condition:

      What’s next?

      ObjectBox database is free to use. Check out our docs and this video tutorial to get started today.

      We strive to bring joy to mobile developers and appreciate all kinds feedback, both positive and negative. You can always raise an issue on GitHub or post a question on Stackoverflow. Otherwise, star the ObjectBox  Java database GitHub repo and up-vote the features you’d like to see in the next release.

       

      Beginner C++ Database Tutorial: How to use ObjectBox

      Beginner C++ Database Tutorial: How to use ObjectBox

      Introduction

      As a direct follow up from the ObjectBox database installation tutorial, today we’ll code a simple C++ example app to show how the database can be used. Before starting to program, let’s briefly overview what we want to achieve with this tutorial and what is the best way to work through it.

      Overview of the app we want to build

      In short, we will make a console calculator app with an option to save results into memory. These will be stored as objects of the Number class. Every Number will also have an ID for easy reference in future calculations. Apart from the function to make calculations, we will create a function to enter memory. It will list all the database entries and have an option to clear memory. By coding all of this, we will make use of such standard ObjectBox operations as put, get, getAll and removeAll.

      Our program will consist of seven files: 

      • the FlatBuffers schema file, that defines the model of a class we want to store in the database
      • the header file, for class function definitions
      • the source file, for function implementation
      • the four files with objectbox binding code that will be created by objectbox-generator

      How to use this tutorial

      While looking at coding examples is useful in many cases, the best way to learn such a practical skill like programming is to solve problems independently. This is why we included an exercise for each step. You are encouraged to make the effort and do each of them, even if you don’t know the answer straight away. Only move to the next step after you test each part of your program and make sure that everything works as intended. Ideally, you should only use the code snippets presented here to check yourself or look for hints when you feel stuck. Bear in mind that sometimes there might be several different ways to achieve the same results. So if something that we ask you to do in this tutorial doesn’t work for you, try to come up with your own solution.

      How to create the FlatBuffers file?

      First, we’ll create the FlatBuffers schema (.fbs) for our app. This is required for the objectbox-generator to generate binding code that will allow us to use the ObjectBox library in our project. 

      The FlatBuffers schema consists of a table, which defines the object we want to store in the database, and the properties of this object. Each property consists of a name and a type. We want to keep our example very simple, so just two properties is enough.

      1. To replicate a calculator’s memory, we want ObjectBox to store some numbers. We can define the Number object by giving the table a corresponding name.
      2. Inside the table, we want to have two properties: id and contents. The contents of each Number object is the number itself (double), while id is an ulong that our program will assign to each of them for easy identification.

      Exercise: create a file called numbers.fbs and define the table in the format

      Reveal code

      Generating binding code

      Now that the FlatBuffers file is ready, we can generate the binding code. To do this, run the objectbox-generator for our FlatBuffers file:

      The following files will be generated:

      • objectbox-model.h
      • objectbox-model.json
      • numbers.obx.hpp
      • numbers.obx.cpp

      The header file

      This is where the main chunk of our code will be. It will contain the Calculator class and all the function definitions.

      1. Start by including the three ObjectBox header files: objectbox.hpp, objectbox-model.h and numbers.obx.hpp. Our whole program will be based on one class, called Calculator. It should only have two private members: Store and Box. Store is a reference to the database and will manage Boxes. Each Box stores objects of a particular class. In this example, we only need one Box. Let’s call it numberBox, as it will store Numbers that we want to save in the memory of our calculator.

      Exercise: create a file called calculator.hpp and define the Calculator class with two private members: reference to the obx library member Store and a Box of Numbers.

      Reveal code

      2. After the constructor, we define the run function. It will be responsible for the menu of our program. There should be two main options: to perform calculations and enter memory. As discussed above, we want this app to do two things: perform calculations and show memory. We’ll define these as separate functions, called Calculate and Memory. The first one is quite standard, so we won’t go into a detailed explanation here. The only thing you should keep in mind is that we need to account for the case when the user wants to  operate on a memory item. To deal with this, we’ll process input in a function called processInput.

      Exercise: define the parametrised constructor which takes a reference to Store as a parameter. Then define the run and Calculate functions.

      Reveal code

      3. The final part of this function is for saving results into memory. We start by asking the user if they want to do that. If the answer is positive, we create a new instance of Number and set the most recent result as a value of its contents. To save our object in the database, we can operate with put(object) on our Box. put is one of the standard ObjectBox operations, which is used for creating new objects and overwriting existing ones. 

      Exercise: create an option to store the result in memory, making use of the ObjectBox put operation.

      Reveal code

      4. Next, we should define processInput, which will read input as a string and check whether it has the right format. Now, to make it recognise the memory items, we have to come up with a standard format for these. Remember, we defined an ID property for our Numbers. Every number in our database has an ID, so we can refer to them as, e.g. m1, m2, m3 etc. To read the numbers from memory, we can make use of the get(obx_id) operation. It returns a unique pointer to the corresponding Number, whose contents we need to access and use as our operand.

      Exercise: define the processInput function, which detects when something like m1 was used as an operand and updates x, y, and op according to the input.

      Reveal code

      5. The last function in our header file will be Memory. It should list all the numbers contained in the database and have an option to clear data. We can read all the database entries by calling the getAll ObjectBox operator. It returns a vector of unique pointers. To clear memory, you can simply operate with removeAll on our Box.

      Exercise: define the Memory function, which lists all the memory items, and can delete all of them by request.

      Reveal code

      The source file

      To tie everything together, we create a source (.cpp) file. It should contain only the main function that initialises the objectbox model, creates an instance of the Calculator app, and runs it. To create the ObjectBox model, use

      then passing options as a parameter when you initialise the Store.

      Exercise: create the source file

      Reveal code

      Final notes

      Now you can finally compile and run your application. At this point, a good exercise would be to try and add some more functionality to this project. Check out the ObjectBox C++ documentation to learn more about the available operations.

      After you’ve mastered ObjectBox DB, why not try ObjectBox Sync? Here is another tutorial from us, showing how easily you can sync between different instances of your cross platform app.

      Other than that, if you spot any errors in this tutorial or if anything is unclear, please come back to us. We are happy to hear your thoughts.

      Beginner C++ tutorial: ObjectBox installation

      Beginner C++ tutorial: ObjectBox installation

      This ObjectBox beginner tutorial is for people who have limited knowledge of C++ development (no prior experience with external libraries is required). It will walk you through the installation process of all the development tools needed to get started with ObjectBox on Windows. By the way, ObjectBox is a database with intuitive native APIs, so it won’t take you long to start using it.

      Firstly, we will need to set up a Linux subsystem (WSL2) and install such tools as:

      • CMake, which will generate build files from the ObjectBox source code to work on Linux;
      • Git, which will download the source code from the ObjectBox repository.

      Then, we will install ObjectBox and run a simple example in Visual Studio Code.

      Windows Subsystem for Linux (WSL2)

      In this section, you will set up a simple Linux subsystem that you can use to build Objectbox in C++.

      1. Install WSL (Note: this requires a reboot; it also configures a limited HyperV that may cause issues with e.g. VirtualBox).
        Warning: to paste e.g. a password to the Ubuntu setup console window, right-click the title bar and select Edit → Paste. CTRL + V may not work.
      2. (optional, but recommended) install Windows Terminal from Microsoft Store and use Ubuntu from there (does not have the copy/paste issue, also supports terminal apps better).
      Windows Terminal in the Microsoft Store

      3. Within Windows Terminal, open Ubuntu by choosing it from the dropdown menu.

      Drop-down menu in Windows Terminal, through which a new tab for Ubuntu can be opened

      4. Get the latest packages and upgrade:

      5. Install build tools

      Install ObjectBox using CMake

      Now that you have WSL2 and all the packages, we can switch to VS Code and install ObjectBox with the help of CMake.

      1. In Ubuntu, create a new directory and then open it in Visual Studio Code:

      2. Install the following extensions:

      Extensions tab in Visual Studio Code, showing what needs to be installed in this tutorial: C/C++, CMake Tools and Remote - WSL

      3. Create a text file called CMakeLists.txt with the following code. It will tell CMake to get the ObjectBox source code from its Git repository and link the library to your project.

      4. Create a simple main.cpp file that will help us verify the setup:

      5. Follow this official guide for VS code and CMake to select Clang as the compiler, configure and build ObjectBox. As a result, .vscode and build folders will be generated. So your directory should now look like this:

      Explorer tab in Visual Studio Code, showing the two new folders that were generated after a successful build

      Running the tasks-list app example

      Finally, we can check that everything works and run a simple example.

      1. Click the “Select target to launch” button on the status bar and select “myapp” from the dropdown menu. Then launch it. You should see it output the correct version as in the screenshot.

      "Select launch target" menu in Visual Studio Code
      Output of main.cpp, verifying the version of ObjectBox used and demonstrating that the C++ build files were generated correctly.

      2. Before proceeding with the example, you need to download the most recent ObjectBox generator for Linux from releases. Then come back to the Windows Terminal and type

      to open the current directory in Windows Explorer. Copy the objectbox-generator file in there.

      3. Back in VS Code, you should now run the generator for the example code:

      If you get a “permission denied” error, try this to make the generator file executable for your user:

      4. Now choose objectbox-c-examples-tasks-cpp-gen as the target and run it. You should see the menu of a simple to-do list app as shown on the screenshot. It stores your tasks, together with their creation time and status. Try playing around with it and exploring the code of this example app to get a feel of how ObjectBox can be used.

      Output of the Objectbox C++ tasks-list app example showing its menu with available commands

      Note: if you see a sync error (e.g. Can not modify object of sync-enabled type “Task” because sync has not been activated for this store), please delete the first line from the tasklist.fbs file and run the objectbox generator once again. Or, if you want to try sync, apply for our Early Access Data Sync. There is a separate example (called objectbox-c-examples-tasks-cpp-gen-sync) that you can run after installing the Sync Server.