Reshta iOS App

Reshta iOS App

What Is Reshta?

Download on the App Store

reshta.app

Reshta is a secure iOS app for manually tracking your money.
I built it from scratch. Alone.

Reshta (Решта) is a 🇺🇦 Ukrainian word for “change” – the money a seller gives back when you pay more than the price.

Who Is Reshta For?

Designed for people who want simple and private control over their finances.

Reshta makes manual tracking as quick and simple as possible, with all categories displayed on one screen and intuitive drag-and-drop transaction recording.

It is a deliberately simple, privacy-focused alternative to automated finance apps.

You're very welcome to try it!

How Reshta Makes Manual Money Tracking Simple

1. All categories (cards) are on one screen.

  • Income Sources (e.g. Salary)
    The first row. Reset to zero on the first day of each month.
  • Accounts (e.g. Wallet)
    The second row. Do not reset.
  • Spending Categories (e.g. Food)
    The remaining space. Reset to zero on the first day of each month.

All rows are horizontally scrollable, so you can add as many cards to each category as you want.

2. Transaction Recording with Drag and Drop.

For recording transactions, simply drag and drop cards on the dashboard, just like moving icons on the iPhone Home Screen.

For example, to record a food expense paid from a wallet, drag the Wallet card onto the Food card, enter the amount, and tap the checkmark. As simple as that.

Other Features

  • Edit any card or transaction.
  • Reorder cards however you want.
  • Choose an icon for each card from a catalog.
  • View transactions grouped by day inside each card.
  • Add descriptions and tags to transactions. The date and time are recorded automatically but can be edited manually.
  • Support for 43 currencies, with automatic conversion and manual adjustment.
  • Exclude a card from the total balance calculation.
  • Hide cents if you're rich and they don't matter to you :)
  • Hide the Income row to make room for an additional row of Spending Category cards.
  • Choose between Dark, Light, and System themes.
  • Statistics with line charts for income and expenses by day, week, month, quarter, or year.
  • Delete or archive cards. Deleting also removes related transactions. Archiving preserves transactions and allows you to restore the card later.
  • Export all your data as a CSV file.
  • Completely delete your account and all your data from the server.

Subscription

The app is free, and you can create up to 5 cards in each category (Income Sources, Accounts, and Spending Categories). If you need more cards, a subscription removes this limitation.

There are two options:

  • Monthly Plan – $1.99
  • Annual Plan – $11.99 (50% OFF)

Family Sharing is included, so you can share it with your family without paying for multiple subscriptions.

The goal of the subscription is simply to cover the cost of the AWS infrastructure or, at least, help keep it low.

Development

Disclaimer

Yes, I used AI. BUT I didn't "vibe-code" the app, and I can explain every single line of code in the project.

I'm primarily a backend developer, and Reshta is my first iOS app. Before this project, I hadn't written a single line of Swift. So, I learned Swift and iOS development by building Reshta, using Paul Hudson's lessons and AI as an additional explainer and reviewer.

My progress on Reshta.
My progress on Reshta.

Project Management

I used a simple Kanban board. Nothing fancy, it just works for me.

Trello with a real photo of the Moon in the background.
Trello with a real photo of the Moon in the background.

Backend

I built the backend with FastAPI.
When I started the project, I was working at a company where FastAPI was the primary framework, so I chose it simply because I didn't want to constantly switch between frameworks.

Some Details:

  • Layered architecture. Three feature modules (user, family, reshta), each with five layers:

    • api – routes only.
    • service – stateless business logic.
    • dao – queries and eager loading.
    • model – ORM models.
    • dto – Pydantic input/output schemas.
  • Async from end to end.

    • FastAPI + SQLAlchemy 2.0 async ORM with asyncpg
    • redis.asyncio
    • aiohttp
  • PostgreSQL as the primary data store.

  • Redis for user sessions, tokens, and caching.

  • Granian as the ASGI server.

  • msgspec for JSON serialization.

  • Rate limiting with slowapi.

  • Materialized and repairable balances.

    • Account balances are updated whenever a transaction is created, edited, or deleted.
    • A dedicated endpoint can rebuild them from the transaction history.
  • Self-scheduling currency updates. A background asyncio task refreshes 43 exchange rates from an external API every 2 hours.

  • Mailgun API is used for verification and password-reset emails, sent via aiohttp as FastAPI's BackgroundTasks.

AWS

