macOS виджет, показывающий три месяца сразу: предыдущий, текущий и следующий. Сегодняшняя дата выделена цветным кружком. Нажатие на виджет открывает стандартное приложение Календарь.
RU: В работе часто нужно быстро смотреть даты не только текущего месяца, но и соседних периодов: месяц назад, текущий месяц и месяц вперёд. Это нужно для планирования задач, сверки дедлайнов, отчётов, оплат, графиков и событий, которые обычно лежат на границе месяцев.
Готовые решения не закрывают этот сценарий:
- Widget Wizard — показывает несколько месяцев, но только от текущего вперёд; предыдущий месяц недоступен.
- Mini Calendar — ориентирован на один текущий месяц, не даёт компактный обзор «прошлый / текущий / следующий».
- Calendarique — удобен как menu bar widget, но не предлагает нужный трёхмесячный вид в одном medium-виджете.
- Dato — хороший menu bar calendar, но это не постоянный виджет на рабочем столе.
Поэтому — собственный MVP: компактный macOS-виджет macCalendarWidget, который занимает один medium-слот и сразу показывает предыдущий, текущий и следующий месяц с выделением сегодняшней даты.
EN: When working, it's often necessary to quickly check dates not just for the current month but for adjacent periods: the previous month, the current month, and the next month. This is needed for planning tasks, verifying deadlines, reports, payments, schedules, and events that typically span month boundaries.
Existing solutions don't cover this scenario well:
- Widget Wizard — shows multiple months but only forward from the current one; the previous month is not available.
- Mini Calendar — focused on a single current month, doesn't offer a compact "previous / current / next" overview.
- Calendarique — great as a menu bar widget but doesn't provide the needed three-month view in a single medium widget.
- Dato — a good menu bar calendar, but not a persistent desktop widget.
Hence this MVP: a compact macOS widget that fits in one medium slot and immediately shows the previous, current, and next month with today highlighted.
Виджет появляется в Notification Center / на рабочем столе (macOS 14+):
- Левая колонка — прошлый месяц (приглушённый)
- Центральная — текущий месяц (акцентный цвет, жирный заголовок)
- Правая колонка — следующий месяц (приглушённый)
- Сегодня выделен кружком системного акцентного цвета
RU: Если не хочется собирать самому — скачайте готовый
.zipиз раздела Releases.EN: If you don't want to build from source — download the pre-built
.zipfrom Releases.
RU — Шаги:
- Скачать
macCalendarWidget.zipиз Releases и распаковать - Открыть Terminal в папке с
macCalendarWidget.app - Запустить скрипт установки:
bash install.sh
- Если macOS заблокировал запуск → правый клик на app → Открыть → Открыть
- В меню-баре кликнуть на часы → Edit Widgets → найти Three Month Calendar → добавить (Medium)
EN — Steps:
- Download
macCalendarWidget.zipfrom Releases and unzip it - Open Terminal in the folder containing
macCalendarWidget.app - Run the install script:
bash install.sh
- If macOS blocks launch → right-click the app → Open → Open
- Click the clock in the menu bar → Edit Widgets → find Three Month Calendar → add it (Medium)
Note / Примечание: The app is signed with a personal development certificate, not a paid Developer ID. macOS may show a Gatekeeper warning on first launch — this is expected. Right-click → Open bypasses it. No personal data is collected; the widget only reads the system calendar locale (no calendar events, no permissions requested).
| Что | Версия |
|---|---|
| macOS | 14.0 Sonoma или новее |
| Xcode | 15 или новее |
| xcodegen | 2.x (устанавливается через Homebrew) |
| Apple Developer Account | Бесплатный (Personal Team) |
macCalendarWidget/
├── project.yml ← спецификация для xcodegen
├── .gitignore
├── README.md
│
├── macCalendarWidget/ ← основное приложение (контейнер)
│ ├── macCalendarWidgetApp.swift ← @main, открывает Calendar при тапе на виджет
│ ├── ContentView.swift ← окно-инструкция + onOpenURL handler
│ ├── macCalendarWidget.entitlements ← App Sandbox
│ ├── Info.plist ← генерируется xcodegen
│ └── Assets.xcassets/
│
└── macCalendarWidgetWidgetExtension/ ← WidgetKit расширение
├── ThreeMonthWidget.swift ← вся логика виджета
├── macCalendarWidgetWidgetExtension.entitlements ← App Sandbox
├── Info.plist ← генерируется xcodegen
└── Assets.xcassets/
.xcodeprojне хранится в репозитории — он генерируется изproject.yml. Это стандартная практика при использовании xcodegen.
git clone <ваш-репо-url>
cd macCalendarWidgetbrew install xcodegenОткройте project.yml и замените ZSS72DMBEZ на свой Team ID в двух местах:
# macCalendarWidget target
DEVELOPMENT_TEAM: ZSS72DMBEZ ← заменить
# macCalendarWidgetWidgetExtension target
DEVELOPMENT_TEAM: ZSS72DMBEZ ← заменитьКак найти свой Team ID:
security find-identity -v -p codesigning
# Вывод: "Apple Development: email@example.com (XXXXXXXXXX)"
# ^^^^^^^^^^
# это Team ID (10 символов)Или в Xcode → Settings → Accounts → выбрать аккаунт → нажать на команду.
Также замените bundle ID prefix если нужно (по умолчанию com.zinovevvv):
options:
bundleIdPrefix: com.yourname ← поменятьИ bundle ID в обоих таргетах:
PRODUCT_BUNDLE_IDENTIFIER: com.yourname.macCalendarWidget
# и
PRODUCT_BUNDLE_IDENTIFIER: com.yourname.macCalendarWidget.widgetExtensionxcodegen generateПосле этого в папке появится macCalendarWidget.xcodeproj.
open macCalendarWidget.xcodeprojВ Xcode:
- Кликнуть на
macCalendarWidgetв Project Navigator (синяя иконка) - Выбрать target macCalendarWidget → вкладка Signing & Capabilities
- Убедиться: ✓ Automatically manage signing, Team = ваш Apple ID
- То же самое для target macCalendarWidgetWidgetExtension
Если Xcode показывает жёлтый треугольник → нажать Fix Issue (он сам создаст provisioning profile).
В Xcode нажать ⌘R (схема: macCalendarWidget).
Откроется окно приложения — это нормально, оно является контейнером для виджета.
После успешного запуска через Xcode:
APP_SRC=$(find ~/Library/Developer/Xcode/DerivedData -name "macCalendarWidget.app" -path "*/Debug/*" | grep -v Index | head -1)
cp -Rf "$APP_SRC" /Applications/macCalendarWidget.app
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister \
-f /Applications/macCalendarWidget.app
open /Applications/macCalendarWidget.app- Кликнуть на часы в правом верхнем углу menubar → Edit Widgets
- Или: правый клик на рабочем столе → Edit Widgets
- Найти Three Month Calendar → добавить, выбрать размер Medium
# 1. Пересобрать
xcodebuild -scheme macCalendarWidget -destination "platform=macOS" -configuration Debug clean build
# 2. Заменить приложение
pkill -f macCalendarWidget 2>/dev/null && sleep 1
APP_SRC=$(find ~/Library/Developer/Xcode/DerivedData -name "macCalendarWidget.app" -path "*/Debug/*" | grep -v Index | head -1)
rm -rf /Applications/macCalendarWidget.app
cp -Rf "$APP_SRC" /Applications/macCalendarWidget.app
open /Applications/macCalendarWidget.app
# 3. Удалить и добавить виджет заново (WidgetKit кэширует рендер)macCalendarWidget.app
└── macCalendarWidgetWidgetExtension.appex ← WidgetKit extension
└── ThreeMonthWidget.swift
├── CalendarProvider ← TimelineProvider, обновляет в полночь
├── ThreeMonthCalendarView ← HStack из трёх MonthView + Divider
├── MonthView ← заголовок + сетка дней через HStack/VStack
└── DayCell ← одна ячейка, today = Circle(accentColor)
Почему нужен App Sandbox:
WidgetKit daemon на macOS отказывается регистрировать расширения без entitlement com.apple.security.app-sandbox. Без него виджет компилируется, но не появляется в Edit Widgets.
Почему HStack вместо LazyVGrid:
LazyVGrid внутри HStack с тремя frame(maxWidth: .infinity) колонками некорректно вычисляет ширину ячеек в WidgetKit — числа обрезаются в "...". Явные HStack на каждую строку недели дают гарантированно равные ячейки.
Почему приложение нужно копировать в /Applications:
Запуск из DerivedData (временная папка Xcode) работает при разработке, но macOS не сканирует её при регистрации виджетов системой. Виджет появляется в Edit Widgets только если приложение находится в /Applications.
Открытие Calendar при тапе:
Widget URL macCalendarWidget://openCalendar → macOS открывает основное приложение → onOpenURL → NSWorkspace.shared.open(...) по bundle ID com.apple.iCal.
Виджет обновляется один раз в сутки, в полночь (policy: .after(nextMidnight)). Данные — только системный Calendar.current, никаких сетевых запросов и permissions.
| Проблема | Причина | Решение |
|---|---|---|
| Виджет не появляется в Edit Widgets | Нет App Sandbox entitlement, или приложение не в /Applications |
Проверить entitlements, скопировать в /Applications, сделать lsregister |
WidgetKit_Simulator.WidgetDocument.Error error 5 |
Запущена схема Extension, а не основное приложение; или нет подписи | Переключить схему на macCalendarWidget, настроить Signing |
| Виджет не обновляется после пересборки | WidgetKit кэширует рендер | Удалить виджет и добавить заново |
No signing certificate "Mac Development" found |
Неверный Team ID в project.yml |
Найти правильный ID через security find-identity и обновить project.yml + xcodegen generate |
Числа обрезаются в "..." |
LazyVGrid неверно считает ширину в WidgetKit |
Использовать ручной HStack на каждую строку (уже исправлено) |
- Swift 5 / SwiftUI
- WidgetKit (macOS 14+)
- xcodegen — генерация
.xcodeprojизproject.yml - Подпись: Apple Development certificate (Personal Team, бесплатно)
