Search code examples
swiftswiftuijsondecoder

How to use sink to assign to an array


Working on a demo I switched to using Combine, but just cannot seem to find a way to assign the values that I get from json decoding in sink to point to an array , below is my code , as you can see in the commented out code using URLSession it was much easier …thanks

Currently I just see the default record

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/[email protected]")!, published_date: Date())
}


struct CardView: View {
    @State private var news = [NewsItem]()
    @State private var request = 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 {
            Task {
                await fetchData()
            }
        }
    }
    
    func fetchData() async {
        let url = URL(string: "https://www.hackingwithswift.com/samples/headlines.json")!
      
//            URLSession.shared.dataTask(with: url) { data, response, error in
//                if let error = error {
//                    print(error.localizedDescription)
//                } else if let data = data {
//                    let json = JSONDecoder()
//
//                    json.dateDecodingStrategy = .iso8601
//                    do {
//                        let user = try  json.decode([NewsItem].self, from: data)
//                        news = user
//                    } catch {
//                        print(error.localizedDescription)
//                    }
//                }
//            }.resume()
        
        URLSession.shared.dataTaskPublisher(for: url)
            .map(\.data)
            .decode(type: [NewsItem].self, decoder: JSONDecoder())
            .replaceError(with: [NewsItem.shared])
            .sink(receiveValue: { item in
                news = item
            })
            .store(in: &request)
            
       
    }
    
}

Solution

  • You are seeing the default output because you are replacing all errors. Use at least print to look at the error before replacing it.

    Turned out the issue here was the decoding of the Date. Applying the proper decoding strategy fixed the issue.

    func fetchData() async {
        //create custom decoder and apply dateDecodingStrategy
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .iso8601
        let url = URL(string: "https://www.hackingwithswift.com/samples/headlines.json")!
        
        URLSession.shared.dataTaskPublisher(for: url)
            .map(\.data)
            // use the custom decoder
            .decode(type: [NewsItem].self, decoder: decoder)
            // if an error occures at least print it
            .mapError({ error in
                print(error)
                return error
            })
            .replaceError(with: [NewsItem.shared])
            .sink(receiveValue: { item in
                news = item
            })
            .store(in: &request)
    }