Engineering

Designing for Low-Latency Compliance in Shariah Signal

How to handle slow third-party API delays using skeleton states and optimistic updates for instant financial screening.

Amin Shekh · October 28, 2026
Share

When a user enters a stock ticker into Shariah Signal, they expect an immediate verdict. However, the technical reality of financial screening is rarely instantaneous. We rely on third-party APIs to fetch balance sheets, income statements, and business activity breakdowns. These external requests are subject to network congestion, server-side processing delays, and rate-limiting throttles. If the app simply displays a spinning loader until every byte arrives, the user perceives the app as broken or slow. To solve this, we implement a design pattern known as Low-Latency Compliance, which uses UI skeleton states and optimistic UI updates to bridge the gap between a request and a verified result.

The Problem with Synchronous Expectations

In financial apps, the stakes for accuracy are high. Unlike a social media feed where a delayed post is a minor inconvenience, a delayed or incorrect Shariah compliance verdict can impact a user’s investment decisions. The instinctual engineering response is to make everything synchronous: wait for the data, validate it, and then show it. But in a mobile environment where users might be on 3G or spotty Wi-Fi, a three-second API roundtrip feels like an eternity.

We categorise our data fetches into three latency buckets:

  1. 1.Local Cache (0-50ms): Tickers already screened and stored in our SQLite database.
  2. 2.Edge Cache (100-300ms): Recently updated tickers stored on our CDN.
  3. 3.Cold Fetch (800ms-3000ms): New or expired data requiring a full scrape and calculation.

Designing for low-latency compliance means ensuring the UI behaves identically across all three buckets, even when the underlying data source is struggling.

Implementing Skeleton States That Communicate Intent

Skeleton states (or "shimmer" effects) are more than just placeholders; they are instructional. In Shariah Signal, we don't just show a grey box. We mirror the exact layout of the final verdict screen. This reduces cognitive load because the user’s eyes can rest where the information will eventually appear.

We use Jetpack Compose to build these skeletons. By creating a reusable ShimmerWrapper component, we can swap the actual UI components for their skeleton counterparts during the Loading state of our ViewModel. The key is to match the line height and corner radius of the actual cards. If the skeleton is a sharp-edged rectangle but the final card has a 12dp corner radius, the transition will cause a visual "jump" that feels unpolished.

The Role of Optimistic Updates

Optimistic UI is the practice of updating the interface as if an action was successful before the server confirms it. While this is common in "Like" buttons on social media, it is trickier in a compliance context. We cannot "optimistically" tell a user a stock is Halal if we don't know yet.

Instead, we apply optimistic updates to the navigation and metadata. When a user taps a stock, we immediately transition to the detail view and populate the ticker symbol and company name from the local search index. This gives the user the feeling of instant movement. The specific Shariah ratios—debt-to-market cap, interest-encumbered income—remain in a skeleton state until the API returns.

This separation of "Known Data" (Ticker, Name, Sector) and "Derived Data" (Compliance Status, Ratios) is essential. It allows the app to feel fast without sacrificing the integrity of the financial advice.

Technical Breakdown: Handling Asynchronous States

To manage this, we use a ScreeningState sealed class in our Kotlin ViewModels. This allows us to track exactly where we are in the data lifecycle without messy boolean flags.

StateUI ActionData Source
IdleShow Search BarNone
SearchingShow SuggestionsLocal SQLite
LoadingMetadataTransition to Detail / Show SkeletonLocal + API
CalculatingShow Skeleton RatiosAPI Response Processing
SuccessRender Ratios & VerdictFully Parsed Data
ErrorShow Retry StateNetwork/Timeout

By breaking the loading process into metadata and calculation phases, we can start rendering the UI before the heavy financial math is finished. For a deeper look at how we manage the local side of this, see our article on Optimizing SQLite for Speed.

Dealing with API Timeout and Stale Data

Financial data has an expiration date. A stock that was compliant last quarter might fail today if its debt levels have risen. When the API is slow, we face a choice: show old data or show nothing.

We implemented a "Stale-While-Revalidate" (SWR) pattern. If we have data in the local cache that is less than 24 hours old, we show it immediately but keep a small "Refreshing..." indicator visible. If the data is older than our compliance threshold, we force the skeleton state. This ensures that the user never acts on truly outdated information while still rewarding them with speed when the data is relatively fresh.

Consistency Across the Studio

The lessons learned here aren't limited to finance. In Salah Companion, we use similar logic for calculating prayer times based on GPS coordinates. The app doesn't wait for a perfect GPS lock to show the UI; it uses the last known location to provide an instant estimate, then smoothly updates the times once the high-accuracy coordinates arrive. In both apps, the goal is the same: eliminate the "white screen of death" and keep the user in the flow.

Checklist for Low-Latency UI

When building features that rely on external APIs, we follow this checklist to ensure the experience remains snappy:

  • Match Dimensions: Do your skeleton placeholders match the exact pixel height and width of the final loaded content?
  • Partial Hydration: Can you show the title and header immediately using local data while the body loads?
  • Transition Timing: Use a 200-300ms fade-in when the real data arrives to prevent a jarring visual snap.
  • Failure States: Does the UI have a clear "Retry" button that appears if the API takes longer than 10 seconds?
  • Throttling: If a user is rapidly switching between tickers, are you cancelling the previous API calls to save bandwidth?

Designing for the "Worst Case" Connection

We often test Shariah Signal using the "Network Link Conditioner" to simulate a 3G connection with 2% packet loss. This is where the skeleton state strategy truly shines. On a perfect office connection, the skeleton is visible for only 100ms—barely a flicker. On a poor connection, the skeleton serves as a reassuring "Wait, I'm working on it" message. Without it, the user might tap the button three times, thinking the first tap didn't register, which only queues up more unnecessary API requests.

Final Thoughts on Technical Empathy

Low-latency design is ultimately an act of empathy. It acknowledges that the user's time is valuable and that their connection is not always perfect. By moving the complexity of API delays behind a curtain of skeletons and optimistic updates, we ensure that Shariah Signal remains a tool that assists the user rather than frustrating them. For teams building productivity apps, the focus should always be on reducing the time between a user’s intent and the app's response.

What to do next

  1. 1.Audit your loaders: Replace generic circular progress bars with skeleton layouts that mimic your content structure to reduce perceived wait time.
  2. 2.Implement SWR (Stale-While-Revalidate): Set up a caching layer that displays recently fetched data immediately while triggering a background refresh.
  3. 3.Stress test for latency: Use Android Studio’s network inspection tools to simulate high-latency environments and ensure your app doesn't hang or crash during slow API responses.
#android engineering#shariah signal#ui design#performance

One email per launch. No spam, unsubscribe any time.