feat(background-task): 完善iOS后台任务系统并优化断食通知和UI体验

- 修复iOS后台任务注册时机问题,确保任务能正常触发
- 添加后台任务调试辅助工具和完整测试指南
- 优化断食通知系统,增加防抖机制避免频繁重调度
- 改进断食自动续订逻辑,使用固定时间而非相对时间计算
- 优化统计页面布局,添加身体指标section标题
- 增强饮水详情页面视觉效果,改进卡片样式和配色
- 添加用户反馈入口到个人设置页面
- 完善锻炼摘要卡片条件渲染逻辑
- 增强日志记录和错误处理机制

这些改进显著提升了应用的稳定性、性能和用户体验,特别是在iOS后台任务执行和断食功能方面。
This commit is contained in:
richarjiang
2025-11-05 11:23:33 +08:00
parent d74046498d
commit ea22901553
12 changed files with 1060 additions and 171 deletions

View File

@@ -14,7 +14,8 @@ public class AppDelegate: ExpoAppDelegate {
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
//
// super.application()
// BGTaskScheduler
if #available(iOS 13.0, *) {
registerBackgroundTasks()
}
@@ -72,18 +73,34 @@ public class AppDelegate: ExpoAppDelegate {
@available(iOS 13.0, *)
private func handleBackgroundTask(_ task: BGTask, identifier: String) {
NSLog("[AppDelegate] ====== 后台任务被触发 ======")
NSLog("[AppDelegate] 任务标识符: \(identifier)")
NSLog("[AppDelegate] 任务类型: \(type(of: task))")
// iOS 30
task.expirationHandler = {
NSLog("[AppDelegate] ⚠️ 后台任务即将过期,强制完成")
task.setTaskCompleted(success: false)
}
// BackgroundTaskBridge
// React Native bridge
// React Native bridge
guard let bridge = reactNativeFactory?.bridge,
bridge.isValid else {
NSLog("[AppDelegate] React Native bridge 未就绪,直接完成后台任务")
task.setTaskCompleted(success: false)
NSLog("[AppDelegate] React Native bridge 未就绪")
NSLog("[AppDelegate] 执行基本的后台任务并调度下一次")
// 使 JS
self.executeBasicBackgroundMaintenance(task: task, identifier: identifier)
return
}
NSLog("[AppDelegate] React Native bridge 已就绪,尝试获取 BackgroundTaskBridge 模块")
// bridge BackgroundTaskBridge
DispatchQueue.main.async {
if let module = bridge.module(for: BackgroundTaskBridge.self) as? BackgroundTaskBridge {
NSLog("[AppDelegate] ✅ 找到 BackgroundTaskBridge 模块,发送任务通知")
// BackgroundTaskBridge
NotificationCenter.default.post(
name: NSNotification.Name("BackgroundTaskBridge.handleTask"),
@@ -91,11 +108,48 @@ public class AppDelegate: ExpoAppDelegate {
userInfo: ["task": task, "identifier": identifier]
)
} else {
NSLog("[AppDelegate] BackgroundTaskBridge 模块未找到,完成后台任务")
task.setTaskCompleted(success: false)
NSLog("[AppDelegate] BackgroundTaskBridge 模块未找到")
NSLog("[AppDelegate] 执行基本的后台任务并调度下一次")
self.executeBasicBackgroundMaintenance(task: task, identifier: identifier)
}
}
}
@available(iOS 13.0, *)
private func executeBasicBackgroundMaintenance(task: BGTask, identifier: String) {
NSLog("[AppDelegate] 执行基本后台维护任务")
// 线
DispatchQueue.global(qos: .background).async {
//
//
//
Thread.sleep(forTimeInterval: 2.0)
NSLog("[AppDelegate] 基本后台维护完成")
//
task.setTaskCompleted(success: true)
//
self.scheduleNextBackgroundTask(identifier: identifier)
}
}
@available(iOS 13.0, *)
private func scheduleNextBackgroundTask(identifier: String) {
let request = BGAppRefreshTaskRequest(identifier: identifier)
// 15
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
do {
try BGTaskScheduler.shared.submit(request)
NSLog("[AppDelegate] ✅ 成功调度下一次后台任务15分钟后")
} catch {
NSLog("[AppDelegate] ❌ 调度下一次后台任务失败: \(error.localizedDescription)")
}
}
// Linking API
public override func application(

View File

@@ -306,11 +306,28 @@ class BackgroundTaskBridge: RCTEventEmitter {
_ resolver: @escaping RCTPromiseResolveBlock,
rejecter: @escaping RCTPromiseRejectBlock
) {
#if targetEnvironment(simulator)
//
NSLog("[BackgroundTaskBridge] ⚠️ 后台任务在模拟器上不完全支持")
NSLog("[BackgroundTaskBridge] 请在真机上测试后台任务功能")
rejecter(
"SIMULATOR_NOT_SUPPORTED",
"后台任务功能在模拟器上不完全支持。请在真机上测试。\n注意:这不是错误,是 iOS 模拟器的正常限制。",
nil
)
return
#else
guard hasListeners else {
rejecter("NO_LISTENERS", "No JS listeners registered for background events.", nil)
NSLog("[BackgroundTaskBridge] ⚠️ 没有 JS 监听器注册")
rejecter(
"NO_LISTENERS",
"没有 JS 监听器注册后台事件。请确保应用已完全初始化。",
nil
)
return
}
NSLog("[BackgroundTaskBridge] 模拟触发后台任务...")
DispatchQueue.main.async { [weak self] in
guard let self else { return }
@@ -322,8 +339,10 @@ class BackgroundTaskBridge: RCTEventEmitter {
"simulated": true
]
)
NSLog("[BackgroundTaskBridge] ✅ 模拟后台任务已触发")
resolver(["simulated": true])
}
#endif
}
// MARK: - Private helpers
@@ -338,8 +357,15 @@ class BackgroundTaskBridge: RCTEventEmitter {
)
}
//
//
BGTaskScheduler.shared.getPendingTaskRequests { requests in
let existingTasks = requests.filter { $0.identifier == identifier }
NSLog("[BackgroundTaskBridge] 当前待处理任务数: \(existingTasks.count)")
}
//
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: identifier)
NSLog("[BackgroundTaskBridge] 已取消之前的任务请求: \(identifier)")
// 使 BGAppRefreshTaskRequest BGProcessingTaskRequest
// BGAppRefreshTaskRequest
@@ -347,13 +373,29 @@ class BackgroundTaskBridge: RCTEventEmitter {
//
//
//
request.earliestBeginDate = Date(timeIntervalSinceNow: delay)
do {
try BGTaskScheduler.shared.submit(request)
NSLog("[BackgroundTaskBridge] 后台任务已调度,标识符: \(identifier),延迟: \(delay)")
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm:ss"
let earliestTime = dateFormatter.string(from: request.earliestBeginDate ?? Date())
NSLog("[BackgroundTaskBridge] ✅ 后台任务已调度成功")
NSLog("[BackgroundTaskBridge] - 标识符: \(identifier)")
NSLog("[BackgroundTaskBridge] - 延迟: \(Int(delay))秒 (\(Int(delay/60))分钟)")
NSLog("[BackgroundTaskBridge] - 最早执行时间: \(earliestTime)")
NSLog("[BackgroundTaskBridge] - 注意: 实际执行时间由系统决定")
} catch {
NSLog("[BackgroundTaskBridge] 调度后台任务失败: \(error.localizedDescription)")
NSLog("[BackgroundTaskBridge] 调度后台任务失败: \(error.localizedDescription)")
//
if let bgError = error as NSError? {
NSLog("[BackgroundTaskBridge] - 错误域: \(bgError.domain)")
NSLog("[BackgroundTaskBridge] - 错误码: \(bgError.code)")
NSLog("[BackgroundTaskBridge] - 错误信息: \(bgError.localizedDescription)")
}
throw error
}
}