Building a digital wallet Android app in Kotlin - Session 1

This post is part of a series where I build a digital wallet app for Android in Kotlin. This is a learning exercise suggested to me by my Android mentor. Over the coming months I’ll share my progress and my learnings, in addition to any feedback I received in order to iterate and improve on my code. I hope others can learn from what I learned as part of this experiment.

Requirements

The task is to build a digital wallet app; the task itself is a coding exercise that a real company requires from its software engineering interview candidates. The concept is fairly simple.

The app has two screens:

  • The first screen lists the accounts for the user and the account totals (think of it as an ‘accounts summary’ screen) where a list of accounts is grouped by institution and multiple accounts can be displayed per institution in different currencies.
  • The second screen lists the transactions for the account (think of it as an ‘accounts detail’ screen). Transactions are grouped by month in descending order as you’d expect from any banking app.

Stories (cards):

  • The requirements state that there is one Data Story and several User Stories. For the purposes of this activity I’ll be aiming to complete every story. It seems that for interview purposes a real candidate only has to do some of these stories.
  • I’ll be explaining each User Story as I complete them in future posts; essentially they each require some new piece of functionality to be built.
  • The Data Story that I worked through in this post is as follows:
    • Load accounts into the main accounts screen using data from a bundled JSON file; do this in a way so the user is not waiting (blocked) while the data is loading (the JSON file may be very large).
    • The returned accounts data can optionally be stored in memory or in a data store of some kind. Load it from its source upon launching the app.

My design & approach

Story 1: Data Story

For story 1 - the data story, it seems there is no UI work. When the description says 3 JSON payloads are included in the bundle, I’m guessing this means they are included as a resource (in app/res). Reading the Android resource docs shows me that convention is to keep these in a subdirectory called raw which is an Android Resource Directory. I’ve now added these files.

Clearly, the obvious hint from this story’s requirements is loading the file in a way that doesn’t block for the user; some kind of async/background task.

Async approach

From my understanding to date, there are many ways to do this: Kotlin Coroutines, RxJava, callbacks, and what I can see in about 15 chapters ahead of where I am now in the BNR book - WorkManager (a Jetpack library for background tasks)

My gut tells me:

  • RxJava is overkill for this task, and it takes a while to learn
  • Coroutines is widely talked about online and at conferences, it may be easy to do for this task, however…
  • I am learning towards using new Jetpack components wherever possible since this is the new Google way of life.

Hence, I’m going to try to use WorkManager for this story to async load the JSON files even though I have no idea how WorkManager works yet, I’ll do a little reading and play around with it here.

Data models

The other thing is that I’ll need to create some models to load that file into, some Kotlin data classes, and I’ll need to deserialise that somehow into those models.

If we were writing tests for this story, I’m guessing we’d have some interface and we could load a sample JSON file from in place of the real one (not relevant yet).

I’ll do a manual test with an enormous JSON file to see what happens too. I’m therefore going to just make a basic UI with 1 button that loads this file and displays it in a text view, so I can see the end result.

Parsing & deserialising

The other point in this story as per the requirements is to decide whether to load the deserialised data into memory or store the data. As this project is about learning, I think it’s important to explore storing the data, but not just yet. Clearly, they are looking for how you decide what the best form of storage is, if you choose to store the data, and why.

I have an understanding on how to use Room to store this in some kind of local DB, however, I don’t know if that’s the best approach to use a local DB on device; not sure of other options yet.

I would like to split this part of the requirements into a separate “Data Story 2” after I’ve finished some User Stories

Session 1: Saturday, 8 Feb 2020

Doing some reading on WorkManager, I need 3 main parts: a Worker class where I’ll have my background long-running task, a WorkRequest to execute the task, optionally, some constraints, and and then I need to call .enqueue on the singleton to call it.

The other thing I’m thinking is that I should load this into memory only once inside a ViewModel so it doesn’t need to get called again on a configuration change.

In terms of parsing the JSON and deserialising, there are a few libraries for this but I prefer not bringing in a library if I don’t have to. I came across one post that talks about just using org.json.JSONObject (included in stdlib) to parse the JSON. Then I can create my data structures.

My Dev Results

Parsing the JSON with just JSONObject got messier than I expected, because I also need to start with an InputStream and BufferedReader and it just felt verbose for what I want. I decided to use a simple popular library called Klaxon to parse the JSON.

I ended up being able to output JSON on-screen in a textView from a background task with WorkManager.

I’m using a LiveData observer on my WorkManager to observe whether the background task is complete, and only then I load my parsed Accounts list which I can now use in the Activity (or preferably, ViewModel - TODO).

Right now this cut does need some refactoring - of course in Prod I’m never going to make properties on classes public, as I have done in AccountsInMemoryStore.accounts, which by the way, is also a terrible name for a class. Also, I know I should put my Strings into resources rather than keeping them as raw text. Also, I know I should encapsulate what I have currently in MainActivity.onCreate which has a lot of responsibilities at once right now.

Overall WorkManager felt a bit painful to use because I can’t just pass any result down to use elsewhere; WorkManager requires you to store results in something called a Data object, and a Data object is not able to use custom objects - it only allows Int, Double, String, Float, Boolean, Byte and a couple of others I think, but I can’t just pass my custom object as a result into Data, which kind of sucks. And using a ByteStream feels a bit ugly. Anyway, the workaround is to save my result in a singleton called AccountsInMemoryStore which I can then access later after I know the result is successful, thereby not having to use this Data object to save my result at all. I really don’t get why WorkManager is recommended by Google in the Android documentation if it’s so limited.

Part 2: See Part 2 of Building a digital wallet Android app in Kotlin.