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.