The EC2 instance sits in a public subnet, but there are no inbound rules. All inbound traffic goes through a Cloudflare Tunnel, which makes my life much easier, especially when it comes to TLS certificates. RDS (PostgreSQL) and ElastiCache (Valkey, Redis-compatible but cheaper) stay in a private subnet, where they are accessible only from the EC2 instance's security group.

Automated Deployment on Push to main

Job 1 – build
  1. Checkout the repository (actions/checkout@v5).
  2. Log in to GHCR using the automatic GITHUB_TOKEN.
  3. Build the image from the Dockerfile with two tags: :<GITHUB_SHA> (the tag that is deployed, and makes rollback possible) and :latest (the fallback).
  4. Push both tags to GHCR.
Job 2 – deploy
  1. Get AWS credentials using OpenID Connect (OIDC). No long-lived keys.
  2. Send the script execution command with SSM: bash /opt/reshta/deploy.sh $GITHUB_SHA.
  3. Wait for completion in a loop (maximum 6 attempts). Each wait has a time limit. break on success.
  4. Get the output: prints StandardOutputContent, then StandardErrorContent.
  5. Check the Status. If it is not Success, the job stops with exit code 1.
On the server – deploy.sh
  1. Enable strict mode set -euo pipefail: exit on a failed command, an unset variable, or a failure inside a pipe.
  2. Go to the /opt/reshta directory.
  3. Read the commit SHA from the first argument (the image tag to deploy).
  4. Set the permissions mask so all new files are mode 600 (owner only).
  5. Create a temporary file with a random name next to .env, and remove it when the script exits.
  6. Get the environment variables from SSM Parameter Store and write them to the temporary file.
  7. Append the image tag GITHUB_SHA=<sha> to the temporary file.
  8. Replace .env with the temporary file atomically. If step 15 fails, the previous known-good .env stays unchanged.
  9. Pull the image.
  10. Start the container. Compose recreates reshta_server because the image changed. The container applies the Alembic migrations, then starts the server.
  11. Clean up by removing unused images more than 7 days old.

iOS – Swift, SwiftUI, and a Pinch of UIKit

In this part, I don't want to dive into the technical details and would rather talk about the personal side of development.

As I mentioned before, Reshta is my first iOS project. Before starting it, I had zero experience with Swift, SwiftUI, or UIKit.

So I decided to trick myself. I started building the backend without even looking into how difficult iOS development might be. My strategy was: once I had invested enough time in the backend, I wouldn't have much choice but to learn iOS and finish the project. It was a bit naive, but it did work.

Of course, I had some frustration at the beginning. For example, at one point I didn't even know where the terminal was in Xcode (it doesn't have a termial, LOL). But I knew that this was just a normal part of climbing the learning curve. Keep calm and carry on.

After about two weeks of researching and poking around with Swift, I felt ready to start building.

I used the MVVM design pattern just because it seemed well-separated.

At first, I wanted to create all the UI elements myself, with custom colors and designs. But I quickly realized that I needed to compromise and use Apple's built-in components where they made sense. For example, using Apple's standard colors made adding Dark Mode almost effortless.

I learned how to use Universal Links and Apple App Site Association (AASA), which allowed me to implement automatic login after email confirmation during registration and email changes.

I'm still very excited about @Observable. The way changing a property automatically updates every View that uses it. Love it.

Overall, I'd say the development process was surprisingly smooth for my first iOS project, except for two parts:

  • The first was the drag-and-drop logic. I had to dig a bit into UIKit, which was something I wanted to avoid.
  • The second was the line chart on the Statistics page. Getting it to work smoothly with a full year of daily data was tricky.

But yeah, I did it. And it was fun. I'll probably build something for iOS again.

Release 1.0 🎉
Release 1.0 🎉

Design

Despite having a degree in Graphic Design, designing the app and creating all the visual assets was the least fun part for me.

I didn't use AI for this part. I tried, but it didn't work out well, so I decided I could do a better job myself.

I gathered some references for inspiration.
I gathered some references for inspiration.

The App Store screenshots were also made from scratch.
The App Store screenshots were also made from scratch.

I also created all the pictures in this post myself. I even drew the hand for one of them.
I also created all the pictures in this post myself. I even drew the hand for one of them.

Reshta LinkedIn cover image (dark mode).
Reshta LinkedIn cover image (dark mode).

I think that's it

This project taught me many things, but most importantly, it showed me that anything can be done if you dive into it without overthinking how difficult it's going to be.

It was a lot of fun, and I'll keep learning Swift/SwiftUI and extending Reshta's functionality.