The code for this example is available in the
Trails repository.
Prerequisites
Quick Links to Prerequisites:
- Kotlin Multiplatform Project Setup: Basic understanding of KMP and a project set up with shared
commonMaincode. - Coroutines and Flow: Basic understanding of Kotlin Coroutines and Flow for asynchronous programming.
- Gradle: Familiarity with Gradle for dependency management.
Installation
1
Add the Dependency
Add the Store library to your project’s dependencies. Since we’re working with a KMP project, we’ll add the dependency to the
commonMain source set.libs.versions.toml
build.gradle.kts
2
Sync the Project
After adding the dependency, sync your project with Gradle to download the Store library.
Building a Store
Now, let’s build a simple Store to fetch and cache posts from our API and cache it for offline access.1
Define the Data Models
Define the models for a post.
Using SqlDelight for Local Database Models:The SQL schema defined using SqlDelight will generate Kotlin models for you. Here’s how the For more details on how SqlDelight generates Kotlin classes from SQL schemas, check out the SqlDelight Docs.
PostEntity class might look after generation:2
Create the API Interface
Define and implement an interface for your network calls. In this example, we’ll use Ktor for HTTP requests.
Dependency Injection Context:Trails uses kotlin-inject, a compile-time dependency injection library for Kotlin. The
@Inject annotation indicates a class can be injected.3
Implement Converters
We need to convert between our network model, domain model, and local database model.
4
Set Up the Store Factory
We’ll use a factory for creating a
PostStore instance.About the
TODO() Placeholders:The TODO() placeholders indicate where implementations will be provided in the subsequent steps.5
Implement the Fetcher
Our Fetcher will interact with the network data source using the
PostOperations interface.6
Implement the Source of Truth
Our Source of Truth will delegate to a local SqlDelight database.
7
Implement the Converter
Our Converter will convert between our network model, local database model, and domain model using the
PostExtensions object.Understanding the Converter’s Role:The Converter bridges the gap between the network model, local database model, and domain model within the Store.While you have extension functions for conversions, the Converter integrates these into the Store’s pipeline, ensuring data flows correctly through each layer.
8
Implement the Updater
Our Updater will make a network call to update the post.
9
Implement the Bookkeeper
Our Bookkeeper keeps track of failed syncs to enable eagerly resolving conflicts after local mutations.
10
Build the Store
Provide the implementations to the Store Builder.
How Components Work Together:Each component of the Store has a specific role:
Fetcher: Retrieves data from the network.Source of Truth: Manages local data storage.Converter: Handles data transformations between models.Updater: Syncs local changes back to the network.Bookkeeper: Keeps track of failed updates for retry mechanisms.
Using the Store
Now, let’s use the Store to fetch and cache post data for the Trails post detail screen.1
Create a Post Repository
We’ll create a
PostRepository that uses the PostStore to fetch and cache post data. The primary reason for this extra layer is it enables us to extract Store from the domain layer as an implementation detail of the PostRepository. It also enables us to add additional methods and strategies to the PostRepository in the future.A market is a composition of stores and systems enabling exchange between
consumers and providers.
2
3
Implement the Post Detail Presenter
A Circuit Presenter is intended to be the business logic for a screen’s UI and a translation layer in front of the data layer. Our
PostDetailScreenPresenter will use the PostRepository to load the post data and update the UI in response to user actions.4
Display the Post Detail Screen