Skip to content

About

Prayer time app for masOS

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

Β 

History

86 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Namaz Vakti app icon

πŸ•Œ Namaz Vakti

Native Apple (macOS menu bar + iOS) and Android apps for Muslim prayer times β€” powered by the Turkish Directorate of Religious Affairs (Diyanet).

CI Platform Swift Kotlin UI Localization License: MIT


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 β†’.

πŸ“Έ Screenshots

macOS

Menu bar Main window
macOS menu-bar dropdown β€” today's times with a live countdown macOS Today window β€” hero countdown and the six prayer times

iOS

Today Today (dark) Monthly Qibla Settings
Today β€” live countdown to the next prayer Today in dark mode Monthly β€” the cached month as a table Qibla finder β€” a live compass pointing toward the Kaaba Settings β€” location, notifications, and the Live Activity toggle

Lock Screen, Live Activity & Dynamic Island

The next-prayer Live Activity and a prayer-time notification, live on the Lock Screen:

Live Activity and a prayer-time notification on the iOS Lock Screen

Dynamic Island β€” the same Live Activity, compact / minimal / expanded (preview)

Dynamic Island previews: expanded, compact, and minimal

Lock Screen widgets β€” rectangular, circular, and inline accessory families (preview)

Lock Screen widget previews: rectangular, circular, and inline

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).

✨ Features

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.

πŸ“‹ Requirements

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

πŸš€ Build & run

The repo is platform-separated: the Apple (iOS + macOS) project is under apple/ and the native Android app under android/ (see Android below).

Apple (macOS + iOS)

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.app

For 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.iOS

Run the tests (pure schedule + decoding logic, no network):

xcodebuild test -scheme NamazVakti -destination 'platform=macOS' -derivedDataPath build/DerivedData

Uses local ad-hoc signing β€” no Apple Developer account required. Re-run xcodegen generate whenever you add or remove source files.

Android

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 APK

Or open android/ in Android Studio. See android/README.md and docs/android/ (PLAN.md, RELEASE.md) for the full story.

🧭 Data source

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.

πŸ—οΈ Architecture (Apple)

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 in Europe/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 shared Localizable.xcstrings.
  • Sources/Shared β€” cross-platform SwiftUI used by both apps: the Today / Settings views and the location picker; the Theme system (Theme/ThemeID/ThemeManager + \.theme environment + Default/Arc tokens) in Theme/; the redesigned Monthly (Monthly/ β€” MonthlyDay, DayRowLayout, MonthChart, the Focus Card) in MonthView; DateLocalizer; the Color.cardBackground shim; and the bundled Arc fonts in Fonts/.
  • Sources/App (macOS) β€” MenuBarExtra menu bar item + the MainWindowView sidebar shell.
  • Sources/iOS (iOS) β€” PrayerTimesApp entry point + RootTabView tab bar, and the Qibla compass (QiblaController wrapping 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.

Notable decisions

  • 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 a Sendable PrayerTimesProvider; the @MainActor PrayerStore awaits it from a cancellable Task, and a structured Task (not a GCD timer) drives the once-a-second countdown.
  • The menu bar uses a plain String title with monospaced digits: a rendered MenuBarExtra label 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.

πŸ€– Android

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).

πŸ—ΊοΈ Roadmap

  • Official Diyanet AwqatSalah provider
  • Location-aware widget (per-widget configuration)
  • More languages β€” prioritized by Muslim population
  • Launch at login (macOS); localized Live Activity / Settings strings

πŸ“„ License

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.

About

Prayer time app for masOS

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages