- Swift 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .swiftpm/xcode/xcshareddata/xcschemes | ||
| .vscode | ||
| Sources/CanvasMenuBar | ||
| .editorconfig | ||
| .gitignore | ||
| .swift-version | ||
| Bundler.toml | ||
| icon.png | ||
| Package.resolved | ||
| Package.swift | ||
| README.md | ||
CanvasMenuBar
(NOTE: forked from https://github.com/EthanMcKanna/CanvasMenuBar)
CanvasMenuBar is a native SwiftUI menu bar app that keeps your macOS status bar up to date with Canvas assignments due today (based on your local timezone). It polls the Canvas REST API on a schedule, stores your credentials securely in the Keychain, and stays out of the Dock thanks to LSUIElement.
Requirements
- macOS 13.0 Ventura or newer
- Xcode 15 (or newer) with the latest Swift toolchain
- A Canvas account with permission to create an API token
Getting Started
- Create or pick a folder – this repo already lives in
CanvasMenuBar/. OpenCanvasMenuBar.xcodeprojin Xcode. - Build & run – choose the "CanvasMenuBar" target and hit Run. The app appears as an icon in the right side of the menu bar.
- Configure Canvas – click the menu bar icon →
Settings…, then pick your preferred Data Source:- API Token (full metadata) – enter your Canvas domain (e.g.
school.instructure.com) and a personal access token (Canvas → Account → Settings → New Access Token). Follow Canvas' token guide if you need help. - Calendar Feed (no token required) – choose Calendar Feed and paste the iCal URL from Canvas → Calendar → Calendar Feed. This works even if your school blocks API-token creation.
- API Token (full metadata) – enter your Canvas domain (e.g.
- Refresh – the assignments list refreshes immediately, and again on the cadence you set in Settings (default: every 30 minutes). Use the
Refreshbutton for on-demand syncs. - Work the list – use the day navigator (chevrons + the floating
Todaypill) to browse any date. Flip the All / Assignments / Events filter, tap the course chips to focus on a single class, check off assignments you finish, and keep an eye on the progress tracker (you can toggle it off in Settings if you want a minimalist layout). - Menu bar badge – turn on Show remaining assignments count in Settings → Menu Bar to see a tiny badge atop the calendar icon. The number always reflects today’s remaining assignments, even if you’re previewing future days.
How it Works
- The app calls
GET /api/v1/calendar_eventswithtype=assignment,start_date, andend_dateset to the current day to pull only due-today assignments. It follows pagination via the CanvasLinkheader and filters the results once more on-device. - If you switch to the Calendar Feed data source,
ICSAssignmentsServiceparses the.icsfeed, unescapes HTML descriptions, locations, and categories, and still filters everything down to "due today" locally. - Networking lives in
Networking/CanvasAPI.swift, which handles ISO-8601 date decoding, pagination, and error translation. - Credentials are stored outside of
UserDefaultsusing the Keychain wrapper inServices/KeychainService.swift. A UUIDconfigurationVersionis published whenever settings change soAssignmentsViewModelknows to refresh. MenuBarExtrarenders the UI.AssignmentsMenuViewshows the daily list, relative due times, state badges, and (optionally) a completion tracker.SettingsViewexposes Canvas connection details plus toggles for the tracker, menu bar badge, and launch-on-login behavior.- The app hides its Dock icon by setting
LSUIElement = trueinInfo.plist, so it behaves like a dedicated menu bar utility.
Calendar Feed Extras
ICSAssignmentsServicecaches each.icsdownload (re-validating with Canvas'ETag) and slices it locally per day, so one refresh grabs the feed once even though we prefetch neighboring dates.- Summary suffixes like
Quiz 7.1 [Required] [ECON 202]become badges. Everything before the final bracket turns into inline tags (“REQUIRED”, “HONORS”, etc.) while the trailing token still maps to the course name, making the new course chips and badge row more informative without API metadata. - Tap the info glyph or the row's context menu to open a detail sheet. Calendar-feed HTML lands in a sanitized
AttributedString, so embedded links stay clickable without dragging the Canvas UI into the menu bar. - Locations gain context menu actions (copy or open in Maps) and appear inside the detail sheet too, which is especially useful for .ics-only lab rooms or review sessions.
Customization Ideas
- Filter by specific courses by filling
contextCodesinsideCanvasAPI.fetchAssignments. - Add reminders/notifications when due dates approach.
- Persist a cache of the last fetch so the menu populates instantly on launch.
- Surface more Canvas metadata (submission scores, attachments, etc.).
- Persist completion history to sync across devices or export to Reminders.
- Add keyboard shortcuts for quick day navigation or filter changes.
- Let the app remember your preferred popover size or theme.
Canvas Without API Tokens
If your institution disables personal access tokens, use the built-in Calendar Feed option:
- In Canvas, open Calendar and click Calendar Feed on the right.
- Copy the secret iCal (
.ics) link. - In CanvasMenuBar Settings, set Data Source to Calendar Feed and paste the URL.
The feed includes all assignments/events visible on your calendar, and CanvasMenuBar will filter them down to items due today.
Troubleshooting
- 401 errors: usually caused by an expired or revoked token. Delete + re-add in Settings.
- Empty list: confirm you're looking at the right Canvas instance and that assignments actually have due dates set for today.
- Networking: the status text in the menu will show the error message surfaced by
CanvasAPIError; check Console logs for richer context if needed. - Launch at login toggle errors: Some enterprise machines block programmatic login items. If you see an error in Settings, manually add the app in System Settings → General → Login Items.
Feel free to extend the project or integrate with login items if you want it to launch automatically at login.
Automated Releases
Publishing a GitHub release now triggers the workflow in .github/workflows/release.yml, which builds a signed CanvasMenuBar.app, zips it with ditto, uploads the artifact, and (for release events) attaches the zip to the release automatically. The workflow expects the following repository secrets:
MACOS_CERTIFICATE– Base64-encodedDeveloper ID Applicationcertificate (.p12).MACOS_CERTIFICATE_PASSWORD– Password used when exporting that.p12file.KEYCHAIN_PASSWORD– Any strong passphrase for the temporary CI keychain.MACOS_CERT_IDENTITY– Thecodesignidentity string (e.g.,Developer ID Application: Jane Doe (ABCDE12345)).APPLE_TEAM_ID– Your 10-character Apple Developer Team ID.NOTARYTOOL_KEY– Base64-encoded App Store Connect API key (.p8) used bynotarytool.NOTARYTOOL_KEY_ID– The Key ID associated with that API key.NOTARYTOOL_ISSUER– The Issuer ID from App Store Connect.
Create a GitHub release (or run the workflow manually) after these secrets are in place to produce a signed, notarized, and stapled bundle named CanvasMenuBar-<tag>.zip that you can distribute directly to users without Gatekeeper warnings.