Small kotlin library for persisting _single instances_ of kotlin data classes

Overview

PerSista

license-apache2

central-0.1.2

api-16

circleci


Small library for persisting single instances of kotlin data classes.

NB: PerSista uses typeOf() internally which is marked as @ExperimentalStdlibApi so is subject to change (it is on its way to stabilization though, the target is for Kotlin 1.6)

Firstly, what PerSista is not:

  • it's not a database
  • it's not an ORM
  • it's not a generic persistent key-value store
  • it's not a replacement for Jetpack DataStore

Its only use is to persistently store single instances of kotlin data classes (so that they can be recreated after process death, for example) and it does that with the lowest setup cost possible.

It's currently being used in a clean architecture sample to persist app state in a domain module

Positives

  • pure Kotlin so can be used in non-android modules
  • coroutine based
  • very simple API
  • tiny (~150 lines of code)

Restrictions

  • can only save one instance per data class: write a second instance of the same class, it will just overwrite the previous one
  • not suitable for enormous data classes, hard limit of 2 GB per instance

How to get it

Copy the PerSista.kt class into your own app, or add this gradle line to your project (you'll also need mavenCentral() in your list of maven repos)

implementation("co.early.persista:persista:0.1.2")

How to use it

Construct PerSista with the folder you want your data saved to, for an android app you'll want to use Application.getFilesDir(). Typically you will share this single PerSista instance, just use some form of DI (manual or otherwise) to get it wherever it's needed.

val perSista = PerSista(application.filesDir)

Define your data class, mark it serializable (also don't forget to add the kotlin serialization plugin to gradle)

@Serializable
data class DashboardState(
    val dashboardId: Int = 0,
    val userName: String,
    val drivers: List = emptyList(),
    val error: Error? = null,
    @Transient val isUpdating: Boolean = false
)

@Serializable
data class Driver(
    val driverId: Int,
    val driverName: String,
    val powerLevel: Int,
    val lat: Int,
    val long: Int
)

Save an instance like this:

} ">
val state = DashboardState(dashboardId = 777, userName = "erdo")

// from inside a coroutine scope
perSista.write(state)

// from outside a coroutine scope
perSista.write(state){ savedState ->
}

Read an instance like this:

} ">
val defaultState = DashboardState(userName = "defaultUser")

// from inside a coroutine scope
val state = perSista.read(defaultState)

// from outside a coroutine scope
perSista.read(state){ readState ->
}

How it works

PerSista uses Kotlin's built in Serialization to turn data classes into json, then it writes that json as a text file on the file system.

Because it needs to accept any data class you might throw at it, it depends on typeOf() internally to work out the class type of the item - typeOf() is marked as @ExperimentalStdlibApi at the moment so it could potentially change in future.

It's up to you to ensure that you only pass something which is serializable to the write method. Because typeOf() is one of those things that behaves differently depending on which kotlin platform you are running on, I've taken a conservative approach, and in the worst case scenario when using PerSista if the write() fails: nothing will happen, if the read() fails: you will receive the default value. Pass a logger in to the constructor if you want to see more detail about what happened.

