• Skip to main content
  • Skip to primary sidebar

Phelela

  • Phelela
  • Build with me
  • Engineering Notes
  • Beyond Code
  • My GitHub

Phase 2 — Teaching My Weather App to Survive a Bad Connection

23. September 2026

At the end of Phase 1, my Kotlin Multiplatform weather app could fetch weather data from Meandair and put it on the screen.

Which was great — as long as everything worked.

Phase 2 was about all the moments when it doesn’t.

No internet. A slow request. An API that doesn’t respond. Cached weather that is already too old to be useful.

Suddenly, “fetch data and display it” wasn’t enough.

The app needed a memory

The first big change was adding SQLDelight.

Until then, weather data mostly lived as long as the request that fetched it. I wanted the app to keep useful data around, so losing the network wouldn’t immediately mean losing the weather too.

Adding the database itself wasn’t the difficult part.

The difficult part was deciding when to trust it.

Weather is a particularly annoying thing to cache because yesterday’s photo is still yesterday’s photo. Yesterday’s temperature pretending to be today’s temperature is a problem.

So the repository had to start answering questions I didn’t really have in Phase 1:

Is the cached data still fresh?

Should I show saved data immediately?

When should I go back to the network?

What if that refresh fails?

And if I have old data but no internet, is old data better than nothing?

I settled on a 5-minute freshness window. The app can show saved weather immediately, fetch an update when that cache has expired, and keep the saved data visible if the update fails. A manual refresh always tries the network first.

That became an important distinction in the app:

“I couldn’t get fresh data” is not the same as “I have no data.”

Then I started breaking things

Once the cache was working, I spent a lot more time thinking about failure.

A timeout isn’t the same as having no connection. An authentication problem isn’t something that will magically disappear if I retry it three times. And retrying every failed request forever is definitely not a strategy.

So I added explicit timeouts, limited retries where they actually make sense, and fallback to cached data when a refresh fails.

I also ran into a concurrency problem I hadn’t thought much about beforehand.

Two parts of the app could ask for a refresh at almost the same time. Without coordination, that could mean unnecessary duplicate requests. The repository now coordinates those refreshes so callers can reuse work that has already succeeded.

It’s one of those implementation details that nobody will ever notice when using the app.

Which is kind of the point.

A successful request can still be wrong

My favorite bug from this phase had nothing to do with the weather API.

The Android map loaded successfully.

The tiles downloaded.

Nothing crashed.

And the entire map said:

“API KEY REQUIRED.”

Technically, quite a lot had gone right.

From the user’s perspective, the result was obviously broken.

The tile provider required an API key, so I eventually switched the Android preview to OpenStreetMap. While fixing it, I also found that the square map tiles were being stretched into a rectangular container, so the proportions and framing needed some attention too.

It was a nice reminder that a successful request doesn’t necessarily mean the app did the right thing.

The boring things that become important later

Phase 2 also included some work that isn’t particularly exciting to show in a blog post: logging, configuration, the beginnings of analytics, and a lot more testing around synchronization and recovery.

Analytics is still just groundwork: typed events and an injectable sink, with no external provider connected yet.

But I’m glad these pieces are there.

When something fails now, I have a much better chance of understanding why.

And the data-layer tests aren’t only checking whether a successful API call returns data anymore. They also cover things like offline behavior, failed refreshes, retries, cancellation, and overlapping requests.

That feels like a much more realistic definition of “working.”

And yes, I changed the UI too

After spending several weeks thinking about databases, cache expiration, retries, and synchronization, moving a few pixels around was surprisingly therapeutic.

So the app also got a visual cleanup during this phase.

The UI wasn’t really the point of Phase 2, but I like that the app is slowly becoming more polished on the outside while getting less fragile underneath.

Phase 2, done

My original goal for this phase was to build a stable data layer with persistence, offline support, and proper error handling.

Technically, that’s what I did.

But the more useful lesson was slightly different.

An offline-first app isn’t just an app with a database.

It’s an app that has decided what to do when the network disappears, when its data gets old, when refreshing fails, and when the answer it gets back isn’t actually useful.

Phase 1 got the app talking to the outside world.

Phase 2 taught it not to panic when the outside world stops answering.

in Build with me

Reader Interactions

you may also like
Building a Kotlin Multiplatform Weather App at Meandair — Phase 1: Laying the Foundation
Software Engineering for One — How to Start as a Solo Developer
Designing a Weather App: What I Learned Before Writing a Single Line of Code
Why I Rebuilt My Sudoku App: A Developer’s Refactoring Journey

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Primary Sidebar

Katarína Švábik

Hi, I’m Katarina. I’m a mobile developer working with Android and iOS, and this blog is where I share things I learn, test, or find interesting in the world of mobile development.

I enjoy clear explanations, practical insights, and topics that make me think — a mix I try to bring into my writing as well.

Outside of tech, I like Sudoku, running, and reading thrillers and detective stories. I also cook and bake gluten-free, because I’m celiac, and experimenting in the kitchen has become part of my routine.

Welcome — hope you find something here that’s useful or inspiring.

My life in pictures

My kind of offline mode.
Old but still stealing the spotlight.
Chaos of ideas, order of pillows.
Where the world goes quiet for a moment.
Because every good day starts with a cookie.
2025 © Phelela
theme by soleilflare