Skip to content
HL
Available
Case study / mobilein progress

Sakinah

Muslim companion app: prayer times, Qur'an mushaf, Qibla and trainers, in EN/AR/FR

Sakinah Today screen on iPhone showing the next prayer with a countdown, today's five prayer times and the ayah of the dayQur'an index with a Continue reading card, Surahs and Ahzab toggle, search field and the list of surahs
Today tab: the next prayer with a countdown, today's prayer strip with the calculation method, and a daily ayah with recitation.

At a glance

Platforms
iOS 26+ (SwiftUI), Android (Kotlin, Jetpack Compose, minSdk 26)
Timeline
July 2026 to present
Team
Solo: product, design implementation, iOS, Android
Languages
English, Arabic (RTL, Eastern or Western numerals), French
Codebase
About 320 Swift source files and 81 test files on iOS; Android split into 9 core and 15 feature modules
Tests
About 1,460 iOS XCTest methods

Overview

Sakinah is a free Muslim companion app built natively for iPhone in SwiftUI, with a Kotlin and Jetpack Compose port for Android. It puts prayer times with adhan notifications, a Qur'an reader with recitation, Qibla, a du'a and adhkar library, nearby masjids and guided wudu, salah and Umrah flows in one app, in English, Arabic (RTL) and French. I designed and built it on my own.

The challenge

A worship app has less room for error than most consumer apps. A wrong prayer time, an adhan that never fires, a lost tasbih count or wrongly transcribed religious text are all real failures for the user. iOS silently drops any pending local notifications beyond 64 per app, and with every alert switched on a single day needs 18 of them, so the cap is reached in under four days. The pinned open-source prayer-time library cannot compute every regional authority's official timetable (Morocco's ministry method is one example). Arabic had to be a full RTL layout with a choice between Eastern and Western numerals, not just a string swap. All of this had to work offline, use content from citable sources, and then be rebuilt a second time on Android.

My role

I own the whole product: I started it in July 2026 and have been the only engineer on the iOS app since. On iOS I built the app shell (the system five-tab TabView with one navigation stack per tab), a design-system module with tokens and three appearances (light, night and pure-black OLED), the prayer-time and notification engines, an append-only SQLite journal for worship data, Qur'an text and recitation audio, 29 feature folders, StoreKit 2 supporter subscriptions and gifts, AdMob with Google's UMP consent flow, and Firebase Crashlytics and Analytics. I also wrote the Python content-ingest scripts and an XCTest suite of about 1,460 test methods. For Android, I wrote nine porting specifications and built a multi-module Kotlin and Compose app with staging and production flavors, Play Billing, AdMob with UMP, and a converter that generates Android string resources from the iOS String Catalogs.

