Native Apple (macOS menu bar + iOS) and Android apps for Muslim prayer times β powered by the Turkish Directorate of Religious Affairs (Diyanet).
This is a two-platform monorepo. The Apple app (apple/) is SwiftUI + WidgetKit: on macOS it
lives in your menu bar with a live countdown to the next prayer and opens into a full window; on
iOS it's a tabbed app (Today / Monthly / Qibla / Settings). The Android app (android/) is a
native Kotlin / Jetpack Compose rebuild of the same four tabs. All three share the same Diyanet
(EzanVakti) data and the same feature set β jump to Android β.
| Menu bar | Main window |
|---|---|
![]() |
![]() |
| Today | Today (dark) | Monthly | Qibla | Settings |
|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
The next-prayer Live Activity and a prayer-time notification, live on the Lock Screen:
Dynamic Island β the same Live Activity, compact / minimal / expanded (preview)
Lock Screen widgets β rectangular, circular, and inline accessory families (preview)
The Dynamic Island and Lock Screen-widget images are previews β those surfaces don't render into headless Simulator captures (the Lock Screen shot above is a real-device capture).
The Apple (macOS + iOS) app; the Android app mirrors the same feature set.
- Menu bar countdown β the next prayer with a live, jitter-free countdown (e.g.
Afternoon 1:23:45). - Dropdown panel β today's six times with the next one highlighted, location, and the Gregorian + Hijri date.
- Main window (macOS) β sidebar with Today (rich view), Monthly, and Settings. A Dock icon appears only while the window is open.
- iOS app β the same Today / Monthly / Settings as a tab bar, with a responsive Today hero.
- Monthly β a spacious Focus Card for one day over a compact month chart (each day a thin row, the six prayers as colored dots in equal columns); tap any day to promote it.
- Home Screen widget β small / medium WidgetKit widget with the next prayer and a live countdown.
- Lock Screen widgets (iOS) β rectangular, circular, and inline accessory families.
- Dynamic Island & Live Activity (iOS) β a user-toggled next-prayer countdown in the Dynamic Island (compact / minimal / expanded) and on the Lock Screen.
- Qibla finder (iOS) β a live compass that points to the Kaaba, using your location for a true-north bearing, with figure-8 calibration and a "facing the Qibla" cue (and haptic).
- App icon β a flat crescent & star, shipped for both apps.
- Notifications β optional local notifications at each prayer time.
- Location β Automatic mode tracks your current city (reverse-geocoded to the nearest Diyanet district) or Pinned mode keeps a city you choose (country β city β district). Each location's month is cached, so revisiting one makes no extra request.
- Localized β English, Turkish, and Arabic (right-to-left), including the app name and locale-aware Gregorian + Hijri dates. Adding a language β
- Themes β a Default theme (the system look, auto light/dark) and a dark/gold Arc theme (with bundled Fraunces / Hanken Grotesk / Spline Sans Mono fonts), chosen in Settings β Appearance and applied app-wide on both platforms.
- Accessibility β full Dynamic Type scaling (including the hero countdown) and automatic dark mode.
Apple (apple/):
- macOS 14 (Sonoma) or later, and/or iOS 17 or later
- Xcode 26+ to build
- XcodeGen β
brew install xcodegen
Android (android/):
- Android 11 (API 30) or later
- JDK 21 and the Android SDK (compileSdk 36), or Android Studio
The repo is platform-separated: the Apple (iOS + macOS) project is under apple/ and the
native Android app under android/ (see Android below).
Run the commands below from apple/:
cd apple
brew install xcodegen
xcodegen generate # regenerate NamazVakti.xcodeproj from project.yml
xcodebuild -scheme NamazVakti -destination 'platform=macOS' \
-derivedDataPath build/DerivedData build
open build/DerivedData/Build/Products/Debug/NamazVakti.appFor iOS, build for the Simulator and launch it on a booted device:
xcodebuild -scheme NamazVaktiiOS -destination 'generic/platform=iOS Simulator' \
-derivedDataPath build/DerivedData build
xcrun simctl install booted build/DerivedData/Build/Products/Debug-iphonesimulator/NamazVaktiiOS.app
xcrun simctl launch booted com.olcayertas.NamazVakti.iOSRun the tests (pure schedule + decoding logic, no network):
xcodebuild test -scheme NamazVakti -destination 'platform=macOS' -derivedDataPath build/DerivedDataUses local ad-hoc signing β no Apple Developer account required. Re-run
xcodegen generate whenever you add or remove source files.
A native Kotlin / Jetpack Compose app with two Gradle modules β :domain (pure Kotlin/JVM, no
Android deps, with the ported unit tests) and :app (the Compose UI). Run from android/:
cd android
./gradlew :domain:test # the ported logic tests (no Android SDK needed)
./gradlew :app:assembleDebug # the debug APKOr open android/ in Android Studio. See android/README.md and
docs/android/ (PLAN.md, RELEASE.md) for the full story.
Prayer times come from the no-auth EzanVakti wrapper of Diyanet's published tables:
GET https://ezanvakti.emushaf.net/vakitler/{districtId} # one call = a full month
The hierarchy endpoints (/ulkeler, /sehirler/{id}, /ilceler/{id}) drive the location
picker. Everything goes through a PrayerTimesProvider protocol, so the official Diyanet
AwqatSalah API can be dropped in later without touching the UI.
The Apple app lives under apple/; paths below are relative to it.
Sources/Coreβ UI-free, shared by every target:PrayerDay,PrayerSchedule(next-prayer math inEurope/Istanbul),PrayerCache,PrayerTimesProvider/PlacesProvider(EzanVakti),PrayerStore,NotificationScheduler,Qibla(great-circle bearing to the Kaaba β pure, so it's unit-tested without a device),LocationTracker(CoreLocation) +LocationResolver(reverse-geocode β Diyanet district, with a pure, unit-tested Turkish-aware name matcher), and the sharedLocalizable.xcstrings.Sources/Sharedβ cross-platform SwiftUI used by both apps: the Today / Settings views and the location picker; theThemesystem (Theme/ThemeID/ThemeManager+\.themeenvironment + Default/Arc tokens) inTheme/; the redesigned Monthly (Monthly/βMonthlyDay,DayRowLayout,MonthChart, the Focus Card) inMonthView;DateLocalizer; theColor.cardBackgroundshim; and the bundled Arc fonts inFonts/.Sources/App(macOS) βMenuBarExtramenu bar item + theMainWindowViewsidebar shell.Sources/iOS(iOS) βPrayerTimesAppentry point +RootTabViewtab bar, and the Qibla compass (QiblaControllerwrapping CoreLocation heading/location +QiblaView). The compass is iOS-only β Macs have no magnetometer, so it's filtered out of the macOS sidebar.Sources/Widgetβ WidgetKit timeline provider and views (Home Screen + iOS Lock Screen accessory families), built into both the macOS and iOS widget extensions.Sources/LiveActivity(iOS) β the ActivityKit attributes and the Live Activity / Dynamic Island UI, shared by the iOS app and its widget.
- App Group (
group.com.olcayertas.NamazVakti) β the app and widget share the selected district + cached month, so the widget follows your Automatic/Pinned location (not a fixed default). It degrades gracefully to per-process storage when the entitlement is absent, and a one-time migration carries over a district saved before the App Group existed. - Themes are a value-type token set in
\.theme(Default = system semantics so it's pixel-identical to the un-themed look; Arc = fixed dark palette + bundled fonts, and forces.dark). The widget / Live Activity / Dynamic Island / menu-bar title can't read the runtime theme (separate processes / plain String), so they keep the system look. - Automatic location is Turkey-focused β it reverse-geocodes to a Diyanet (EzanVakti) district; outside TΓΌrkiye, or when permission is denied, it falls back to the pinned/default city.
- Swift 6 language mode with complete strict concurrency. Networking is
async/await(URLSession.data(from:)) behind aSendablePrayerTimesProvider; the@MainActorPrayerStoreawaits it from a cancellableTask, and a structuredTask(not a GCD timer) drives the once-a-second countdown. - The menu bar uses a plain
Stringtitle with monospaced digits: a renderedMenuBarExtralabel hangs AppKit's status-item sizing on macOS 26. (This β not async/await β was the real cause of an earlier hang, so the data layer is free to use modern concurrency.) - Localized via String Catalogs (English source Β· Turkish Β· Arabic with RTL) β including the app/widget names and locale-aware Gregorian + Hijri dates. See docs/LOCALIZATION.md.
A native Kotlin / Jetpack Compose app under android/ β a separate codebase from the Apple
sources, but the same Diyanet (EzanVakti) data and the same four tabs. Two Gradle modules:
:domain (pure Kotlin/JVM, no Android deps, unit-tested β the ported schedule / Qibla /
location-matcher logic) and :app (the Compose UI). Kotlin 2.2, Compose BOM 2025.06, Material 3,
minSdk 30 / target+compile 36, Hilt DI, an R8 release build.
Feature parity with Apple: the Today / Monthly / Qibla / Settings tabs, a live Qibla compass, prayer-time notifications (exact alarms + boot reschedule), automatic location (reverse-geocode to a Diyanet district) plus pinned, a Glance home-screen widget, two themes (Material You dynamic + the dark/gold Arc), and EN Β· TR Β· AR localization including Arabic RTL.
See android/README.md and docs/android/ β
PLAN.md (design) and RELEASE.md (Play release).
- Official Diyanet
AwqatSalahprovider - Location-aware widget (per-widget configuration)
- More languages β prioritized by Muslim population
- Launch at login (macOS); localized Live Activity / Settings strings
Released under the MIT License Β© 2026 Olcay ErtaΕ.
Prayer-time data Β© T.C. Diyanet Δ°Εleri BaΕkanlΔ±ΔΔ±, accessed via the community EzanVakti service. This project is not affiliated with or endorsed by Diyanet.