If you DO want to receive exceptions (during development for instance) pass 'strictMode = true' to the constructor, in that case, the only time you won't receive an exception when something goes wrong is when an attempted read fails because it can't find the file (that's most likely because a write was never performed in the first place).

PerSista prioritizes correctness over performance, all the reads and writes are guaranteed to be done sequentially as they are performed using a coroutine dispatcher created from a single-threaded Executor (though you can overide that if you want)

file storage location

You can browse the files that are saved on an emulator using Device Explorer in Android Studio (View -> Windows -> Device File Explorer), for an android device they will typically be in: data/data/your.app.package/files/persista

Performance

Performance is "ok", and nothing is run on the UI thread anyway. On a 2017 Pixel XL, PerSista seems to have a setup overhead of around 400ms the first time it writes, after that a small instance of a data class takes around 2-30ms to store.

Versioning

PerSista has no concept of data class versioning, so if you change the definition of your data class (due to an app upgrade for instance) and kotlin serialization is no longer able to decode the old json to the new data class, you will just get your default value back

Obfuscation

The state of your data class will be saved in a file named after the data class's qualified name e.g. "foo.bar.app.DashboardState". If that data class gets obfuscated however, the qualified name could easily be renamed to something like "a.b.c.a". If you release a new version of the app, this time the qualified name could be renamed to "b.c.d.d" and PerSista won't be able to find the data from your previous installation. Check the example app's proguard configuration for details.

Permissions

None required on Android, as long as you stick to the internal app directory: Application.getFilesDir()

Example App

example app screenshot

There is a complete mini example android app in this repo if you need something to copy-paste to get started.

Anything else

The non-suspend APIs invoke a functional parameter when they are complete, if you are using PerSista on Android this will arrive on the main thread. For non UI kotlin platforms (which don't have a Dispatchers.Main) you should override the mainDispatcher parameter in the constructor to an available dispatcher of your choice.

License

Copyright 2015-2021 early.co

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
You might also like...
DS-for-Kotlin - Some classic data sturctures write in kotlin for fun

DS-for-Kotlin Just write some classic data structure by kotlin during my leisure

Algorithms and data structures in Kotlin.
Algorithms and data structures in Kotlin.

Here you can find the most common algorithms and data structures written in Kotlin. The goal of this project is to create the most eloquent implementa

Multi-thread ZX0 data compressor in Kotlin

ZX0-Kotlin ZX0-Kotlin is a multi-thread implementation of the ZX0 data compressor in Kotlin. Requirements To run this compressor, you must have instal

Data structures in kotlin that maintain order

Ordered Data Structures I came from C++ and fell in love with kotlin. I used the C++ stdlib a lot. I have really been wanted to reach for map and unor

FirestoreCleanArchitectureApp is an app built with Kotlin and Firestore that displays data in real-time using the MVVM Architecture Pattern. For the UI it uses Jetpack Compose,  Android's modern toolkit for building native UI.
FirestoreCleanArchitectureApp is an app built with Kotlin and Firestore that displays data in real-time using the MVVM Architecture Pattern. For the UI it uses Jetpack Compose, Android's modern toolkit for building native UI.

FirestoreCleanArchitectureApp FirestoreCleanArchitectureApp is an app built with Kotlin and Cloud Firestore that displays data in real-time using Andr

Solution code for Android Kotlin Fundamentals Codelab 8.1 Getting data from the internet

MarsRealEstateNetwork - Solution Code Solution code for Android Kotlin Fundamentals Codelab 8.1 Getting data from the internet Introduction MarsRealEs

KotlinForDS - An exploration of data science using Kotlin

Kotlin Jupyter Notebook An example notebook can be found here: https://mybinder.

Android Data Managment System Android UI - Kotlin- Firebase
Android Data Managment System Android UI - Kotlin- Firebase

DataManagmentSystem Data Managment System Android UI - Kotlin- Firebase Android Data Managment System App Design And Kotlin with Firebase The project

Kotlin cli maven spring failsafe findbugs cucumber mockito junit car data

Kotlin cli maven spring failsafe findbugs cucumber mockito junit car data

Owner
Eric Donovan
Eric Donovan
Clean MVVM with eliminating the usage of context from view models by introducing hilt for DI and sealed classes for displaying Errors in views using shared flows (one time event), and Stateflow for data

Clean ViewModel with Sealed Classes Following are the purposes of this repo Showing how you can remove the need of context in ViewModels. I. By using

Kashif Mehmood 22 Oct 26, 2022
🎲 Kotlin Symbol Processor to auto-generate extensive sealed classes and interfaces for Android and Kotlin.

SealedX ?? Kotlin Symbol Processor to auto-generate extensive sealed classes and interfaces for Android and Kotlin. Why SealedX? SealedX generates ext

Jaewoong Eum 236 Nov 30, 2022
Ksp-di-library - Small library for DI in KMM apps

DI-KSP Small library for DI in KMM apps. Uses KSP for processing DI annotations:

Anna Zharkova 3 Feb 6, 2022
Demo Spting REST Service on Kotlin. Works with PostgreSQL via Spring Data. Data initialization provided by liquibase

Spring Boot REST API with Kotlin Spring Boot REST API service. Spring Data with PostgreSQL. Data initialization with Liquibase. Swagger UI Reference D

null 0 Jun 10, 2022
Detailing about the data provided (Data Visualization Application)

Detailing about the data provided (Data Visualization Application): • In the application, the data provided in the CSV is used for the Scatter plot cr

Neha Sharma 0 Nov 20, 2021
Use Android Data Binding wih Live Data to glue View Model and Android

Gruop-C Spliff Summary Use Android Data Binding wih Live Data to glue View Model and Android. Asynchronous communications implemented with KotlinX Cor

null 2 Nov 21, 2021
A small backend for the Thinkrchive app written in Kotlin with Ktor

A small backend for the Thinkrchive app written in Kotlin with Ktor. It uses Postgresql with a few requests and JWT authentication for admins.

Thinkrchive 6 Dec 12, 2022
A small application for working with the Github API, made as a practical task. GeekBrains, course of study: Popular libraries: RxJava 2, Dagger 2, Moxie.

GeekBrains_Course_AndroidOnKotlin_HW_My_Movie Домашнее задание к занятию №2-6 Студента GeekBrains Веремеенко Дмитрия Факультет: Android-разработки Кур

Dmitriy 3 Aug 24, 2021
Chain Relations is a small casual existential game about life, human needs and long-term relations.

Chain Relations Chain Relations is a small casual existential game about life, human needs and long-term relations. ChainRelations.360p.mp4 Game objec

Andrzej Novosiolov 4 Dec 2, 2022
A library that extends the existing JDBC API so that data objects can be used as input (to set parameters) and output (from ResultSet's rows).

SqlObjectMapper This is a library that extends the existing JDBC API so that data objects can be used as input (to set parameters) and output (from Re

Qualified Cactus 2 Nov 7, 2022