Architecture

  1. Clients

    • iOS app

      Swift, SwiftUI, XcodeGen, iOS 26

      The system TabView with five tabs (Today, Qur'an, Qiblah, Dhikr, More), one NavigationStack per tab, and 29 feature folders.

    • Android app

      Kotlin, Jetpack Compose, Gradle multi-module

      A native port built from nine written specs, with one type-safe NavHost and a nested graph per tab, Haze backdrop blur on API 31+, and staging and production flavors of a single application id.

  2. Services

    • Prayer-time engine

      Adhan (Swift) 1.5.0, CoreLocation

      Computes prayer times on the device and picks the regional authority from the user's location; a facade chooses between a verified cached official timetable and the local engine, with no network on the read path.

    • Adhan and reminder scheduler

      UserNotifications

      Keeps a rolling 7-day window of one-shot notifications, adhans first, and tops it up each time the app comes to the foreground.

  3. Media

    • Recitation and adhan audio

      AVFoundation

      Downloads one MP3 per ayah into an on-disk cache from pinned mirrors, keeps three ayat queued ahead, and uses a bundled per-reciter table of ayah durations for a scrubber that spans the whole surah.

  4. Data

    • Worship journal

      SQLite3 C API (WAL, synchronous FULL)

      An append-only event journal for tasbih counts, prayer logs and reading positions; triggers reject updates and deletes, and each row carries event and device ids so journals can be merged.

    • Bundled content

      Python ingest scripts, JSON, String Catalogs

      Qur'an text, translations and tafsir, Hisn al-Muslim (132 chapters, 267 adhkar) and the guided-flow texts are ingested from pinned source files, checked and bundled so they work offline.

  5. Third-party

    • Official timetables

      AlAdhan API over HTTPS

      Covers authorities the on-device library cannot compute: a month is fetched in one request, cached to disk against an anchored location, and reused offline.

    • Maps and places

      MapKit (MKLocalSearch, MKDirections)

      Nearby & Drive finds masjids, shows travel times, and opens Apple's own place card for hours and details.

    • Monetization and diagnostics

      StoreKit 2, Google Play Billing, Google Mobile Ads, UMP, Firebase

      Supporter subscriptions and one-off gifts, ads behind a consent flow, a 7-day ad-free period on first install, and Crashlytics crash reporting.

  6. Infrastructure

    • Support and privacy site

      Firebase Hosting

      A static support page, privacy policy and app-ads.txt for the store listings.

Key decisions

  1. 01

    Rolling 7-day notification window, adhans first

    iOS keeps only 64 pending local notifications per app and drops the rest without an error, and with every alert on a day needs 18. My scheduler keeps a rolling 7-day window of one-shot adhans (35 at most), fills the rest of the budget with reminders soonest first, and refills whenever the app comes forward, so a reminder never pushes out an adhan. The trade-off is that reminders run out a few days before the adhans do if the app is not opened.

  2. 02

    Offline engine plus cached official timetables

    Most regions are computed on the device with the pinned Adhan library. For authorities it cannot compute, the app fetches an official monthly timetable and caches it against an anchored location that only moves when the user does, so GPS jitter and relaunches still find the cached month. If a method has neither a local equivalent nor a cached month, the app shows a waiting state instead of quietly using another authority's times.

  3. 03

    Append-only SQLite journal for worship data

    Tasbih counts, prayer logs and reading positions go to an append-only journal on the system SQLite3 C API in WAL mode with synchronous=FULL, so a write returns only after it is on stable storage. The app reads state derived from the journal, and event and device ids let two journals be union-merged. Editable records such as notes and plans later gained created/updated stamps and tombstones so a future backend can tell a deletion from a missing record.

  4. 04

    The network is fenced off, and content is pinned

    Only five named source files may make HTTP calls: Qur'an edition downloads, official timetables, recitation audio, adhkar audio and gold and silver prices for the zakat nisab. A test scans the source tree and fails if networking code appears anywhere else. Fixed files come from byte-identical mirrors, and edition and adhkar audio downloads are checked against sizes and SHA-256 hashes bundled with the app.

  5. 05

    Arabic RTL, numerals and source text as enforced rules

    The UI mirrors in RTL, and Arabic can use Eastern or Western digits. Every number goes through a numeral formatter, and a test fails if any Arabic string contains a typed digit. The ingest scripts write guided-flow text only if every line is found letter by letter in one of three independent extractions of the publisher's PDF, and Arabic is also checked word by word with its vowel marks.

  6. 06

    Android strings generated from iOS catalogs

    Instead of translating twice, I wrote a Python converter that turns the iOS String Catalogs into Android resources, including placeholders and plurals, and its check mode fails when the committed output is out of date. The Android modules follow nine written porting specs, so the two apps stay consistent in behavior and wording.

Results

  • Built and in pre-release testing (not yet on the App Store or Google Play).
  • An iOS app with five tabs and 29 feature areas: prayer times, adhan and reminders, a Qur'an index and reader with recitation, Qibla, the du'a and adhkar library, the 99 Names, Ruqyah, a Muslim calendar, a zakat calculator, Nearby & Drive, a hifz journey, and wudu, salah, Umrah and janazah guides.
  • Adhan notifications with bundled audio, per-prayer and adhkar reminders that keep firing for a week without opening the app.
  • Full English, Arabic (RTL) and French localization, with a choice between Eastern and Western Arabic numerals.
  • An iOS test suite of about 1,460 XCTest methods, including rules checked in code: the network fence, no typed digits in Arabic strings, and durability of the worship journal.
  • A multi-module Kotlin and Jetpack Compose Android port with Play Billing, AdMob with UMP consent, and separate staging and production flavors.
  • Monetization on both platforms with no worship feature behind a paywall: optional supporter subscriptions and one-off gifts (which remove ads), ads behind a consent flow, and a 7-day ad-free period after install.

Stack

iOS
SwiftSwiftUIXcodeGenStoreKit 2MapKitCoreLocationUserNotificationsAVFoundationSQLite3Adhan (Swift)
Android
KotlinJetpack ComposeNavigation ComposeGradle multi-moduleDataStoreGoogle Play BillingHaze
Services and SDKs
Google Mobile AdsGoogle UMPFirebase CrashlyticsFirebase AnalyticsGoogle Tag ManagerAlAdhan APIFirebase Hosting
Tooling
XCTestPython content-ingest scriptsString CatalogsiOS-to-Android string converter

Sakinah · screenshots

Qibla compass screen showing a 137 degree south-east bearing, the distance to Makkah and a sun check
1 / 5Qibla: the bearing from true north, distance to Makkah and a sun-position check. When the device has no compass, the screen shows the computed direction instead.