diff --git a/iOS/RSSuper/Services/NotificationManager.swift b/iOS/RSSuper/Services/NotificationManager.swift
new file mode 100644
index 0000000..e2d4294
--- /dev/null
+++ b/iOS/RSSuper/Services/NotificationManager.swift
@@ -0,0 +1,59 @@
+import UserNotifications
+import Foundation
+
+final class NotificationManager {
+ private init() {}
+ static let shared = NotificationManager()
+
+ private let notificationService = NotificationService.shared
+
+ func requestPermissions() async -> Bool {
+ await notificationService.requestAuthorization()
+ }
+
+ func checkPermissions() async -> Bool {
+ let status = await notificationService.getAuthorizationStatus()
+ return status == .authorized || status == .provisional
+ }
+
+ func scheduleNotification(
+ title: String,
+ body: String,
+ delay: TimeInterval = 0,
+ completion: ((Bool, Error?) -> Void)? = nil
+ ) {
+ notificationService.showLocalNotification(
+ title: title,
+ body: body,
+ delay: delay,
+ completion: completion
+ )
+ }
+
+ func showNotification(
+ title: String,
+ body: String,
+ completion: ((Bool, Error?) -> Void)? = nil
+ ) {
+ notificationService.showNotification(
+ title: title,
+ body: body,
+ completion: completion
+ )
+ }
+
+ func updateBadgeCount(_ count: Int) {
+ notificationService.updateBadgeCount(count)
+ }
+
+ func clearNotifications() {
+ notificationService.clearAllNotifications()
+ }
+
+ func getPendingNotifications(completion: @escaping ([UNNotificationRequest]) -> Void) {
+ notificationService.getDeliveredNotifications { notifications in
+ let requests = notifications.map { $0.request }
+ completion(requests)
+ }
+ }
+}
diff --git a/iOS/RSSuper/Services/NotificationPreferencesStore.swift b/iOS/RSSuper/Services/NotificationPreferencesStore.swift
new file mode 100644
index 0000000..f6cf5a6
--- /dev/null
+++ b/iOS/RSSuper/Services/NotificationPreferencesStore.swift
@@ -0,0 +1,40 @@
+import Foundation
+
+final class NotificationPreferencesStore {
+ private static let userDefaultsKey = "notification_preferences"
+
+ private init() {}
+ static let shared = NotificationPreferencesStore()
+
+ private let userDefaults: UserDefaults
+
+ private init(userDefaults: UserDefaults = .standard) {
+ self.userDefaults = userDefaults
+ }
+
+ func save(_ preferences: NotificationPreferences) {
+ do {
+ let data = try JSONEncoder().encode(preferences)
+ userDefaults.set(data, forKey: Self.userDefaultsKey)
+ } catch {
+ print("Failed to save notification preferences: \(error)")
+ }
+ }
+
+ func load() -> NotificationPreferences {
+ guard let data = userDefaults.data(forKey: Self.userDefaultsKey),
+ let preferences = try? JSONDecoder().decode(NotificationPreferences.self, from: data) else {
+ return NotificationPreferences()
+ }
+ return preferences
+ }
+
+ func clear() {
+ userDefaults.removeObject(forKey: Self.userDefaultsKey)
+ }
+
+ func resetToDefaults() {
+ let defaults = NotificationPreferences()
+ save(defaults)
+ }
+}
diff --git a/iOS/RSSuper/Services/NotificationService.swift b/iOS/RSSuper/Services/NotificationService.swift
new file mode 100644
index 0000000..9cf5145
--- /dev/null
+++ b/iOS/RSSuper/Services/NotificationService.swift
@@ -0,0 +1,138 @@
+import UserNotifications
+import Foundation
+
+final class NotificationService {
+ private init() {}
+ static let shared = NotificationService()
+
+ private var notificationCenter: UNUserNotificationCenter {
+ UNUserNotificationCenter.current()
+ }
+
+ func requestAuthorization() async -> Bool {
+ do {
+ let status = try await notificationCenter.requestAuthorization(options: [.alert, .badge, .sound])
+ return status
+ } catch {
+ return false
+ }
+ }
+
+ func getAuthorizationStatus() async -> UNAuthorizationStatus {
+ await notificationCenter.authorizationStatus()
+ }
+
+ func getNotificationSettings() async -> UNNotificationSettings {
+ await notificationCenter.notificationSettings()
+ }
+
+ func showNotification(
+ title: String,
+ body: String,
+ identifier: String = UUID().uuidString,
+ completion: ((Bool, Error?) -> Void)? = nil
+ ) {
+ let content = UNMutableNotificationContent()
+ content.title = title
+ content.body = body
+ content.categoryIdentifier = "rssuper_notification"
+ content.sound = .default
+
+ let request = UNNotificationRequest(
+ identifier: identifier,
+ content: content,
+ trigger: nil
+ )
+
+ notificationCenter.add(request) { error in
+ completion?(error == nil, error)
+ }
+ }
+
+ func showLocalNotification(
+ title: String,
+ body: String,
+ delay: TimeInterval = 0,
+ identifier: String = UUID().uuidString,
+ completion: ((Bool, Error?) -> Void)? = nil
+ ) {
+ let content = UNMutableNotificationContent()
+ content.title = title
+ content.body = body
+ content.categoryIdentifier = "rssuper_notification"
+ content.sound = .default
+
+ let trigger = UNTimeIntervalNotificationTrigger(timeInterval: delay, repeats: false)
+
+ let request = UNNotificationRequest(
+ identifier: identifier,
+ content: content,
+ trigger: trigger
+ )
+
+ notificationCenter.add(request) { error in
+ completion?(error == nil, error)
+ }
+ }
+
+ func showPushNotification(
+ title: String,
+ body: String,
+ data: [String: String] = [:],
+ identifier: String = UUID().uuidString,
+ completion: ((Bool, Error?) -> Void)? = nil
+ ) {
+ let content = UNMutableNotificationContent()
+ content.title = title
+ content.body = body
+ content.categoryIdentifier = "rssuper_notification"
+ content.sound = .default
+
+ for (key, value) in data {
+ content.userInfo[key] = value
+ }
+
+ let request = UNNotificationRequest(
+ identifier: identifier,
+ content: content,
+ trigger: nil
+ )
+
+ notificationCenter.add(request) { error in
+ completion?(error == nil, error)
+ }
+ }
+
+ func updateBadgeCount(_ count: Int) {
+ UIApplication.shared.applicationIconBadgeNumber = count
+ }
+
+ func clearAllNotifications() {
+ notificationCenter.removeAllDeliveredNotifications()
+ updateBadgeCount(0)
+ }
+
+ func getDeliveredNotifications(completion: @escaping ([UNNotification]) -> Void) {
+ notificationCenter.getDeliveredNotifications { notifications in
+ completion(notifications)
+ }
+ }
+
+ func removeDeliveredNotifications(withIdentifiers identifiers: [String]) {
+ notificationCenter.removeDeliveredNotifications(withIdentifiers: identifiers)
+ }
+
+ func removePendingNotificationRequests(withIdentifiers identifiers: [String]) {
+ notificationCenter.removePendingNotificationRequests(withIdentifiers: identifiers)
+ }
+
+ func addNotificationCategory() {
+ let category = UNNotificationCategory(
+ identifier: "rssuper_notification",
+ actions: [],
+ intentIdentifiers: [],
+ options: []
+ )
+ notificationCenter.setNotificationCategories([category])
+ }
+}
diff --git a/native-route/ios/RSSuper/Info.plist b/native-route/ios/RSSuper/Info.plist
index 50b275a..602cff5 100644
--- a/native-route/ios/RSSuper/Info.plist
+++ b/native-route/ios/RSSuper/Info.plist
@@ -18,6 +18,8 @@
1.0
CFBundleVersion
1
+ AppGroupID
+ group.com.rssuper.shared
NSLocationWhenInUseUsageDescription
We need your location to provide nearby feed updates.
NSUserNotificationsUsageDescription