Home > Enterprise >  How to use sink to assign to an array
How to use sink to assign to an array

Time:11-26

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)
            
       
    }
    
}

CodePudding user response:

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)
}

CodePudding user response:

If you want to use Combine you need an ObservableObject and assign the end of the pipeline to an @Published var. When the object is deinit it will cancel the pipeline automatically.

The advantatage of async/await and .task is we don't need objects anymore and the task is cancelled when the UIView (that SwiftUI manages) dissapears.

  • Related