Add MoonWatch app, widget extension, shared module, and .gitignore

Adds the real MoonWatch implementation on top of the initial Xcode
template: new SwiftUI views (MoonDiskView, StarFieldView), the
MoonWatchWidget extension, and a Shared module containing the moon
phase calculator, shadow shape, and shared asset catalog. Also adds a
Swift/Xcode/macOS .gitignore and untracks per-user xcuserdata files.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-07 21:05:16 -07:00
parent 1698e3ce15
commit 87f8c7173c
17 changed files with 1211 additions and 24 deletions

View File

@@ -10,10 +10,42 @@ import Testing
struct MoonWatchTests {
@Test func example() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
// Swift Testing Documentation
// https://developer.apple.com/documentation/testing
private let iso8601: ISO8601DateFormatter = {
let f = ISO8601DateFormatter()
f.formatOptions = [.withInternetDateTime]
return f
}()
@Test func julianDayUnixEpoch() {
let utc = try #require(iso8601.date(from: "1970-01-01T00:00:00Z"))
let jd = MoonPhaseCalculator.julianDay(for: utc)
#expect(abs(jd - 2_440_587.5) < 0.0001)
}
/// Full moon near total lunar eclipse peak (UTC).
@Test func goldenFullMoonJanuary2019() {
let date = try #require(iso8601.date(from: "2019-01-21T05:16:00Z"))
let s = MoonPhaseCalculator.snapshot(for: date)
#expect(s.namedPhase == .fullMoon)
#expect(s.normalizedPhase > 0.5 && s.normalizedPhase < 0.52)
#expect(s.illuminatedFraction > 0.99)
}
/// New moon (UTC).
@Test func goldenNewMoonNovember2024() {
let date = try #require(iso8601.date(from: "2024-11-01T13:00:00Z"))
let s = MoonPhaseCalculator.snapshot(for: date)
#expect(s.namedPhase == .newMoon)
#expect(s.normalizedPhase >= 0 && s.normalizedPhase < 0.02)
#expect(s.illuminatedFraction < 0.02)
}
/// First quarter (UTC).
@Test func goldenFirstQuarterOctober2024() {
let date = try #require(iso8601.date(from: "2024-10-10T19:00:00Z"))
let s = MoonPhaseCalculator.snapshot(for: date)
#expect(s.namedPhase == .firstQuarter)
#expect(s.normalizedPhase > 0.25 && s.normalizedPhase < 0.28)
#expect(s.illuminatedFraction > 0.45 && s.illuminatedFraction < 0.60)
}
}