- 添加新的processing任务标识符到iOS配置文件 - 重构BackgroundTaskBridge支持不同任务类型(refresh/processing) - 增强后台任务日志记录和调试信息 - 修复任务类型配置不匹配问题 - 改进任务调度逻辑和错误处理机制 - 添加任务执行时间戳记录用于调试 - 移除notification-settings中未使用的AuthGuard依赖
621 lines
19 KiB
Swift
621 lines
19 KiB
Swift
//
|
||
// BackgroundTaskBridge.swift
|
||
// OutLive
|
||
//
|
||
// Native bridge responsible for scheduling and relaying iOS background tasks to the JS runtime.
|
||
//
|
||
|
||
import BackgroundTasks
|
||
import Foundation
|
||
import React
|
||
import UIKit
|
||
|
||
@objc(BackgroundTaskBridge)
|
||
class BackgroundTaskBridge: RCTEventEmitter {
|
||
|
||
private enum TaskKind: String {
|
||
case processing
|
||
case refresh
|
||
}
|
||
|
||
private let queue = DispatchQueue(label: "com.anonymous.digitalpilates.background.bridge", qos: .utility)
|
||
|
||
private var hasListeners = false
|
||
private var identifier: String?
|
||
private var kind: TaskKind = .processing
|
||
private var requiresNetworkConnectivity = false
|
||
private var requiresExternalPower = false
|
||
private var defaultDelay: TimeInterval = 60 * 30 // 30 minutes
|
||
private var currentTask: BGTask?
|
||
private let pendingTaskWaitTimeout: TimeInterval = 20
|
||
private var waitingForJSListeners = false
|
||
private var pendingTaskPayload: [String: Any]?
|
||
private var pendingTaskTimeoutWorkItem: DispatchWorkItem?
|
||
|
||
override init() {
|
||
super.init()
|
||
// 监听来自 AppDelegate 的后台任务通知
|
||
NotificationCenter.default.addObserver(
|
||
self,
|
||
selector: #selector(handleTaskNotification(_:)),
|
||
name: NSNotification.Name("BackgroundTaskBridge.handleTask"),
|
||
object: nil
|
||
)
|
||
}
|
||
|
||
deinit {
|
||
NotificationCenter.default.removeObserver(self)
|
||
}
|
||
|
||
override static func requiresMainQueueSetup() -> Bool {
|
||
true
|
||
}
|
||
|
||
override func startObserving() {
|
||
hasListeners = true
|
||
queue.async { [weak self] in
|
||
self?.emitPendingTaskIfPossible()
|
||
}
|
||
}
|
||
|
||
override func stopObserving() {
|
||
hasListeners = false
|
||
}
|
||
|
||
@objc
|
||
private func handleTaskNotification(_ notification: Notification) {
|
||
guard #available(iOS 13.0, *),
|
||
let userInfo = notification.userInfo,
|
||
let task = userInfo["task"] as? BGTask else {
|
||
return
|
||
}
|
||
|
||
NSLog("[BackgroundTaskBridge] 收到来自 AppDelegate 的后台任务")
|
||
handle(task: task)
|
||
}
|
||
|
||
override func supportedEvents() -> [String]! {
|
||
[
|
||
"BackgroundTaskBridge.execute",
|
||
"BackgroundTaskBridge.expire"
|
||
]
|
||
}
|
||
|
||
@objc
|
||
func configure(
|
||
_ options: NSDictionary,
|
||
resolver: @escaping RCTPromiseResolveBlock,
|
||
rejecter: @escaping RCTPromiseRejectBlock
|
||
) {
|
||
guard #available(iOS 13.0, *) else {
|
||
rejecter(
|
||
"BACKGROUND_TASK_UNAVAILABLE",
|
||
"BGTaskScheduler requires iOS 13 or later.",
|
||
nil
|
||
)
|
||
return
|
||
}
|
||
|
||
guard let identifier = options["identifier"] as? String,
|
||
!identifier.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
||
rejecter("INVALID_IDENTIFIER", "A non-empty identifier is required.", nil)
|
||
return
|
||
}
|
||
|
||
let taskTypeRaw = (options["taskType"] as? String)?.lowercased()
|
||
let taskKind = TaskKind(rawValue: taskTypeRaw ?? "") ?? .processing
|
||
|
||
let requiresNetwork = (options["requiresNetworkConnectivity"] as? Bool) ?? false
|
||
let requiresPower = (options["requiresExternalPower"] as? Bool) ?? false
|
||
let defaultDelaySeconds = (options["defaultDelay"] as? NSNumber)?.doubleValue
|
||
|
||
queue.async { [weak self] in
|
||
guard let self else { return }
|
||
|
||
self.identifier = identifier
|
||
self.kind = taskKind
|
||
self.requiresNetworkConnectivity = requiresNetwork
|
||
self.requiresExternalPower = requiresPower
|
||
|
||
if let delay = defaultDelaySeconds, delay > 0 {
|
||
self.defaultDelay = delay
|
||
}
|
||
|
||
do {
|
||
try self.scheduleTask(after: self.defaultDelay)
|
||
resolver([
|
||
"identifier": identifier,
|
||
"scheduled": true,
|
||
"defaultDelay": self.defaultDelay
|
||
])
|
||
} catch {
|
||
rejecter("BACKGROUND_TASK_ERROR", error.localizedDescription, error)
|
||
}
|
||
}
|
||
}
|
||
|
||
@objc
|
||
func schedule(
|
||
_ options: NSDictionary,
|
||
resolver: @escaping RCTPromiseResolveBlock,
|
||
rejecter: @escaping RCTPromiseRejectBlock
|
||
) {
|
||
guard #available(iOS 13.0, *) else {
|
||
rejecter(
|
||
"BACKGROUND_TASK_UNAVAILABLE",
|
||
"BGTaskScheduler requires iOS 13 or later.",
|
||
nil
|
||
)
|
||
return
|
||
}
|
||
|
||
guard let identifier = identifier else {
|
||
rejecter("NOT_CONFIGURED", "Call configure() before scheduling.", nil)
|
||
return
|
||
}
|
||
|
||
let delay = (options["delay"] as? NSNumber)?.doubleValue
|
||
let effectiveDelay = (delay?.isFinite == true && delay! >= 0) ? delay! : defaultDelay
|
||
|
||
queue.async { [weak self] in
|
||
guard let self else { return }
|
||
|
||
do {
|
||
try self.scheduleTask(after: effectiveDelay)
|
||
resolver([
|
||
"identifier": identifier,
|
||
"scheduled": true,
|
||
"delay": effectiveDelay
|
||
])
|
||
} catch {
|
||
rejecter("BACKGROUND_TASK_ERROR", error.localizedDescription, error)
|
||
}
|
||
}
|
||
}
|
||
|
||
@objc
|
||
func complete(
|
||
_ success: NSNumber,
|
||
rescheduleAfter: NSNumber?,
|
||
resolver: @escaping RCTPromiseResolveBlock,
|
||
rejecter: @escaping RCTPromiseRejectBlock
|
||
) {
|
||
guard #available(iOS 13.0, *) else {
|
||
rejecter(
|
||
"BACKGROUND_TASK_UNAVAILABLE",
|
||
"BGTaskScheduler requires iOS 13 or later.",
|
||
nil
|
||
)
|
||
return
|
||
}
|
||
|
||
let shouldRescheduleAfter = rescheduleAfter?.doubleValue ?? defaultDelay
|
||
let completionSuccess = success.boolValue
|
||
|
||
queue.async { [weak self] in
|
||
guard let self else { return }
|
||
|
||
if let task = self.currentTask {
|
||
task.setTaskCompleted(success: completionSuccess)
|
||
self.currentTask = nil
|
||
}
|
||
|
||
do {
|
||
try self.scheduleTask(after: shouldRescheduleAfter)
|
||
resolver([
|
||
"rescheduled": true,
|
||
"delay": shouldRescheduleAfter,
|
||
"success": completionSuccess
|
||
])
|
||
} catch {
|
||
rejecter("BACKGROUND_TASK_ERROR", error.localizedDescription, error)
|
||
}
|
||
}
|
||
}
|
||
|
||
@objc
|
||
func cancelAll(
|
||
_ resolver: @escaping RCTPromiseResolveBlock,
|
||
rejecter: @escaping RCTPromiseRejectBlock
|
||
) {
|
||
guard #available(iOS 13.0, *) else {
|
||
rejecter(
|
||
"BACKGROUND_TASK_UNAVAILABLE",
|
||
"BGTaskScheduler requires iOS 13 or later.",
|
||
nil
|
||
)
|
||
return
|
||
}
|
||
|
||
queue.async {
|
||
BGTaskScheduler.shared.cancelAllTaskRequests()
|
||
resolver(["cancelled": true])
|
||
}
|
||
}
|
||
|
||
@objc
|
||
func getPendingRequests(
|
||
_ resolver: @escaping RCTPromiseResolveBlock,
|
||
rejecter: @escaping RCTPromiseRejectBlock
|
||
) {
|
||
guard #available(iOS 13.0, *) else {
|
||
rejecter(
|
||
"BACKGROUND_TASK_UNAVAILABLE",
|
||
"BGTaskScheduler requires iOS 13 or later.",
|
||
nil
|
||
)
|
||
return
|
||
}
|
||
|
||
BGTaskScheduler.shared.getPendingTaskRequests { [weak self] requests in
|
||
guard let self else { return }
|
||
|
||
let mapped = requests
|
||
.filter { request in
|
||
guard let identifier = self.identifier else {
|
||
return true
|
||
}
|
||
return request.identifier == identifier
|
||
}
|
||
.map { request -> [String: Any] in
|
||
var payload: [String: Any] = [
|
||
"identifier": request.identifier
|
||
]
|
||
|
||
if let date = request.earliestBeginDate {
|
||
payload["earliestBegin"] = date.timeIntervalSince1970 * 1000
|
||
} else {
|
||
payload["earliestBegin"] = NSNull()
|
||
}
|
||
|
||
if let processingRequest = request as? BGProcessingTaskRequest {
|
||
payload["requiresNetworkConnectivity"] = processingRequest.requiresNetworkConnectivity
|
||
payload["requiresExternalPower"] = processingRequest.requiresExternalPower
|
||
payload["type"] = "processing"
|
||
} else {
|
||
payload["type"] = "refresh"
|
||
}
|
||
|
||
return payload
|
||
}
|
||
|
||
resolver(mapped)
|
||
}
|
||
}
|
||
|
||
@objc
|
||
func backgroundRefreshStatus(
|
||
_ resolver: @escaping RCTPromiseResolveBlock,
|
||
rejecter: @escaping RCTPromiseRejectBlock
|
||
) {
|
||
guard Thread.isMainThread else {
|
||
DispatchQueue.main.async { [weak self] in
|
||
self?.backgroundRefreshStatus(resolver, rejecter: rejecter)
|
||
}
|
||
return
|
||
}
|
||
|
||
let status = UIApplication.shared.backgroundRefreshStatus
|
||
switch status {
|
||
case .available:
|
||
resolver("available")
|
||
case .denied:
|
||
resolver("denied")
|
||
case .restricted:
|
||
resolver("restricted")
|
||
@unknown default:
|
||
resolver("unknown")
|
||
}
|
||
}
|
||
|
||
@objc
|
||
func simulateLaunch(
|
||
_ 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 {
|
||
NSLog("[BackgroundTaskBridge] ⚠️ 没有 JS 监听器注册")
|
||
rejecter(
|
||
"NO_LISTENERS",
|
||
"没有 JS 监听器注册后台事件。请确保应用已完全初始化。",
|
||
nil
|
||
)
|
||
return
|
||
}
|
||
|
||
NSLog("[BackgroundTaskBridge] 模拟触发后台任务...")
|
||
DispatchQueue.main.async { [weak self] in
|
||
guard let self else { return }
|
||
|
||
self.sendEvent(
|
||
withName: "BackgroundTaskBridge.execute",
|
||
body: [
|
||
"identifier": self.identifier ?? "simulated",
|
||
"timestamp": Date().timeIntervalSince1970 * 1000,
|
||
"simulated": true
|
||
]
|
||
)
|
||
NSLog("[BackgroundTaskBridge] ✅ 模拟后台任务已触发")
|
||
resolver(["simulated": true])
|
||
}
|
||
#endif
|
||
}
|
||
|
||
@objc
|
||
func markJSReady(
|
||
_ resolver: @escaping RCTPromiseResolveBlock,
|
||
rejecter: @escaping RCTPromiseRejectBlock
|
||
) {
|
||
queue.async { [weak self] in
|
||
guard let self else { return }
|
||
|
||
let hadPendingTask = self.waitingForJSListeners && self.pendingTaskPayload != nil
|
||
self.emitPendingTaskIfPossible()
|
||
resolver([
|
||
"pendingTaskFlushed": hadPendingTask
|
||
])
|
||
}
|
||
}
|
||
|
||
// MARK: - Private helpers
|
||
|
||
@available(iOS 13.0, *)
|
||
private func scheduleTask(after delay: TimeInterval) throws {
|
||
guard let identifier else {
|
||
throw NSError(
|
||
domain: "BackgroundTaskBridge",
|
||
code: 1,
|
||
userInfo: [NSLocalizedDescriptionKey: "Identifier is missing. Configure the bridge first."]
|
||
)
|
||
}
|
||
|
||
// 先检查待处理的任务
|
||
BGTaskScheduler.shared.getPendingTaskRequests { requests in
|
||
let existingTasks = requests.filter { $0.identifier == identifier }
|
||
NSLog("[BackgroundTaskBridge] 当前待处理任务数: \(existingTasks.count)")
|
||
}
|
||
|
||
// 取消之前的任务请求,避免重复调度
|
||
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: identifier)
|
||
NSLog("[BackgroundTaskBridge] 已取消之前的任务请求: \(identifier)")
|
||
|
||
// 根据任务类型选择合适的请求类
|
||
// processing 类型使用 BGProcessingTaskRequest,fetch 类型使用 BGAppRefreshTaskRequest
|
||
let request: BGTaskRequest
|
||
|
||
switch kind {
|
||
case .processing:
|
||
request = BGProcessingTaskRequest(identifier: identifier)
|
||
case .refresh:
|
||
request = BGAppRefreshTaskRequest(identifier: identifier)
|
||
}
|
||
|
||
// 设置最早开始时间
|
||
// 注意:实际执行时间由系统决定,可能会延迟
|
||
// 系统通常会在设备空闲、网络连接良好、电量充足时执行
|
||
if let appRefreshRequest = request as? BGAppRefreshTaskRequest {
|
||
appRefreshRequest.earliestBeginDate = Date(timeIntervalSinceNow: delay)
|
||
} else if let processingRequest = request as? BGProcessingTaskRequest {
|
||
processingRequest.earliestBeginDate = Date(timeIntervalSinceNow: delay)
|
||
processingRequest.requiresNetworkConnectivity = requiresNetworkConnectivity
|
||
processingRequest.requiresExternalPower = requiresExternalPower
|
||
}
|
||
|
||
do {
|
||
try BGTaskScheduler.shared.submit(request)
|
||
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)")
|
||
|
||
// 打印详细错误信息
|
||
if let bgError = error as NSError? {
|
||
NSLog("[BackgroundTaskBridge] - 错误域: \(bgError.domain)")
|
||
NSLog("[BackgroundTaskBridge] - 错误码: \(bgError.code)")
|
||
NSLog("[BackgroundTaskBridge] - 错误信息: \(bgError.localizedDescription)")
|
||
}
|
||
|
||
throw error
|
||
}
|
||
}
|
||
|
||
@available(iOS 13.0, *)
|
||
private func handle(task: BGTask) {
|
||
queue.async { [weak self] in
|
||
guard let self else { return }
|
||
|
||
self.currentTask = task
|
||
NSLog("[BackgroundTaskBridge] ===== 开始处理后台任务 =====")
|
||
NSLog("[BackgroundTaskBridge] 任务标识符: \(task.identifier)")
|
||
NSLog("[BackgroundTaskBridge] 任务类型: \(type(of: task))")
|
||
NSLog("[BackgroundTaskBridge] 当前时间: \(Date().description)")
|
||
|
||
if self.identifier == nil {
|
||
self.identifier = task.identifier
|
||
NSLog("[BackgroundTaskBridge] 使用任务标识符初始化 identifier: \(task.identifier)")
|
||
}
|
||
|
||
// 设置任务过期处理器
|
||
task.expirationHandler = { [weak self] in
|
||
guard let self else { return }
|
||
NSLog("[BackgroundTaskBridge] 后台任务即将过期")
|
||
self.queue.async {
|
||
if let currentTask = self.currentTask {
|
||
currentTask.setTaskCompleted(success: false)
|
||
}
|
||
self.currentTask = nil
|
||
|
||
// 即使任务过期,也要重新调度下一次任务
|
||
self.rescheduleTask()
|
||
}
|
||
|
||
guard self.hasListeners else { return }
|
||
|
||
DispatchQueue.main.async {
|
||
self.sendEvent(
|
||
withName: "BackgroundTaskBridge.expire",
|
||
body: [
|
||
"identifier": self.identifier ?? "",
|
||
"timestamp": Date().timeIntervalSince1970 * 1000
|
||
]
|
||
)
|
||
}
|
||
}
|
||
|
||
let payload: [String: Any] = [
|
||
"identifier": self.identifier ?? task.identifier,
|
||
"timestamp": Date().timeIntervalSince1970 * 1000
|
||
]
|
||
|
||
guard self.hasListeners else {
|
||
NSLog("[BackgroundTaskBridge] 暂无 JS 监听器,等待 JS 初始化 (最多 \(Int(self.pendingTaskWaitTimeout)) 秒)")
|
||
self.cacheTaskForLater(payload: payload)
|
||
return
|
||
}
|
||
|
||
NSLog("[BackgroundTaskBridge] 准备发送后台任务执行事件到JS")
|
||
NSLog("[BackgroundTaskBridge] hasListeners: \(self.hasListeners)")
|
||
self.emitTaskToJS(payload: payload)
|
||
NSLog("[BackgroundTaskBridge] ✅ 事件已发送到JS层")
|
||
}
|
||
}
|
||
|
||
@available(iOS 13.0, *)
|
||
private func executeDefaultTaskAndReschedule() {
|
||
waitingForJSListeners = false
|
||
pendingTaskPayload = nil
|
||
pendingTaskTimeoutWorkItem?.cancel()
|
||
pendingTaskTimeoutWorkItem = nil
|
||
|
||
// 执行默认的后台任务逻辑(当没有JS监听器时)
|
||
NSLog("[BackgroundTaskBridge] 执行默认后台任务逻辑")
|
||
|
||
// 这里可以添加一些基本的任务逻辑,比如:
|
||
// 1. 检查应用状态
|
||
// 2. 更新本地存储
|
||
// 3. 准备下次任务的数据
|
||
|
||
// 完成当前任务
|
||
if let currentTask = self.currentTask {
|
||
currentTask.setTaskCompleted(success: true)
|
||
self.currentTask = nil
|
||
}
|
||
|
||
// 重新调度下一次任务
|
||
self.rescheduleTask()
|
||
}
|
||
|
||
@available(iOS 13.0, *)
|
||
private func rescheduleTask() {
|
||
guard let identifier = self.identifier else {
|
||
NSLog("[BackgroundTaskBridge] 无法重新调度任务:标识符为空")
|
||
return
|
||
}
|
||
|
||
// 先取消之前的任务请求,避免重复
|
||
BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: identifier)
|
||
|
||
// 使用与配置时相同的任务类型
|
||
let request: BGTaskRequest
|
||
switch self.kind {
|
||
case .processing:
|
||
request = BGProcessingTaskRequest(identifier: identifier)
|
||
if let processingRequest = request as? BGProcessingTaskRequest {
|
||
processingRequest.earliestBeginDate = Date(timeIntervalSinceNow: self.defaultDelay)
|
||
processingRequest.requiresNetworkConnectivity = self.requiresNetworkConnectivity
|
||
processingRequest.requiresExternalPower = self.requiresExternalPower
|
||
}
|
||
case .refresh:
|
||
request = BGAppRefreshTaskRequest(identifier: identifier)
|
||
if let appRefreshRequest = request as? BGAppRefreshTaskRequest {
|
||
appRefreshRequest.earliestBeginDate = Date(timeIntervalSinceNow: self.defaultDelay)
|
||
}
|
||
}
|
||
|
||
do {
|
||
try BGTaskScheduler.shared.submit(request)
|
||
NSLog("[BackgroundTaskBridge] 已重新调度后台任务,标识符: \(identifier),延迟: \(self.defaultDelay)秒")
|
||
} catch {
|
||
NSLog("[BackgroundTaskBridge] 重新调度后台任务失败: \(error.localizedDescription)")
|
||
|
||
// 如果调度失败,尝试使用更短的延迟时间重试
|
||
let retryDelay = min(self.defaultDelay * 0.5, 5 * 60) // 最多5分钟
|
||
request.earliestBeginDate = Date(timeIntervalSinceNow: retryDelay)
|
||
|
||
do {
|
||
try BGTaskScheduler.shared.submit(request)
|
||
NSLog("[BackgroundTaskBridge] 使用重试延迟重新调度后台任务成功: \(retryDelay)秒")
|
||
} catch {
|
||
NSLog("[BackgroundTaskBridge] 重试调度后台任务也失败: \(error.localizedDescription)")
|
||
}
|
||
}
|
||
}
|
||
|
||
private func cacheTaskForLater(payload: [String: Any]) {
|
||
waitingForJSListeners = true
|
||
pendingTaskPayload = payload
|
||
|
||
pendingTaskTimeoutWorkItem?.cancel()
|
||
|
||
NSLog("[BackgroundTaskBridge] 缓存后台任务等待JS监听器...")
|
||
NSLog("[BackgroundTaskBridge] 等待超时时间: \(Int(pendingTaskWaitTimeout))秒")
|
||
|
||
let timeoutItem = DispatchWorkItem { [weak self] in
|
||
guard let self else { return }
|
||
guard self.waitingForJSListeners else { return }
|
||
|
||
NSLog("[BackgroundTaskBridge] ⚠️ 等待 JS 监听器超时,执行默认处理")
|
||
self.waitingForJSListeners = false
|
||
self.pendingTaskPayload = nil
|
||
self.pendingTaskTimeoutWorkItem = nil
|
||
self.executeDefaultTaskAndReschedule()
|
||
}
|
||
|
||
pendingTaskTimeoutWorkItem = timeoutItem
|
||
queue.asyncAfter(deadline: .now() + pendingTaskWaitTimeout, execute: timeoutItem)
|
||
NSLog("[BackgroundTaskBridge] 已设置超时计时器")
|
||
}
|
||
|
||
private func emitPendingTaskIfPossible() {
|
||
guard hasListeners else { return }
|
||
guard waitingForJSListeners, let payload = pendingTaskPayload else { return }
|
||
|
||
pendingTaskTimeoutWorkItem?.cancel()
|
||
pendingTaskTimeoutWorkItem = nil
|
||
waitingForJSListeners = false
|
||
pendingTaskPayload = nil
|
||
|
||
NSLog("[BackgroundTaskBridge] JS 监听器已就绪,发送缓存的后台任务事件")
|
||
emitTaskToJS(payload: payload)
|
||
}
|
||
|
||
private func emitTaskToJS(payload: [String: Any]) {
|
||
DispatchQueue.main.async { [weak self] in
|
||
guard let self else { return }
|
||
NSLog("[BackgroundTaskBridge] 正在发送事件到JS层...")
|
||
self.sendEvent(
|
||
withName: "BackgroundTaskBridge.execute",
|
||
body: payload
|
||
)
|
||
NSLog("[BackgroundTaskBridge] ✅ 事件发送成功")
|
||
}
|
||
}
|
||
}
|