ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β ββββ βββ βββββββ βββββββββββββββββββββββ βββββββ ββββββ ββββββββββββ β
β βββββ βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β ββββββ ββββββ βββ βββ βββββββββ ββββββ ββββββββ βββ βββ β
β βββββββββββββ βββ βββ βββββββββ ββββββ ββββββββ βββ βββ β
β βββ βββββββββββββββ βββ ββββββ ββββββββββββββ βββ βββ βββ β
β βββ βββββ βββββββ βββ ββββββ βββ ββββββββββ βββ βββ βββ β
β β
β βββ βββββββββββββββ β
β βββ ββββββββββββββββ β
β βββββββ βββ βββ β
β βββββββ βββ βββ β
β βββ ββββββ βββ β
β βββ ββββββ βββ β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The most comprehensive notification framework for iOS. Everything you need for local, remote, and rich notifications.
Features β’ Quick Start β’ Installation β’ Documentation β’ Examples
| Feature | NotificationKit | Others |
|---|---|---|
| Local Notifications | β | β |
| Remote Push | β | |
| Rich Media (Images, Video, GIF) | β | |
| Interactive Actions | β | β |
| A/B Testing | β | β |
| Analytics & Insights | β | β |
| Quiet Hours | β | β |
| Rate Limiting | β | β |
| Notification Channels | β | β |
| Deep Linking | β | |
| Delivery Optimization | β | β |
| User Preferences | β | β |
| Location-based | β | |
| Personalization | β | β |
- Features
- Quick Start
- Installation
- Core Concepts
- Advanced Features
- API Reference
- Examples
- Contributing
- Type-Safe Builder Pattern β Compile-time safety with fluent API
- Rich Media Support β Images, videos, GIFs, and audio attachments
- Interactive Actions β Buttons, text input, and quick replies
- Smart Scheduling β Time interval, calendar, and location triggers
- iOS 15+ Focus Modes β Time Sensitive and Critical alerts
- Built-in Analytics β Track opens, interactions, and dismissals
- Delivery Optimization β ML-powered optimal timing
- A/B Testing β Test different notification variants
- Engagement Metrics β Open rates, response times, heatmaps
- Quiet Hours β Respect user's do-not-disturb preferences
- Rate Limiting β Prevent notification fatigue
- Notification Channels β Android-style channel management
- User Preferences β Per-category and topic subscriptions
- Deep Linking β Navigate users to specific content
- Personalization β Dynamic content with templates
- Location-based β Geofence notifications
- Remote Push Ready β Full APNs integration support
import NotificationKit
@main
struct MyApp: App {
init() {
NotificationKit.configure { config in
config.enableAnalytics = true
config.quietHours = .nightTime // 10 PM - 8 AM
config.rateLimiting = .moderate // 5/hour, 20/day
config.urlScheme = "myapp"
}
}
}// Standard authorization
try await NotificationKit.shared.requestAuthorization()
// Provisional (quiet) authorization
try await NotificationKit.shared.requestProvisionalAuthorization()try await NotificationKit.shared.schedule {
Notification(id: "welcome")
.title("Welcome! π")
.body("Thanks for downloading our app")
.sound(.default)
.trigger(after: .seconds(5))
}That's it! π
Add to your Package.swift:
dependencies: [
.package(url: "https://github.com/muhittincamdali/iOS-Notification-Framework.git", from: "2.0.0")
]Or in Xcode: File β Add Package Dependencies β Enter the repository URL.
| Platform | Minimum Version |
|---|---|
| iOS | 15.0+ |
| macOS | 13.0+ |
| tvOS | 15.0+ |
| watchOS | 8.0+ |
| visionOS | 1.0+ |
| Swift | 5.9+ |
let notification = Notification(id: "promo-123")
.title("Flash Sale! π₯")
.subtitle("24 hours only")
.body("Up to 50% off on all items")
.sound(.default)
.badge(1)
.category("PROMO")
.thread("sales")
.relevanceScore(0.9)
.timeSensitive()// Immediate
.trigger(.immediate)
// After time interval
.trigger(after: .minutes(30))
.trigger(after: .hours(2))
.trigger(after: .days(1))
// Calendar-based
.daily(at: 9, minute: 0) // Every day at 9 AM
.weekly(on: .monday, at: 10) // Every Monday at 10 AM
.monthly(on: 1, at: 9) // 1st of every month
.at(date: specificDate) // Specific date
// Location-based (requires CoreLocation)
.trigger(.geofence(
latitude: 37.7749,
longitude: -122.4194,
radius: 100,
identifier: "office",
onEntry: true
))// Image attachment
try await NotificationKit.shared.schedule {
Notification(id: "product")
.title("New Arrival!")
.body("Check out our latest product")
.image(productImageURL)
}
// Video attachment
.video(videoURL)
// GIF attachment
.gif(gifURL)
// Audio attachment
.audio(audioURL)// Register category
NotificationKit.shared.register(
category: NotificationCategory(identifier: "MESSAGE")
.action(.reply)
.action(.view)
.action(
NotificationAction(identifier: "MARK_READ", title: "Mark as Read")
)
)
// Use in notification
Notification(id: "msg-1")
.title("New Message")
.body("Hey, how are you?")
.category("MESSAGE")// Access analytics
let stats = NotificationKit.shared.analytics.stats
print("Scheduled: \(stats.scheduledCount)")
print("Opened: \(stats.interactedCount)")
print("Open Rate: \(NotificationKit.shared.analytics.openRate * 100)%")
// Get action distribution
let actions = NotificationKit.shared.analytics.actionDistribution
// Export analytics
let jsonData = try NotificationKit.shared.analytics.exportAsJSON()// Create experiment
let experiment = ABExperiment.titleTest(
id: "onboarding-title",
name: "Onboarding Title Test",
titleA: "Welcome to the app!",
titleB: "You're going to love this!"
)
NotificationKit.shared.abTesting.createExperiment(experiment)
// Use in notification
Notification(id: "onboarding")
.title("Default Title") // Will be replaced by variant
.body("Start exploring")
.abTest("onboarding-title")// Configure quiet hours
NotificationKit.configure { config in
// Preset: Night time (10 PM - 8 AM)
config.quietHours = .nightTime
// Custom configuration
config.quietHours = QuietHoursConfiguration(
startTime: "23:00",
endTime: "07:00",
activeDays: [.monday, .tuesday, .wednesday, .thursday, .friday]
)
}
// Bypass for urgent notifications
Notification(id: "urgent")
.title("Security Alert")
.bypassQuietHours()
.critical()// Configure rate limiting
NotificationKit.configure { config in
// Presets
config.rateLimiting = .conservative // 2/hour, 10/day
config.rateLimiting = .moderate // 5/hour, 20/day
config.rateLimiting = .relaxed // 10/hour, 50/day
// Custom
config.rateLimiting = RateLimitingConfiguration(
maxPerHour: 3,
maxPerDay: 15
)
}
// Bypass for important notifications
Notification(id: "important")
.title("Order Update")
.bypassRateLimit()// Create channels
NotificationKit.shared.channels.create([
.general,
.promotions,
.social,
.reminders,
NotificationChannel(
id: "orders",
name: "Order Updates",
description: "Shipping and delivery updates",
importance: .high
)
])
// Use channel
Notification(id: "order-shipped")
.title("Order Shipped!")
.channel("orders")
// Check/toggle channel
let isEnabled = NotificationKit.shared.channels.isChannelEnabled("promotions")
NotificationKit.shared.channels.disable(id: "promotions")// Register deep link handlers
NotificationKit.shared.deepLinks.register(path: "/product/*") { context in
let productId = context.path.split(separator: "/").last
// Navigate to product
}
NotificationKit.shared.deepLinks.register(path: "/settings") { context in
// Navigate to settings
}
// Use in notification
Notification(id: "product-update")
.title("New Features!")
.deepLink("/product/123?ref=notification")
// Build URLs
let url = NotificationKit.shared.deepLinks.buildURL(
path: "/product/456",
params: ["source": "push"]
)// Get/set preferences
let prefs = NotificationKit.shared.preferences
// Global toggle
prefs.isEnabled = true
// Frequency
prefs.frequency = .important // Only important notifications
// Categories
prefs.setCategoryEnabled("promotions", enabled: false)
// Topics
prefs.subscribe(to: "tech-news")
prefs.unsubscribe(from: "sports")
// Sound/Vibration
prefs.soundEnabled = true
prefs.vibrationEnabled = false
// Preview mode
prefs.previewMode = .whenUnlocked// Create personalization data
let data = PersonalizationData(userName: "John")
.value("product", "iPhone 15")
.value("discount", "20%")
// Use in notification
Notification(id: "personal-offer")
.title("Hey {{name}}! π")
.body("Get {{discount}} off on {{product}}")
.personalize(data)
// Result: "Hey John! π" / "Get 20% off on iPhone 15"#if canImport(CoreLocation)
import CoreLocation
// Request authorization
LocationNotificationManager.shared.requestAuthorization(always: true)
// Register geofence
let notification = Notification(id: "store-nearby")
.title("You're near our store!")
.body("Stop by for exclusive in-store deals")
let geofence = GeofenceNotification(
identifier: "store-123",
latitude: 37.7749,
longitude: -122.4194,
radius: 200,
notification: notification
)
.onEntry(true)
.onExit(false)
.name("Downtown Store")
try LocationNotificationManager.shared.register(geofence)
#endiflet scheduler = NotificationScheduler()
// Optimal delivery
try await scheduler.scheduleOptimally(
notification,
within: .next(hours: 24)
)
// Recurring notifications
let ids = try await scheduler.scheduleRecurring(
notification,
pattern: .daily(hour: 9, minute: 0),
count: 7 // Next 7 days
)
// Snooze
try await scheduler.snooze(
notificationId: "reminder-1",
for: .minutes(15)
)
// Batch scheduling
let batchId = try await scheduler.scheduleBatch([
notification1,
notification2,
notification3
])let optimizer = DeliveryOptimizer()
// Get optimal delivery time
let optimalHour = optimizer.optimalHour()
let optimalDay = optimizer.optimalDayOfWeek()
let nextOptimal = optimizer.nextOptimalDeliveryDate()
// Get engagement heatmap
let heatmap = optimizer.engagementHeatmap()
// [0: 0.1, 1: 0.05, ..., 9: 0.8, 10: 0.85, ...]
// Get recommendations
let recommendations = optimizer.getRecommendations()
// ["Best delivery time is around 10:00", "Avoid sending at: 2:00, 3:00, 4:00"]| Method | Description |
|---|---|
configure(_:) |
Configure global settings |
requestAuthorization() |
Request notification permissions |
requestProvisionalAuthorization() |
Request quiet notifications |
schedule(builder:) |
Schedule using builder pattern |
schedule(_:) |
Schedule notification directly |
cancel(identifier:) |
Cancel by ID |
cancelAll() |
Cancel all pending |
pendingNotifications() |
Get pending requests |
deliveredNotifications() |
Get delivered notifications |
register(category:) |
Register interactive category |
setBadge(_:) |
Set badge number |
clearBadge() |
Clear badge |
registerForRemoteNotifications() |
Register for push |
| Method | Description |
|---|---|
.title(_:) |
Set title |
.subtitle(_:) |
Set subtitle |
.body(_:) |
Set body text |
.sound(_:) |
Set sound |
.badge(_:) |
Set badge number |
.category(_:) |
Set category ID |
.thread(_:) |
Set thread ID |
.trigger(_:) |
Set trigger |
.image(_:) |
Add image attachment |
.video(_:) |
Add video attachment |
.timeSensitive() |
Mark time sensitive |
.critical() |
Mark critical |
.relevanceScore(_:) |
Set relevance (0-1) |
.channel(_:) |
Set channel ID |
.abTest(_:) |
Set A/B test ID |
.deepLink(_:) |
Set deep link URL |
.personalize(_:) |
Apply personalization |
.bypassQuietHours() |
Bypass quiet hours |
.bypassRateLimit() |
Bypass rate limiting |
.priority(_:) |
Set priority level |
.expires(at:) |
Set expiration date |
iOS-Notification-Framework/
βββ Sources/
β βββ NotificationKit/
β βββ NotificationKit.swift # Main API
β βββ Core/
β β βββ NotificationConfiguration.swift
β β βββ NotificationAnalytics.swift
β β βββ NotificationScheduler.swift
β β βββ DeliveryOptimizer.swift
β β βββ RateLimiter.swift
β β βββ QuietHoursManager.swift
β β βββ ChannelManager.swift
β β βββ ABTestingEngine.swift
β β βββ DeepLinkHandler.swift
β β βββ UserPreferenceManager.swift
β βββ Models/
β β βββ Notification.swift
β β βββ NotificationTrigger.swift
β β βββ NotificationCategory.swift
β βββ Extensions/
β βββ RichNotificationSupport.swift
β βββ LocationNotificationManager.swift
βββ Tests/
β βββ NotificationKitTests/
βββ Documentation/
βββ Package.swift
swift testRun with verbose output:
swift test --verboseContributions are welcome! Please read CONTRIBUTING.md first.
- Fork the repository
- Create feature branch (
git checkout -b feature/amazing) - Commit changes (
git commit -m 'feat: add amazing feature') - Push (
git push origin feature/amazing) - Open Pull Request
MIT License - see LICENSE for details.