SwiftUI视图展示锻炼数据前,如何确保Firebase Storage视频URL已完成更新?
SwiftUI视图展示锻炼数据前,如何确保Firebase Storage视频URL已完成更新?
我来帮你分析下问题所在,然后给出修复方案~
你当前的代码里,filterExercises函数有两个核心问题导致视图先拿到了没有视频URL的锻炼数据:
- 你在
forEach里给每个锻炼单独开了Task去请求URL,但这些Task是后台异步执行的,函数本身直接返回了还没更新URL的过滤后数组,所以exercises会先被赋值为无URL的数据。 - 你在
Task里修改的exercise是forEach循环里的副本,原数组里的元素根本没被更新;而且就算原元素被修改了,因为你只是修改数组内的元素,没有替换整个数组,@Published也不会触发视图更新。
修复方案
我们需要重构filterExercises函数,确保所有视频URL请求完成后,再返回完整的、带URL的锻炼数组,这样视图拿到的就是最终数据。这里用withTaskGroup来并发处理所有URL请求,既保证效率,又能等待所有请求完成:
import Combine import FirebaseStorage import Foundation class ExerciseViewModel<DS: DataService>: ObservableObject where DS.Item == Exercise { @Published var exercises: [Exercise] = [] private let dataService: DS private let storage = Storage.storage(url: "someURL") private var cancellables = Set<AnyCancellable>() init(dataService: DS, idArray: [String]) { self.dataService = dataService dataService.getData() .sink { error in print("Exercises request failed: \(String(describing: error))") } receiveValue: { [weak self] data in guard let self else { return } Task { // 等待所有URL解析完成后,再给exercises赋值 self.exercises = await self.filterExercises(exercises: data, by: idArray) } } .store(in: &cancellables) } private func filterExercises(exercises: [Exercise], by idArray: [String]) async -> [Exercise] { // 第一步:先过滤出需要的锻炼数据 let filteredExercises = exercises.filter { idArray.contains($0.id ?? "") } // 第二步:用TaskGroup并发处理所有视频URL请求,确保全部完成后返回 return await withTaskGroup(of: Exercise.self) { group in for var exercise in filteredExercises { group.addTask { [weak self] in guard let self else { return exercise } do { if let videoName = exercise.video, !videoName.isEmpty { // 异步获取下载URL并更新到exercise对象 let downloadURL = try await self.storage.reference() .child("video/\(videoName)") .downloadURL() exercise.videoUrl = downloadURL.absoluteString print("Video URL resolved: \(exercise.videoUrl ?? "")") } } catch { print("Error resolving video URL for exercise \(exercise.id ?? "unknown"): \(error)") } return exercise } } // 收集所有Task的结果,组成最终数组 var updatedExercises: [Exercise] = [] for await processedExercise in group { updatedExercises.append(processedExercise) } return updatedExercises } } }
关键修改点说明:
- 用
withTaskGroup替代原来的forEach + 独立Task:TaskGroup可以让我们并发执行所有URL请求,同时等待所有任务完成后再收集结果,保证返回的数组每个元素都已经更新了视频URL。 - 移除了原代码中
self?.exercises = await self?.filterExercises(...) ?? []的可选链,改为先判断self存在再执行,避免不必要的可选值处理。 - 每个TaskGroup的子任务负责处理单个锻炼的URL更新,返回更新后的对象,最后统一收集成数组再返回,这样赋值给
exercises时,是直接替换整个数组,能正确触发@Published的视图更新通知。
这样修改后,你的视图就会在所有视频URL都解析完成后,才拿到完整的锻炼数据,不会再出现先展示无URL内容的问题啦~
备注:内容来源于stack exchange,提问作者Elena Rubilova
相关产品推荐
相关产品推荐

