<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: RithyTep</title>
    <description>The latest articles on DEV Community by RithyTep (@rithytep).</description>
    <link>https://dev.to/rithytep</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1502415%2Fd4214503-6a76-4aa7-9646-4e7b9e0655c0.jpeg</url>
      <title>DEV Community: RithyTep</title>
      <link>https://dev.to/rithytep</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rithytep"/>
    <language>en</language>
    <item>
      <title>Building a macOS Menu Bar App for the Khmer Lunisolar Calendar with SwiftUI</title>
      <dc:creator>RithyTep</dc:creator>
      <pubDate>Wed, 28 Jan 2026 04:22:29 +0000</pubDate>
      <link>https://dev.to/rithytep/building-a-macos-menu-bar-app-for-the-khmer-lunisolar-calendar-with-swiftui-5hk2</link>
      <guid>https://dev.to/rithytep/building-a-macos-menu-bar-app-for-the-khmer-lunisolar-calendar-with-swiftui-5hk2</guid>
      <description>&lt;p&gt;I recently built &lt;strong&gt;KhmerCalendarBar&lt;/strong&gt;, a macOS menu bar app that displays the Cambodian Chhankitek lunisolar calendar. Here's what I learned.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4zq5frcw8g58wchh7au.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4zq5frcw8g58wchh7au.png" alt=" " width="800" height="1228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why
&lt;/h3&gt;

&lt;p&gt;Cambodia uses a lunisolar calendar called Chhankitek alongside the Gregorian calendar. Public holidays like Khmer New Year, Pchum Ben, and Water Festival are determined by this system. I wanted quick access to Khmer dates without opening a browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SwiftUI&lt;/strong&gt; with &lt;code&gt;MenuBarExtra&lt;/code&gt; (.window style)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swift Package Manager&lt;/strong&gt; — zero external dependencies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;create-dmg&lt;/code&gt;&lt;/strong&gt; for styled DMG installers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Architecture
&lt;/h3&gt;

&lt;p&gt;The app uses a single &lt;code&gt;CalendarViewModel&lt;/code&gt; (&lt;code&gt;@MainActor&lt;/code&gt;, &lt;code&gt;ObservableObject&lt;/code&gt;) that manages all state. Views are composable SwiftUI components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KhmerCalendarBarApp
  └─ MenuBarExtra (popover)
       └─ PopoverContentView
            ├─ TodayHeaderView
            ├─ MonthNavigationView
            ├─ CalendarGridView
            ├─ HolidayListView
            ├─ YearOverviewView
            ├─ ClockView
            ├─ SettingsView
            └─ FooterView
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Hard Part: Chhankitek Conversion
&lt;/h3&gt;

&lt;p&gt;The Khmer lunisolar calendar is complex:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Months alternate between 29 and 30 days&lt;/li&gt;
&lt;li&gt;Intercalary months (Adhikameas) are added periodically&lt;/li&gt;
&lt;li&gt;The Buddhist era year differs from Gregorian by 543&lt;/li&gt;
&lt;li&gt;Each year has an animal cycle (like Chinese zodiac but different)&lt;/li&gt;
&lt;li&gt;Waxing and waning phases are tracked per day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The conversion engine maps Gregorian dates to Khmer dates including lunar day, month name, era year, and animal cycle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Live Menu Bar Updates
&lt;/h3&gt;

&lt;p&gt;The menu bar shows Khmer date + time. Two timers handle updates:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Midnight timer&lt;/strong&gt; — recalculates date at day change&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minute timer&lt;/strong&gt; — updates time display every 30 seconds&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;MenuBarExtra&lt;/code&gt; with &lt;code&gt;.window&lt;/code&gt; style gives you a proper popover&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@MainActor&lt;/code&gt; on the ViewModel prevents threading issues&lt;/li&gt;
&lt;li&gt;Timer-based updates need &lt;code&gt;[weak self]&lt;/code&gt; to avoid retain cycles&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UNUserNotificationCenter&lt;/code&gt; requires explicit authorization on macOS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;create-dmg&lt;/code&gt; makes distribution easy without an Apple Developer account&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Try It
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://khmercalendarbar.rithytep.online" rel="noopener noreferrer"&gt;https://khmercalendarbar.rithytep.online&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/RithyTep/KhmerCalendarBar" rel="noopener noreferrer"&gt;https://github.com/RithyTep/KhmerCalendarBar&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License:&lt;/strong&gt; MIT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contributions welcome!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>swift</category>
    </item>
  </channel>
</rss>
