A full-screen alert system for macOS with multi-monitor support, keyboard shortcuts, and extensive customization options.
- Full-screen alerts with animated pulsating border
- Multi-monitor support (all monitors, primary only, external only, or mouse location)
- Sleep/wake recovery handling to prevent UI freezing
- Keyboard shortcuts (⌘S snooze, ⌘↩ action, Esc dismiss)
- Sound playback with 5 bundled sounds and rotation support
- Highly customizable (colors, animations, timings, sounds)
- Snooze management with configurable limits
- Delegate pattern for analytics and custom behavior
- Multiple alert types (meetings, reminders, tasks, custom)
Add ChimeAlert to your Package.swift dependencies:
dependencies: [
.package(url: "https://github.com/uxderrick/ChimeAlert.git", from: "1.0.0")
]Or in Xcode:
- File → Add Package Dependencies
- Enter repository URL:
https://github.com/uxderrick/ChimeAlert.git - Select version and add to target
import ChimeAlert
import Foundation
struct MyMeeting: AlertItem {
var id: String
var title: String
var startTime: Date
var endTime: Date
var notes: String?
var actionURL: URL? // Optional action URL (e.g., Zoom link)
var actionButtonTitle: String? { actionURL != nil ? "Join" : nil }
var attendees: [AlertAttendee]?
var isRecurring: Bool
var recurrenceDescription: String?
var priority: AlertPriority { .high }
var type: AlertType { .meeting }
}import ChimeAlert
// Configure global settings
AlertManager.shared.configuration.soundVolume = 0.9
AlertManager.shared.configuration.maxSnoozeAttempts = 5
AlertManager.shared.monitorPreference = .allMonitorslet meeting = MyMeeting(
id: "meeting-123",
title: "Q4 Planning Meeting",
startTime: Date().addingTimeInterval(300), // 5 minutes from now
endTime: Date().addingTimeInterval(3900), // 1 hour later
notes: "Discuss roadmap and budget",
actionURL: URL(string: "https://zoom.us/j/123456789"),
attendees: [
AlertAttendee(name: "Alice Chen", isOrganizer: true),
AlertAttendee(name: "Bob Smith")
],
isRecurring: false,
recurrenceDescription: nil
)
AlertManager.shared.showAlert(for: meeting)// Pulsating border
AlertManager.shared.configuration.borderColors = [.red, .orange]
AlertManager.shared.configuration.pulseDuration = 1.5
// Type-specific gradients
AlertManager.shared.configuration.meetingGradient = [
Color(red: 0.2, green: 0.4, blue: 0.8),
Color(red: 0.1, green: 0.2, blue: 0.6)
]
// Animation speeds
AlertManager.shared.configuration.glowDuration = 3.0
AlertManager.shared.configuration.entranceAnimationDuration = 0.5// Enable/disable sound
AlertManager.shared.configuration.soundEnabled = false
// Volume (0.0 to 1.0)
AlertManager.shared.configuration.soundVolume = 0.5
// Sound rotation
AlertManager.shared.configuration.rotateSounds = false// Default snooze duration (seconds)
AlertManager.shared.configuration.defaultSnoozeInterval = 300 // 5 minutes
// Available snooze options
AlertManager.shared.configuration.snoozeOptions = [60, 300, 600, 1800]
// Maximum snooze attempts before disabling snooze button
AlertManager.shared.configuration.maxSnoozeAttempts = 5// Show on all monitors
AlertManager.shared.monitorPreference = .allMonitors
// Show on primary monitor only
AlertManager.shared.monitorPreference = .primaryOnly
// Show on external monitors only
AlertManager.shared.monitorPreference = .externalOnly
// Show on monitor where mouse cursor is located
AlertManager.shared.monitorPreference = .mouseLocationImplement AlertDelegate to handle lifecycle events, track analytics, and customize behavior:
@MainActor
class MyAlertDelegate: AlertDelegate {
func alertDidShow(_ item: AlertItem) {
// Track alert display
Analytics.track("alert_shown", properties: ["title": item.title])
}
func alertDidTapAction(_ item: AlertItem) {
// Handle action button tap (Join/Complete/Open)
if let url = item.actionURL {
NSWorkspace.shared.open(url)
}
}
func alertDidSnooze(_ item: AlertItem, duration: TimeInterval) {
// Track snooze action
Analytics.track("alert_snoozed", properties: ["duration": duration])
}
func alertShouldTrackStats() -> Bool {
return true // Enable internal stat tracking
}
func alertDidTrackStat(event: String, properties: [String: Any]) {
// Forward to your analytics system
Analytics.track(event, properties: properties)
}
func alertShouldShow(_ item: AlertItem) async -> Bool {
// Optional: Validate if alert should still be shown
// Useful for checking external state (e.g., task deleted)
return true
}
}
// Set the delegate
AlertManager.shared.delegate = MyAlertDelegate()ChimeAlert supports multiple alert types with different visual styling:
struct Meeting: AlertItem {
var type: AlertType { .meeting }
// Blue/green gradient, "Join" button, shows attendees
}struct Reminder: AlertItem {
var type: AlertType { .reminder }
// Purple gradient, "Complete" button
}struct Task: AlertItem {
var type: AlertType { .task }
// Red gradient, "Complete" button
}struct CustomAlert: AlertItem {
var type: AlertType {
.custom(
gradient: [
ColorComponents(red: 1.0, green: 0.5, blue: 0.0),
ColorComponents(red: 0.8, green: 0.3, blue: 0.0)
],
iconName: "star.fill"
)
}
}Display a countdown badge for trial periods:
AlertManager.shared.configuration.trialBadge = TrialBadgeInfo(
text: "7d trial left",
color: Color.orange
)
// Remove badge
AlertManager.shared.configuration.trialBadge = nilChimeAlert automatically handles system sleep/wake transitions. To integrate with your app's sleep monitoring:
func setupSleepWakeNotifications() {
NSWorkspace.shared.notificationCenter.addObserver(
forName: NSWorkspace.willSleepNotification,
object: nil,
queue: .main
) { _ in
AlertManager.isSystemRecovering = true
}
NSWorkspace.shared.notificationCenter.addObserver(
forName: NSWorkspace.didWakeNotification,
object: nil,
queue: .main
) { _ in
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
AlertManager.isSystemRecovering = false
}
}
}This library displays a "Powered by Chime" badge in the bottom-right corner of all alerts. The badge links to usechime.app when clicked.
Badge removal is only available with a commercial license. See Commercial Licensing for details.
- macOS 13.0 (Ventura) or later
- Swift 5.9 or later
- Xcode 15.0 or later (for development)
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure
swift testpasses - Submit a pull request
The open-source version of ChimeAlert requires the "Powered by Chime" attribution badge to remain visible in all deployments. If you need to use ChimeAlert in a commercial application, you must purchase a commercial license.
- ✅ Remove the attribution badge - Display your own branding without "Powered by Chime"
- ✅ Professional appearance - No third-party branding in your commercial products
- ✅ Perpetual license - One-time payment, use forever (no recurring fees)
- ✅ Unlimited applications - Use in as many commercial products as you want
- ✅ Support development - Help maintain and improve ChimeAlert
| Tier | Price | Best For |
|---|---|---|
| Indie License | $99 USD | Solo developers, indie projects, and businesses with <$100K annual revenue |
| Enterprise License | $499 USD | Companies with ≥$100K annual revenue, agencies, and teams |
Both tiers include:
- Badge removal for all your commercial applications
- Perpetual license (use forever)
- All future updates
- Email support
Contact us at [email protected] with:
- Your tier - Indie or Enterprise
- Company/individual name - For the license certificate
- Use case (optional) - What you're building with ChimeAlert
We'll send you an invoice and license certificate within 1 business day.
ChimeAlert is released under the MIT License.
This means:
- ✅ Free for everyone - personal and commercial use allowed
- ✅ Modify and distribute - create derivative works freely
- ✅ No restrictions - use in any project without limitations
⚠️ Attribution badge required - the "Powered by Chime" badge must remain visible (removal only available with commercial license - see Commercial Licensing)
Extracted from Chime - a meeting reminder app for macOS.
Questions or feedback? Open an issue on GitHub.