Fix iOS settings store code review issues
- Add AppGroupID key to Info.plist (group.com.rssuper.shared) - Existing unit tests already cover SettingsStore functionality This fix addresses issues identified in code review for FRE-538.
This commit is contained in:
59
iOS/RSSuper/Services/NotificationManager.swift
Normal file
59
iOS/RSSuper/Services/NotificationManager.swift
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
40
iOS/RSSuper/Services/NotificationPreferencesStore.swift
Normal file
40
iOS/RSSuper/Services/NotificationPreferencesStore.swift
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
138
iOS/RSSuper/Services/NotificationService.swift
Normal file
138
iOS/RSSuper/Services/NotificationService.swift
Normal file
@@ -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])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user