You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS异步下载API数据后同步更新Core Data的问题求助

问题核心原因

你当前的updateAPI是同步执行逻辑,但download1是异步网络请求——发起请求后立刻去遍历download.quotes时,请求还没完成,数组里没有最新数据,自然无法正确更新Core Data。


解决方案

方案一:使用DispatchGroup等待所有请求完成

通过DispatchGroup跟踪所有异步请求的完成状态,等全部请求结束后再统一更新Core Data并保存:

修改updateAPI函数

func updateAPI() {
    let updateGroup = DispatchGroup()
    
    for stock in depot.aktienKatArray {
        guard let symbol = stock.aKat_symbol, !symbol.isEmpty else { continue }
        
        updateGroup.enter()
        download.download1(stock: symbol) { [weak self] result in
            defer { updateGroup.leave() } // 无论成功失败都标记请求完成
            guard let self = self else { return }
            
            switch result {
            case .success(let quotes):
                // 直接用请求返回的结果更新,不依赖全局数组,避免数据不一致
                if let targetQuote = quotes.first(where: { $0.symbol == symbol }) {
                    // Core Data上下文线程不安全,确保在正确队列更新
                    PersistenceController.shared.container.viewContext.perform {
                        stock.aKat_currPerShare = Double(targetQuote.price) ?? 0
                    }
                }
            case .failure(let err):
                print("下载\(symbol)数据失败: \(err)")
            }
        }
    }
    
    // 所有请求完成后,在主线程保存上下文
    updateGroup.notify(queue: DispatchQueue.main) {
        PersistenceController.shared.saveContext()
        print("所有股票数据已更新并保存")
    }
}

优化download1函数(移除冗余的内部DispatchGroup)

func download1(stock: String, completion: @escaping (Result<[Quote], NetworkError>) -> Void) {
    guard let url = URL(string: API.quoteUrl(for: stock)) else {
        completion(.failure(.invalidURL))
        return
    }
    
    NetworkManager<GlobalQuoteResponse>().fetch(from: url) { result in
        switch result {
        case .failure(let err):
            print(err)
            completion(.failure(err))
        case .success(let resp):
            let quotes = [resp.quote]
            // 在主线程更新全局数组(如果需要保留)
            DispatchQueue.main.async {
                self.quotes.append(contentsOf: quotes)
            }
            completion(.success(quotes))
        }
    }
}

方案二:使用Swift Async/Await(推荐,代码更简洁易维护)

利用Swift并发框架重构异步逻辑,摆脱回调地狱:

第一步:给NetworkManager添加异步请求方法

extension NetworkManager {
    func fetchAsync(from url: URL) async throws -> T {
        return try await withCheckedThrowingContinuation { continuation in
            self.fetch(from: url) { result in
                switch result {
                case .success(let data):
                    continuation.resume(returning: data)
                case .failure(let err):
                    continuation.resume(throwing: err)
                }
            }
        }
    }
}

第二步:重构download1为异步函数

func download1(stock: String) async throws -> [Quote] {
    guard let url = URL(string: API.quoteUrl(for: stock)) else {
        throw NetworkError.invalidURL
    }
    
    let resp = try await NetworkManager<GlobalQuoteResponse>().fetchAsync(from: url)
    let quotes = [resp.quote]
    // 在主线程更新全局数组(如果需要保留)
    await MainActor.run {
        self.quotes.append(contentsOf: quotes)
    }
    return quotes
}

第三步:修改updateAPI为异步函数

func updateAPI() async {
    for stock in depot.aktienKatArray {
        guard let symbol = stock.aKat_symbol, !symbol.isEmpty else { continue }
        
        do {
            let quotes = try await download.download1(stock: symbol)
            if let targetQuote = quotes.first(where: { $0.symbol == symbol }) {
                // 用MainActor确保在主线程更新Core Data(viewContext绑定主线程)
                await MainActor.run {
                    stock.aKat_currPerShare = Double(targetQuote.price) ?? 0
                }
            }
        } catch {
            print("更新\(symbol)数据失败: \(error)")
        }
    }
    
    // 所有更新完成后保存上下文
    await MainActor.run {
        PersistenceController.shared.saveContext()
        print("所有股票数据已更新并保存")
    }
}

调用方式(在异步上下文执行)

// 比如在ContentView的按钮点击或task修饰符中
Task {
    await updateAPI()
}

内容的提问来源于stack exchange,提问作者SPR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 20:35:26