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

SwiftUI:OnAppear调用部分网络抓取函数失效,Refreshable却正常?

问题分析与解决方案

核心可能原因

1. 并发请求触发目标网站反爬/系统限制

当onAppear同时发起两个请求时,目标网站可能对同一IP的并发请求做了拦截限制,而下拉刷新是单独发起请求,因此能绕过限制正常执行。

2. 单例Scrapper的并发资源冲突

你的Scrapper.shared是单例,如果内部未处理并发请求逻辑(比如共享了URLSession资源、用串行队列处理请求),同时调用两个抓取方法会导致资源抢占,其中一个请求无法正常执行。

3. onAppear执行时机的生命周期问题

SwiftUI的onAppear可能在视图未完全初始化时触发,此时依赖的资源(比如Scrapper的初始化状态)未就绪,导致请求失败;而下拉刷新触发时视图已完全就绪,因此请求正常。

具体排查与解决步骤

第一步:先确认问题本质

打开fetchHistorico中注释的print("scrapeando Historico")语句,运行后观察控制台:

  • 如果没有输出:说明fetchHistorico在onAppear中没被调用,检查视图绑定或触发逻辑
  • 如果输出了但没看到Entro en el historico:打印error的详细信息,定位是网络错误、解析错误还是被网站拦截
  • 如果输出了Entro en el historico:说明请求成功但视图没更新,检查historicoDolar是否为@Published属性(假设ViewModel是ObservableObject)

第二步:针对性解决

解决并发请求限制问题

给两个请求添加延迟,错开发起时间:

.onAppear {
    fetchDolar()
    // 延迟0.5秒发起第二个请求,避免并发拦截
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        fetchHistorico()
    }
}

或者用串行队列确保请求顺序执行:

.onAppear {
    let scrapeQueue = DispatchQueue(label: "com.yourapp.scrape")
    scrapeQueue.async {
        self.fetchDolar()
        scrapeQueue.asyncAfter(deadline: .now() + 0.5) {
            self.fetchHistorico()
        }
    }
}
修复Scrapper单例的并发问题

检查Scrapper内部的抓取方法,确保每个请求使用独立的URLSessionTask,如果用了共享队列,改成并发队列(带合适的QoS):

// 示例:Scrapper内部的并发队列设置
class Scrapper {
    static let shared = Scrapper()
    private let queue = DispatchQueue(label: "com.scrapper.queue", attributes: .concurrent)
    
    func scrappearDolar(completion: @escaping (Result<DolarData, Error>) -> Void) {
        queue.async {
            // 执行网络请求与解析,最后回到主线程回调
            DispatchQueue.main.async {
                completion(...)
            }
        }
    }
    
    // 同理处理scrapearHistoricoDolar方法
}
替换onAppear为更可靠的task修饰符

Swift 5.5+的.task修饰符更适配异步场景,会在视图就绪后执行,避免生命周期时机问题:

// 回调式写法
.task {
    fetchDolar()
    try? await Task.sleep(nanoseconds: 500_000_000)
    fetchHistorico()
}

// 推荐改成async/await版本(需要修改Scrapper的方法为异步)
func fetchDolar() async {
    do {
        let data = try await Scrapper.shared.scrappearDolar()
        DispatchQueue.main.async {
            self.dolar = data
            self.isLoading = false
        }
    } catch {
        DispatchQueue.main.async {
            self.error = error
            self.isLoading = false
        }
    }
}

// 然后在task中调用
.task {
    await fetchDolar()
    await fetchHistorico()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 22:48:10