Skip to content

muhittincamdali/iOS-Notification-Framework

╔══════════════════════════════════════════════════════════════════════════════╗
β•‘                                                                              β•‘
β•‘   β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—    β•‘
β•‘   β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•‘    β•‘
β•‘   β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘    β•‘
β•‘   β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘    β•‘
β•‘   β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘     β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘    β•‘
β•‘   β•šβ•β•  β•šβ•β•β•β• β•šβ•β•β•β•β•β•    β•šβ•β•   β•šβ•β•β•šβ•β•     β•šβ•β• β•šβ•β•β•β•β•β•β•šβ•β•  β•šβ•β•   β•šβ•β•   β•šβ•β•    β•‘
β•‘                                                                              β•‘
β•‘                          β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•—β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—                                β•‘
β•‘                          β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β•šβ•β•β–ˆβ–ˆβ•”β•β•β•                                β•‘
β•‘                          β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘                                   β•‘
β•‘                          β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘                                   β•‘
β•‘                          β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘                                   β•‘
β•‘                          β•šβ•β•  β•šβ•β•β•šβ•β•   β•šβ•β•                                   β•‘
β•‘                                                                              β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

The most comprehensive notification framework for iOS. Everything you need for local, remote, and rich notifications.

Swift iOS visionOS SPM License CI

Features β€’ Quick Start β€’ Installation β€’ Documentation β€’ Examples


πŸ† Why NotificationKit?

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 βœ… ❌

πŸ“‹ Table of Contents


✨ Features

πŸ”” Core Notifications

  • 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

πŸ“Š Analytics & Optimization

  • 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

πŸŽ›οΈ Advanced Controls

  • 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

πŸ”— Integration

  • Deep Linking β€” Navigate users to specific content
  • Personalization β€” Dynamic content with templates
  • Location-based β€” Geofence notifications
  • Remote Push Ready β€” Full APNs integration support

πŸš€ Quick Start

1. Configure on App Launch

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"
        }
    }
}

2. Request Permission

// Standard authorization
try await NotificationKit.shared.requestAuthorization()

// Provisional (quiet) authorization
try await NotificationKit.shared.requestProvisionalAuthorization()

3. Schedule a Notification

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! πŸŽ‰


πŸ“¦ Installation

Swift Package Manager

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.

Requirements

Platform Minimum Version
iOS 15.0+
macOS 13.0+
tvOS 15.0+
watchOS 8.0+
visionOS 1.0+
Swift 5.9+

🎯 Core Concepts

Notification Builder

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()

Trigger Types

// 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
))

Rich Notifications

// 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)

Interactive Categories

// 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")

πŸš€ Advanced Features

πŸ“Š Analytics

// 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()

πŸ§ͺ A/B Testing

// 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")

πŸŒ™ Quiet Hours

// 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()

πŸ“ˆ Rate Limiting

// 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()

πŸ“Ί Notification Channels

// 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")

πŸ”— Deep Linking

// 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"]
)

πŸ‘€ User Preferences

// 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

🎯 Personalization

// 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"

πŸ“ Location-Based Notifications

#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)
#endif

πŸ“… Smart Scheduling

let 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
])

πŸ“ˆ Delivery Optimization

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"]

πŸ“– API Reference

NotificationKit

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

Notification Builder

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

πŸ“ Project Structure

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

πŸ§ͺ Testing

swift test

Run with verbose output:

swift test --verbose

🀝 Contributing

Contributions are welcome! Please read CONTRIBUTING.md first.

  1. Fork the repository
  2. Create feature branch (git checkout -b feature/amazing)
  3. Commit changes (git commit -m 'feat: add amazing feature')
  4. Push (git push origin feature/amazing)
  5. Open Pull Request

πŸ“„ License

MIT License - see LICENSE for details.


⬆ Back to Top

Made with ❀️ for the iOS community

GitHub stars GitHub forks

Sponsor this project

Packages

No packages published

Contributors 2

  •  
  •