Skip to content

Releases: salah-kit/SalahKit

SalahKit 0.4.0

Choose a tag to compare

@omar-hanafy omar-hanafy released this 14 Oct 15:32

SalahKit 0.4.0 adds shared missing rows, selectable countdown/count-up focus, civil-day widget entries and handled-alert exclusions. CalculatedPhone uses the missing-row API, MenuBar uses the focus and alert APIs, and PhoneWidgets/MenuBar use the day-boundary option alongside Salah.

PrayerRowConfiguration.event and time are now optional. Styles should use kind for every row, unwrap known events and handle gapReason for missing kinds. Known-only lists keep their order and appearance. The CHANGELOG and RFC 0004 describe the migration. Documentation and example clocks now use the locale's short time format, matching SalahUI in 24-hour locales.

On an explicit clock change, call engine.excludeHandledOccurrencesFromNotifications() before updating or resyncing. Every replan passes engine.notificationExclusions(at: now) to the planner's excluding: set. The engine retains the marked occurrences across later refreshes; ordinary wakes, activation and time-zone changes do not mark new ones. The alerts RFC documents the ordering and retention limits.

Proven

  • Swift Package Index, 2026-10-14. SalahKit is listed after PackageList PR #15688 was accepted. Its compatibility builds hadn't run yet (see Known limitations).

  • Documentation update, 2026-10-14. The 0.4.0 tag and package runtime are unchanged. Current main clarifies the retained consumer calculation policies, timetable source terms and fictional fixture, and the examples' existing semantic-color policy. The adoption guide covers entry points and integration evidence.

  • Final package tests, strict formatting, both default-isolation settings, supported Apple platform builds and strict DocC: 405 tests pass; all four generic simulator platforms build; both isolation configurations, strict formatting and DocC pass. Focus and missing-row tests render the public views/styles; known-only snapshots remain unchanged, including the UTC+14 check.

  • Clock-rollback regressions: without the retained-state fix, the MenuBar and Salah regression tests fail at their ordinary-refresh and wake replay assertions; with it, 96 alert tests, 49 local app-hosted MenuBar tests and 31 Salah alert tests pass. The regressions cover an early engine report, system delivery, rollback into the early window and later ordinary refreshes, while preserving early requests not yet delivered. The 74 alert and 60 reconciliation vectors include five stateful exclusion cases. Those vectors come from an independent Python model and cover retained marks, callback history, expiry, relaunch, capacity, coverage, reminders, namespaces and changed fire dates; all 120 original alert/reconciliation vectors remain unchanged.

  • Local MenuBar and PhoneWidgets app-hosted tests execute the real timeline constructors and verify civil-day/DST turnover, state lookup and entry limits. CalculatedPhone passes 46 unit tests and 10 simulator journeys, including missing polar-day/night rows, Arabic and invalid input.

  • Salah's 450 existing visual baselines pass unchanged using the candidate UI APIs; no image was re-recorded. Its release records final app and signed-app gates.

  • The RFC 0003 and RFC 0004 amendment entries name second consumers. Each amendment was posted to its RFC's discussion, with the maintainer's waiver of the one-week comment window for these pre-1.0 additions: RFC 0003 amendment and RFC 0004 amendment.

  • Final exact-commit CI: CI and Docs for 74c4b7992fe4406056ef47e51771d86774dcd301.

Pending

