## 变更内容总结 1. **iOS后台任务系统重构** - 修复后台任务无法自动运行的问题 2. **日志系统优化** - 改进日志记录机制,添加队列和批量写入 3. **文档新增** - 添加后台任务修复总结和测试指南文档 4. **应用启动优化** - 添加后台任务状态检查和恢复逻辑 5. **版本号更新** - Info.plist版本从1.0.23升级到1.0.24 ## 提交信息类型判断 - **主要类型**: `fix` - 这是一个重要的bug修复,解决了iOS后台任务无法自动运行的核心问题 - **作用域**: `ios-background` - 专注于iOS后台任务功能 - **影响**: 这个修复对iOS用户的后台功能至关重要 ## 提交信息 fix(ios-background): 修复iOS后台任务无法自动运行的问题 主要修复内容: - 修复BackgroundTaskBridge任务调度逻辑,改用BGAppRefreshTaskRequest - 添加任务完成后自动重新调度机制,确保任务持续执行 - 优化应用生命周期管理,移除重复的后台任务调度 - 在应用启动时添加后台任务状态检查和恢复功能 - 将默认任务间隔从30分钟优化为15分钟 次要改进: - 重构日志系统,添加内存队列和批量写入机制,提升性能 - 添加写入锁和重试机制,防止日志数据丢失 - 新增详细的修复总结文档和测试指南 技术细节: - 使用BGAppRefreshTaskRequest替代BGProcessingTaskRequest - 实现任务过期自动重新调度 - 添加任务执行状态监控和恢复逻辑 - 优化错误处理和日志输出 影响范围: iOS后台任务调度、通知推送、应用状态管理
136 lines
4.5 KiB
Swift
136 lines
4.5 KiB
Swift
import Expo
|
||
import React
|
||
import ReactAppDependencyProvider
|
||
import BackgroundTasks
|
||
|
||
@UIApplicationMain
|
||
public class AppDelegate: ExpoAppDelegate {
|
||
var window: UIWindow?
|
||
|
||
var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
|
||
var reactNativeFactory: RCTReactNativeFactory?
|
||
|
||
public override func application(
|
||
_ application: UIApplication,
|
||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
||
) -> Bool {
|
||
// 在应用启动完成前注册后台任务
|
||
if #available(iOS 13.0, *) {
|
||
registerBackgroundTasks()
|
||
}
|
||
|
||
let delegate = ReactNativeDelegate()
|
||
let factory = ExpoReactNativeFactory(delegate: delegate)
|
||
delegate.dependencyProvider = RCTAppDependencyProvider()
|
||
|
||
reactNativeDelegate = delegate
|
||
reactNativeFactory = factory
|
||
bindReactNativeFactory(factory)
|
||
|
||
#if os(iOS) || os(tvOS)
|
||
window = UIWindow(frame: UIScreen.main.bounds)
|
||
factory.startReactNative(
|
||
withModuleName: "main",
|
||
in: window,
|
||
launchOptions: launchOptions)
|
||
#endif
|
||
|
||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||
}
|
||
|
||
// MARK: - Application Lifecycle
|
||
|
||
public override func applicationDidEnterBackground(_ application: UIApplication) {
|
||
super.applicationDidEnterBackground(application)
|
||
|
||
// 后台任务调度现在由 BackgroundTaskBridge 统一管理
|
||
// 这里只需要确保应用有足够的时间完成后台任务
|
||
if #available(iOS 13.0, *) {
|
||
NSLog("[AppDelegate] 应用进入后台,后台任务由 BackgroundTaskBridge 管理")
|
||
}
|
||
}
|
||
|
||
// MARK: - Background Task Registration
|
||
|
||
@available(iOS 13.0, *)
|
||
private func registerBackgroundTasks() {
|
||
let identifier = "com.anonymous.digitalpilates.task"
|
||
|
||
// 注册后台任务处理器
|
||
// 必须在应用启动完成前注册,否则会崩溃
|
||
BGTaskScheduler.shared.register(
|
||
forTaskWithIdentifier: identifier,
|
||
using: nil
|
||
) { [weak self] task in
|
||
// 尝试通知 BackgroundTaskBridge 处理任务
|
||
// 如果 bridge 不可用,标记任务完成
|
||
self?.handleBackgroundTask(task, identifier: identifier)
|
||
}
|
||
|
||
NSLog("[AppDelegate] 后台任务已在应用启动时注册: \(identifier)")
|
||
}
|
||
|
||
@available(iOS 13.0, *)
|
||
private func handleBackgroundTask(_ task: BGTask, identifier: String) {
|
||
// 尝试获取 BackgroundTaskBridge 实例来处理任务
|
||
// 如果 React Native bridge 还未初始化,则直接完成任务
|
||
guard let bridge = reactNativeFactory?.bridge,
|
||
bridge.isValid else {
|
||
NSLog("[AppDelegate] React Native bridge 未就绪,直接完成后台任务")
|
||
task.setTaskCompleted(success: false)
|
||
return
|
||
}
|
||
|
||
// 通过 bridge 查找 BackgroundTaskBridge 模块
|
||
DispatchQueue.main.async {
|
||
if let module = bridge.module(for: BackgroundTaskBridge.self) as? BackgroundTaskBridge {
|
||
// 通知 BackgroundTaskBridge 处理任务
|
||
NotificationCenter.default.post(
|
||
name: NSNotification.Name("BackgroundTaskBridge.handleTask"),
|
||
object: nil,
|
||
userInfo: ["task": task, "identifier": identifier]
|
||
)
|
||
} else {
|
||
NSLog("[AppDelegate] BackgroundTaskBridge 模块未找到,完成后台任务")
|
||
task.setTaskCompleted(success: false)
|
||
}
|
||
}
|
||
}
|
||
|
||
// Linking API
|
||
public override func application(
|
||
_ app: UIApplication,
|
||
open url: URL,
|
||
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
|
||
) -> Bool {
|
||
return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
|
||
}
|
||
|
||
// Universal Links
|
||
public override func application(
|
||
_ application: UIApplication,
|
||
continue userActivity: NSUserActivity,
|
||
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
|
||
) -> Bool {
|
||
let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
||
return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result
|
||
}
|
||
}
|
||
|
||
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
|
||
// Extension point for config-plugins
|
||
|
||
override func sourceURL(for bridge: RCTBridge) -> URL? {
|
||
// needed to return the correct URL for expo-dev-client.
|
||
bridge.bundleURL ?? bundleURL()
|
||
}
|
||
|
||
override func bundleURL() -> URL? {
|
||
#if DEBUG
|
||
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
|
||
#else
|
||
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
||
#endif
|
||
}
|
||
}
|