dimensionhub/dimensionhub/dimensionhubApp.swift

275 lines
9.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// dimensionhubApp.swift
// dimensionhub
//
// Created by on 2025/2/18.
//
import SwiftUI
import SwiftData
import Observation
import BackgroundTasks
@main
struct dimensionhubApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@StateObject var appNav = AppNavigation.shared
private var userId: String
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Item.self,
SearchHistory.self
])
let modelConfiguration = ModelConfiguration(
schema: schema,
isStoredInMemoryOnly: true,
allowsSave: true
)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
init() {
self.userId = KeychainHelper.getPersistentUserId()
//
UNUserNotificationCenter.current().delegate = appDelegate
print("user_id is: \(self.userId)")
}
var body: some Scene {
WindowGroup {
NavigationStack(path: $appNav.path) {
IndexView()
.navigationDestination(for: AppNavigation.Destination.self) { dest in
switch dest {
case .detail(id: let id, channelName: let channelName):
DetailView(id: id, channelName: channelName)
case .followList(num: let num):
FollowListView(num: num)
case .search:
SearchView()
}
}
}
.navigationViewStyle(.stack)
.tint(.black)
.environment(\.userId, self.userId)
.environmentObject(appNav)
.preferredColorScheme(.light)
}
.modelContainer(sharedModelContainer)
}
}
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
let taskId = "com.jihe.dimensionhub.refresh"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
BGTaskScheduler.shared.register(forTaskWithIdentifier: taskId, using: nil) { task in
self.handleAppRefresh(task: task)
}
Task.detached {
await self.registerForPushNotifications()
//
let unreadDramaIds = await self.getSystemUnreadDramaIds()
let userId = KeychainHelper.getPersistentUserId()
let response = await API.resetUserUnreadNum(userId: userId, dramaIds: unreadDramaIds, as: String.self)
switch response {
case .result(let result):
NSLog("reset user unread num result is: \(result)")
case .error(let errorCode, let message):
NSLog("reset user unread error_code: \(errorCode), message: \(message)")
}
Task {@MainActor in
do {
try await UNUserNotificationCenter.current().setBadgeCount(unreadDramaIds.count)
NSLog("UNUserNotificationCenter setBadgeCount num: \(unreadDramaIds.count)")
} catch let err {
NSLog("UNUserNotificationCenter setBadgeCount get error: \(err)")
}
}
}
return true
}
private func handleAppRefresh(task: BGTask) {
self.scheduleAppRefresh()
task.expirationHandler = {
//
task.setTaskCompleted(success: false)
}
Task {@MainActor in
if let unreadNum = await self.fetchUserUnreadCount(), unreadNum > 0 {
do {
try await UNUserNotificationCenter.current().setBadgeCount(unreadNum)
task.setTaskCompleted(success: true)
NSLog("Refresh setBadgeCount num: \(unreadNum)")
} catch let err {
NSLog("Refresh setBadgeCount get error: \(err)")
task.setTaskCompleted(success: false)
}
} else {
task.setTaskCompleted(success: false)
}
}
}
private func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: taskId)
// 1-4
request.earliestBeginDate = Date(timeIntervalSinceNow: 900)
do {
try BGTaskScheduler.shared.submit(request)
} catch {
NSLog("无法安排后台任务: \(error.localizedDescription)")
}
}
private func registerForPushNotifications() {
//
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) {granted, error in
NSLog("通知权限 granted: \(granted)")
guard granted else {
return
}
//
UNUserNotificationCenter.current().getNotificationSettings { settings in
NSLog("通知设置: \(settings)")
guard settings.authorizationStatus == .authorized else {
return
}
NSLog("通知设置: authorized!!!!")
DispatchQueue.main.async {
//
UIApplication.shared.registerForRemoteNotifications()
}
}
}
}
//
private func fetchUserUnreadCount() async -> Int? {
let userId = KeychainHelper.getPersistentUserId()
let response = await API.getUserUnreadNum(userId: userId, as: Int.self)
switch response {
case .result(let unreadNum):
return unreadNum
case .error(let errorCode, let error):
NSLog("fetchUserUnreadCount get error: \(error), code: \(errorCode)")
return nil
}
}
//
private func getSystemUnreadDramaIds() async -> [Int] {
let notifications = await UNUserNotificationCenter.current().deliveredNotifications()
let unreadDramaIds = notifications.flatMap { n -> [Int] in
let userInfo = n.request.content.userInfo
guard let customData = userInfo["custom_data"] as? [String: AnyObject],
let params = customData["params"] as? [String : AnyObject],
let dramaId = params["drama_id"] as? Int else {
return []
}
return [dramaId]
}
return unreadDramaIds
}
// deviceToken
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
NSLog("Device Token: \(token)")
// deviceToken
Task {
let userId = KeychainHelper.getPersistentUserId()
let sendResult = await API.sendDeviceTokenToServer(userId: userId, token: token, as: String.self)
switch sendResult {
case .result(let reply):
NSLog("send token to server get reply: \(reply)")
case .error(let errorCode, let message):
NSLog("send token to server error_code: \(errorCode), message: \(message)")
}
}
}
//
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
NSLog("注册远程通知失败: \(error)")
}
// App
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
completionHandler(.newData)
}
// MARK: UNUserNotificationCenterDelegate
// App
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
//
completionHandler([.banner, .sound])
}
//
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
handleRemoteNotification(userInfo: userInfo)
//
if let deepLink = userInfo["deepLink"] as? String {
handleDeepLink(deepLink)
}
completionHandler()
}
// MARK:
private func handleRemoteNotification(userInfo: [AnyHashable: Any]) {
AppNavigation.shared.handleNotification(userInfo)
}
private func handleDeepLink(_ link: String) {
//
NSLog("处理深度链接: \(link)")
}
}
// userId
struct UserId: EnvironmentKey {
static let defaultValue: String = KeychainHelper.getPersistentUserId()
}
extension EnvironmentValues {
var userId: String {
get { self[UserId.self] }
set { self[UserId.self] = newValue }
}
}