These checks need a person, a device or elapsed time. Most are carried forward from 0.3.0 and 0.2.0, and each issue gives the steps, the expected observations and what to record:

  • A real tap on MenuBar's desktop-widget Stop button (#29). The process harness runs the intent's code in a sandboxed stand-in; a tap observes the extension WidgetKit launches.
  • MenuBar across actual Mac sleep/wake and clock changes, including real banners, rollback and a subsequent ordinary refresh (#28).
  • MosqueBoard on a physical Apple TV connected to a TV: overscan, screen saver and reading distance (#27).
  • PhoneWidgets unopened on a physical iPhone for seven days (#23), and timeline cost on an iPhone XS/XR or the oldest supported available phone (#24).
  • WatchComplication on a paired physical Apple Watch, including face/Smart Stack turnover and VoiceOver (#25).
  • CalculatedPhone alert delivery with a physical iPhone's app closed, plus overnight reminders, changed times and the first-permission case (#22).
  • In-process reminders across actual Mac sleep/wake: MenuBar runs the engine, so #28 covers them.
  • Native-speaker review of the examples' marked Arabic translations and day-offset wording (#31, #32, #33, #34, #35).
  • Outside API review of RFC 0003 and RFC 0004 (#43). The waived comment window does not establish outside adoption.
  • Carried over from 0.1.0: MosqueBoard on a physical iPad (#26); VoiceOver and the largest text sizes on a physical iPhone or iPad (#30); native-speaker review of SalahCore's prayer names in Urdu, Persian, Bengali, Turkish, Indonesian and Malay (#36, #37, #38, #39, #40, #41); and whether Arabic in the Maghreb should default to Latin digits (#42).

Known limitations

  • Plain styles and examples retain platform semantic secondary text under RFC 0004 §8 #6. Recorded light-mode body-text contrast can fall below 4.5:1; applications needing that floor must restyle it. CalculatedPhone's orange warning outline is decorative, with meaning carried by its primary-colored warning symbol/text. These examples do not claim blanket WCAG AA compliance.

  • Hosted CI skips the PhoneWidgets Home Screen extension journey and two paired WatchComplication companion journeys. It passes PhoneWidgets’ 28 unit tests/four app journeys and WatchComplication’s 25 unit tests/one standalone companion journey; physical widget and paired-watch checks remain pending. CalculatedPhone passes 46 unit tests and all 11 journeys.

  • With “Play the app's tone” on, MenuBar's system requests carry no sound. A notification delivered while the app is quit is silent. With the option off, the system plays its sound and the app plays none.

  • A static countdown shows time remaining at its entry's date, rounded down, and can overstate current time remaining by up to one update step.

  • The macOS XCUITest runner cannot start the separate sandboxed Stop harness; the round trip uses a hostless test bundle. CI's MenuBar run is unsigned without the sandbox/App Group; the signed sandboxed path is local.

  • The iOS 27.0 simulator rejected requests immediately after first notification permission until restart. The same case remains to observe on a physical device.

  • Handled occurrences record engine reports rather than proven OS delivery and are not persisted. Relaunch or retention expiry ends replay protection. Handling a clock change during the early-report window can remove a pending request before system presentation.

  • Swift Package Index compatibility builds had not completed when this was checked on 14 and 15 October 2026 (zero builds; the index reported a build backlog). The listing is not compatibility evidence until they run; the CI runs above are.

  • Package 1.0 still requires the ecosystem's shipped-consumer, outside adoption, review, language and device evidence conditions.

SalahKit 0.3.0

Choose a tag to compare

@omar-hanafy omar-hanafy released this 14 Oct 08:44

SalahKit 0.3.0 adds examples, documentation and the project's governance. The public API is unchanged since 0.2.0.

  • Examples/MenuBar: a macOS menu-bar app that keeps running, with prayer alerts and a desktop widget whose Stop button stops the app's tone. It covers the cross-process stop round trip that 0.2.0 left pending; a real tap on the widget is still to observe.
  • MosqueBoard on Apple TV, built from the iPad app's shared code.
  • The article "Lessons from Salah", on acting on the Darwin signal, gating widget reloads and keeping the language's digits.
  • Governance: an RFC process in CONTRIBUTING.md with a GitHub Discussion for each RFC (#6–#9), CODEOWNERS, and .spi.yml for the Swift Package Index.
  • Fixes: SalahUI keeps the hour's leading zero in 24-hour locales (#11); the README's widget helper and the stop signal's declaration compile in a module that defaults to the main actor; the parity study names the right adhan-swift version (#15).

The full list is in CHANGELOG.md.

Proven

  • The cross-process stop round trip (pending in 0.2.0, now covered). Examples/MenuBar's widget Stop button runs StopPlaybackIntent, which clears the shared NowPlaying value and posts a DarwinSignal. MenuBarStopHarness, a separate process sandboxed and signed like the widget extension with the same App Group, ran the intent's own perform() against the running, sandboxed app on macOS 27 (team-signed): the app received the signal, stopped the tone, and republished; the stored value was gone and the widget's timeline, built by the extension's code from the stored values, no longer showed the Stop button. With only the signal posted (the stored value still saying "playing"), the app stopped anyway and its republish cleared the value. The same tests pass unsigned on CI.
  • MenuBar's display, alerts, sleep and clock changes. The menu-bar title is static text refreshed at the dates of a .steps refresh plan: a journey watched the real status item turn to "Asr started 0 seconds ago" at Asr and "Asr started 1 minute ago" a minute later. InProcessAlertEngine follows the uncapped AlertSchedule and NotificationScheduler installs the budgeted NotificationPlan; unit tests show a reminder the plan left out still comes due, gets a banner through deliver and plays the tone, and two alerts due together play the one the engine's default precedence picks. Wake and clock-change notifications posted to injected notification centers catch alerts up within their grace and replan, a time-zone change replans, and a caught-up alert whose request is still pending gets its banner after the replan (the app delivers after the replan's apply, which the alerts article now advises); in the running app, a simulated three-minute sleep through Asr caught its alert up after the wake, and a ten-minute clock change skipped it and redrew the title.
  • MosqueBoard on tvOS, built from the iPad app's shared code and linking only SalahCore: unit tests and journeys (a normal day, Jumu'ah, the expired timetable, Isha after midnight, a partial row, Arabic, and the tallest board, from Isha to its iqama, in English and Arabic) passed on an Apple TV 4K (3rd generation) simulator with tvOS 27; the iPad journeys still pass.
  • SalahUI's times are the locale's own short time format (#11): content tests pin French, Turkish, Indonesian and British English ("04:11"), on macOS and Linux; the snapshots (English and Arabic) are unchanged.
  • Documentation: the article "Lessons from Salah", whose code runs in the package's tests; every article and README snippet those tests mirror also builds in a module that defaults to the main actor, including from a widget provider that isn't on the main actor; the strict DocC build of all six modules; the site at https://salah-kit.github.io/SalahKit/; the parity study's adhan-swift version corrected (#15).
  • CI on the tagged commit (run 38535019816): every job green on GitHub's hosted runners (Linux with Swift 6.2 and the latest Swift, macOS with Xcode 26, both default-isolation settings), including the new MenuBar and MosqueBoard tvOS jobs.

Pending

These need a person, a device or time. Each issue has the steps:

  • A real tap on MenuBar's desktop widget Stop button: the harness runs the intent's code in its own process, not inside the widget extension WidgetKit launches (#29).
  • MenuBar across real Mac sleep, wake and a clock change, with its banners; the round-trip tests make the app post the notifications to itself (#28).
  • MosqueBoard on a real Apple TV connected to a TV: overscan and the screen saver (#27).
  • Native-speaker review of the examples' Arabic: MenuBar's 51 strings are machine-drafted and marked needs_review (#31; the other examples: #32, #33, #34, #35).
  • Carried over from 0.2.0: the seven-day iPhone widget soak (#23); WatchComplication on a real Apple Watch (#25); timeline cost on the oldest supported iPhone (#24); closed-app alert delivery on a real iPhone (#22); in-process reminders across real Mac sleep and wake (#28); and outside API reviews of RFC 0003 and RFC 0004, now discussions #8 and #9 (#43).

Settled since this release. These were pending when 0.3.0 was released:

  • Submitting SalahKit to the Swift Package Index (.spi.yml was ready): listed on 2026-10-14.
  • Carried over from 0.2.0, a decision on the contrast of CalculatedPhone's banner border and the plain styles' secondary text: made in 0.4.0 (see its Known limitations).

Known limitations

  • With "Play the app's tone" on (the default), MenuBar's notifications carry no sound of their own, so one that arrives while the app isn't running is silent; with it off, they use the system's sound and the app plays nothing.
  • A .staticText countdown shows the time left at its entry's date, rounded down: until the next step it can overstate the time left by up to one step.
  • The macOS XCUITest runner is sandboxed and can't start another sandboxed process, so the round trip runs from a hostless test bundle rather than XCUITest.
  • CI builds and tests MenuBar unsigned, without the sandbox and App Group; the signed, sandboxed run is local.
  • On the iOS 27.0 simulator, notification requests added right after the first permission are rejected until the simulator restarts (from 0.2.0).
  • Requests from Salah's move onto SalahKit that add API stay open for an RFC 0004 amendment: rows for the kinds a partial day is missing (#12), a count-up and the next event in NextPrayerView (#13), and midnight entries from PrayerTimelineBuilder (#14).

SalahKit 0.2.0

Choose a tag to compare

@omar-hanafy omar-hanafy released this 14 Oct 05:42

SalahKit 0.2.0 adds widgets that build their own timelines, SwiftUI building blocks you restyle, and prayer alerts within the system's notification limit, each with working examples. The contracts are RFC 0003 (SalahAlerts) and RFC 0004 (SalahWidgets and SalahUI); the changes are in the CHANGELOG. Documentation: https://salah-kit.github.io/SalahKit/.

Proven

  • SalahWidgets (44 tests): reload gating in SharedValue (an unchanged save writes and reloads nothing; a process's registered defaults don't count); seven days of successive 48-hour timelines with a fixed clock, checked every five minutes and around every entry and event, including a provider call 30 hours late (the expired entry shows exactly until it comes), static-text steps, high latitudes, a timetable's partial row, missing day and end, and initial missing data; DarwinSignal across processes, with a helper process posting and observing, and no delivery after cancel or release even for a signal already in flight.
  • SalahUI (39 tests): the color math equals Salah's own on a grid of colors and its palette; snapshots of the default styles at every SalahFixtures moment, in English (left to right) and Arabic (right to left), on macOS 26 and 27, rendered again in a process at UTC+14 to show times never follow the device's zone; narrow rows keep the next prayer's name and never break a time; no countdown or time is shown where the state doesn't know the next prayer.
  • PhoneWidgets, on an iPhone 17 simulator with iOS 27: the widget added to the Home Screen by XCUITest; the real extension reads the app's settings through the App Group, looks the names up in Arabic chosen in the app while the simulator runs in English, and builds a timeline only after a changed save; every widget family names the next prayer for VoiceOver; 26 unit tests, including seven days of timelines with a late provider call. Inside the extension, a 48-hour timeline took 4.7 to 356 ms (356 ms for the first call after the extension started cold; the median was under 8 ms) at a 7 to 19 MiB footprint.
  • WatchComplication, on a paired iPhone 17 and Apple Watch Series 11 simulator pair (iOS and watchOS 27): the missing-settings view, a settings change sent over WatchConnectivity, the watch working from its stored inputs with the iPhone app closed, the ended-timetable and expired views, and the complication extension's own log of timelines built from the Smart Stack; 25 unit tests.
  • SalahAlerts (84 tests, 120 vectors): the planner, scheduler and engine against 60 alert-planning and 60 reconciliation vectors on macOS (vectors and planner tests on Linux too); CalculatedPhone's alert journey on an iOS 27.0 simulator and on CI's iOS 26.2 simulator: permission through Springboard, alerts delivered with the app closed, the pending requests read back after a time change and an injected failure, 64 of 70 probe requests kept.
  • CI on the tagged commit (run 38515965882): macOS with Xcode 26.6 (368 tests, builds for iOS, watchOS, tvOS and visionOS simulators, a MainActor-isolation build, the snapshots at UTC+14), Linux with Swift 6.2 and the latest Swift, both default-isolation builds, lint, and the MosqueBoard, CalculatedPhone, PhoneWidgets and WatchComplication jobs. The documentation of all six modules builds with warnings as errors and is published (run 38515965883).

Pending

These need a person, a device or elapsed time. Nothing here is claimed by this release. Each issue has the steps.

  • PhoneWidgets on a real iPhone for seven days without opening the app or attaching a debugger, recording each timeline the extension built (#23).
  • The timeline's cost on the oldest supported iPhone, an iPhone XS or XR with iOS 17 (#24).
  • WatchComplication on a real Apple Watch: the complication on a face and in the Smart Stack, a settings change, the iPhone app closed, the ended timetable, and VoiceOver on the corner complication (#25).
  • The widget-to-running-app stop round trip moves to 0.3.0 with the MenuBar example, which covers it; a real tap on the widget is #29.
  • SalahAlerts on devices: closed-app delivery on a real iPhone (#22), and in-process reminders across a real Mac's sleep and wake (#28).
  • Native-speaker review of the examples' Arabic strings (PhoneWidgets, WatchComplication, and CalculatedPhone's 25 alert strings), all marked as needing review (#33, #34, #32).
  • Outside review of RFC 0003 and RFC 0004 by developers who aren't building Salah (#43).

Known limitations

  • On the iOS 27.0 simulator, notification requests added after the first permission were rejected ("Source is not authorized") until the simulator restarted; whether devices do the same is pending.
  • A .stateChanges timeline has no entry at midnight, so a widget that lists the day's times adds one at each midnight; PhoneWidgets shows how (RFC 0004 §9).
  • On an iOS 27 simulator, WidgetKit ran a foreground app's first reload request at once and held later ones until the app went to the background or about 90 seconds passed.
  • SalahUI's plain styles draw passed rows and the next prayer's time in the system's secondary color: 3.3 to 3.4:1 in light mode, above ContrastCheck's 3:1 default but below WCAG's 4.5:1 for body text.
  • The watch journeys and PhoneWidgets' Home Screen journey don't run on hosted CI runners (the watch simulator timed out launching the app; adding a widget needs Springboard); they run locally.
  • Snapshot references exist for macOS 26 and 27; a new macOS version needs its own.

SalahKit 0.1.0

Choose a tag to compare

@omar-hanafy omar-hanafy released this 13 Oct 23:23

The first SalahKit release: SalahCore's schedule, SalahCalculation over SalahTimes 0.1, SalahFixtures, and two example apps that prove them through the public API. CHANGELOG · Documentation · RFC 0001 · RFC 0002

Proven

  • CI on the release commit (run 38482091879):
    • macOS with Xcode 26: build, 201 tests, and builds for the iOS, watchOS, tvOS and visionOS simulators;
    • Linux with swift:6.2-noble and swift:latest: build and tests, SalahCalculation included;
    • both default-isolation settings, and swift-format lint;
    • MosqueBoard: the generated project matches project.yml, the app builds under MainActor and nonisolated isolation, unit tests and journeys pass on an iPad simulator, and a link check passes;
    • CalculatedPhone: the generated project matches project.yml, the app builds under both isolation settings, and unit tests and journeys pass on an iPhone simulator.
  • Local runs on macOS 27, Xcode 27.0 and Swift 6.4:
    • swift test: 201 tests (129 SalahCore, 38 SalahCalculation, 34 SalahFixtures), and the build with MainActor default isolation;
    • the documentation site builds with warnings as errors;
    • MosqueBoard: 15 unit tests and 7 journeys on an iPad (A16) simulator with iOS 27;
    • CalculatedPhone: 28 unit tests and 9 journeys on an iPhone 17 simulator with iOS 27.
  • Vectors:
    • 159 Core vectors, written from RFC 0001 before the implementation;
    • 94 calculation vectors in Vectors/calculation/, whose instants come from SalahTimes' independent Python reference;
    • SalahTimes 0.1.0's 1,196 prayer-time vectors, run through CalculationSource. Every instant it keeps equals SalahTimes'. Each of its 465 missing kinds has a documented reason: 443 don't occur, 9 Ishas follow a later day's first event, 4 Ishas aren't after Maghrib, 3 times fall outside their owning span, and 6 are on Apia's skipped date.
  • The calculated consumer.
    • CalculatedPhone runs on an iPhone simulator through the public SalahCore and SalahCalculation API.
    • It shows calculated schedules and the state across midnight: Kashgar's Isha after midnight, and Kazan's Fajr before midnight.
    • It exercises custom calculation parameters.
    • Its journeys inject the clock and location through launch arguments and assert the text on screen.
  • The timetable consumer.
    • MosqueBoard runs on an iPad simulator with MUIS's published timetable and iqama, through SalahCore's public API.
    • It shows partial and expired coverage, events after midnight and an explicitly declared Friday observance, without inventing missing values.
    • It links neither SalahCalculation nor SalahTimes. CI checks this on every run, in xcodebuild's target graph and in the binary's symbols and strings, and tests that each of those checks fails on a build that breaks it.
  • Parity with adhan-swift (docs/calculation-parity.md): SalahCalculation against the times the Salah app computed with adhan-swift over 2026, in 323 configurations at 22 places, including every method under each high-latitude rule at six places from 51.5° to 69.6°, and adjustments at high latitudes. Each of the 39,336 kind-days that differ by more than a minute falls into one of seven explained classes, and a model of adhan-swift's behaviors reproduces Salah within a minute on all 638,873 kind-days where both give a time. Its list of what Salah's users will see after migrating is checked against a generated table of every change by place, method, rule and kind.
  • Fixed before the release:
    • a CI step that could pass a failed build, because it ran without pipefail;
    • a link check that an early match turned into a pass;
    • cross-day order, with a lead, that wasn't kept past a day without events;
    • a series of corrections to the parity report's classes and to its list of what Salah's users will see.

Pending

These need a person or a device. Each item links the issue that tracks it, with the steps.

  1. Outside API review of RFC 0001 and RFC 0002 by an adopter who isn't building Salah (#43).
  2. Native-speaker review:
    • the catalog drafts for ur, fa, bn, tr, id and ms (#36, #37, #38, #39, #40, #41);
    • the Arabic UI strings of MosqueBoard and CalculatedPhone (#35, #32);
    • RFC 0001 §9 #2: should Maghreb Arabic keep Latin digits? (#42)
  3. Real devices:
    • MosqueBoard on an iPad, in both orientations and at the largest text size (#26);
    • CalculatedPhone on an iPhone, with the largest text size and VoiceOver (#30).

Settled since this release. These were pending when 0.1.0 was released:

  • MUIS data: confirm the Asr convention, that the times are local wall-clock times, and that bundling the SODL v1.0 data with attribution is acceptable. Settled in Timetables.md: MosqueBoard shows the published Asar times without assuming a convention, reads the rows as Singapore wall-clock times, and keeps the data under the SODL with attribution.
  • Edinburgh timetable: check how northern UK timetables print an Isha after midnight. Settled: the Edinburgh board is a labelled synthetic fixture for the after-midnight case, and Timetables.md links a real mosque's published summer policy for comparison.
  • Contrast: check both examples' colors with SalahUI's ContrastCheck once it exists. Settled: the examples keep the platform's semantic secondary text, a limit that RFC 0004 §8 #6 and the 0.4.0 release notes document.
  • Salah's migration: two product decisions from the parity report: Qatar's Ramadan toggle, and how Tehran's high-latitude nights are shown. Decided: Salah's choices records them.

Known limitations

  • SalahCore (RFC 0001 §9):
    • a Dhuhr holds one Jumu'ah congregation;
    • a discarded iqama isn't reported;
    • while the state is unavailable it has no statuses;
    • an expired timetable reads as one gap per day;
    • two events at the same instant get different statuses;
    • wall times on days whose clocks went back across midnight (historic only) aren't handled;
    • a state doesn't see a contradiction that a source makes with a day before its context (only a source that breaks the contract can make one);
    • no public initializers for gaps and coverage.
  • SalahCalculation (RFC 0002 §9):
    • a Maghrib and Isha at the same instant leave Isha missing (Tehran's method from about 58°);
    • of an inverted pair, the later kind is the one dropped;
    • a prayer that doesn't occur is reported like an unknown one;
    • there's no way to tell a method's parameters from the user's;
    • there are no adjustments by prayer kind.
  • Isolation: apply MainActor default isolation per target (.defaultIsolation(MainActor.self) in SwiftPM, or the build setting on your app's own targets). A global -default-isolation MainActor, or SWIFT_DEFAULT_ACTOR_ISOLATION passed on the xcodebuild command line, also reaches SalahTimes and breaks the build (RFC 0001 §6.3).
  • Versions: SalahKit 0.1 depends on SalahTimes .upToNextMinor(from: "0.1.0"), so it never resolves a SalahTimes 0.2 it wasn't built against. CI runs Xcode 26, because hosted runners don't offer Xcode 27 yet.
  • Linux: ICU data differs slightly from Apple platforms, for example in a few localized words and default digits (RFC 0001 §6.2).