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

如何使用Combine的sink将JSON解码结果赋值给数组

问题分析与解决方案

你的Combine实现存在三个关键问题,导致无法正确将解码后的值赋值给数组:

1. 未配置JSONDecoder的日期解码策略

原URLSession代码中明确设置了json.dateDecodingStrategy = .iso8601,但Combine代码里的JSONDecoder()使用默认初始化,没有配置该策略,导致JSON中的日期字段解码失败,触发replaceError后返回默认的[NewsItem.shared]。

2. 未在主线程更新@State变量

Combine的sink闭包默认在后台线程执行,而SwiftUI的@State变量必须在主线程更新,否则无法触发视图刷新。

3. 错误使用@State存储订阅对象

request作为Combine订阅的存储容器,不需要用@State修饰——@State仅用于视图需要响应的状态,普通变量即可满足订阅存储需求。


修正后的完整代码

struct NewsItem: Decodable {
    let id: Int
    let title: String
    let strap: String
    let url: URL
    let main_image: URL
    let published_date: Date
    
    static let shared = NewsItem(id: 0, title: "", strap: "", url: URL(string: "https://www.hackingwithswift.com/articles/239/wwdc21-wrap-up-and-recommended-talks")!, main_image: URL(string: "https://www.hackingwithswift.com/resize/300/uploads/wwdc-21@2x.jpg")!, published_date: Date())
}

struct CardView: View {
    @State private var news = [NewsItem]()
    // 用普通变量存储订阅,无需@State
    private var cancellables = Set<AnyCancellable>()
    
    var body: some View {
        List {
            ForEach(news, id:\.id) { news in
                Text(news.title)
                Text("\(news.published_date)")
                Link("Goto Link", destination: news.url)
                AsyncImage(url: news.main_image)
                    .frame(width: 50, height: 50)
            }
        }
        .onAppear {
            fetchData() // Combine本身是异步流,无需Task和await包裹
        }
    }
    
    func fetchData() {
        let url = URL(string: "https://www.hackingwithswift.com/samples/headlines.json")!
        
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .iso8601 // 配置日期解码策略
        
        URLSession.shared.dataTaskPublisher(for: url)
            .map(\.data)
            .decode(type: [NewsItem].self, decoder: decoder)
            .replaceError(with: [NewsItem.shared])
            .receive(on: DispatchQueue.main) // 切换到主线程更新UI状态
            .sink(receiveValue: { items in
                news = items
            })
            .store(in: &cancellables)
    }
}

额外说明

  • 若需要更细致的错误处理,可去掉replaceError,改用sink(receiveCompletion:receiveValue:)捕获并处理错误,而非直接返回默认值。
  • Combine与async/await是两种独立的异步处理方案,无需在同一个函数中混用,本场景下Combine实现完全不需要async修饰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 20:10:34