I’m pretty new to SWIFT, especially parsing JSON I want to display specific item from array in my friend’s JSON API.
I have this API url: https://api.novella-designer.com/api/:version/tree/preview/307ea989-a7aa-4496-a86e-0577cec10754
My problem is, I can’t display one specific item from that array, here’s my JSON decoding file:
import Foundation
@MainActor
class JsonNovella: ObservableObject {
private struct Returned: Codable {
var slides: [Slides]
var status: String
}
struct Slides: Codable, Hashable {
var slide: Slide
}
struct Slide: Codable, Hashable {
var action: [Action]
var id: String
}
struct Action: Codable, Hashable{
var name: String
var map_action: MapAction
}
struct MapAction: Codable, Hashable{
var transaction_elements: [TransitionElements]?
}
struct TransitionElements: Codable, Hashable{
var component_map: ComponentMap?
}
struct ComponentMap: Codable, Hashable{
var elements: [Elements]
}
struct Elements: Codable, Hashable{
var color: String
var name: String
var image_url: String?
var type: String
var text: String?
}
@Published var urlString = "https://api.novella-designer.com/api/:version/tree/preview/307ea989-a7aa-4496-a86e-0577cec10754"
@Published var slides: [Slides] = []
@Published var status = ""
func getData() async {
print("We are accessing the url (urlString)")
guard let url = URL(string: urlString) else {
print("ERROR: could not create URL from (urlString)")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
guard let returned = try? JSONDecoder().decode(Returned.self, from: data) else {
print("đŸ˜ ERROR: could not decode data from (urlString)")
return
}
self.status = returned.status
self.slides = returned.slides
print(returned.slides)
} catch {
print("ERROR: could not get data from (urlString)")
}
}
}
And here is my SWIFTUI file, I don’t actually know, if im doing right. I’ve watched a lot of videos about parsing JSON, but couldn’t find the solution for me.
import Foundation
import SwiftUI
struct NovellaViewer: View {
var loop = 5
@StateObject var novellaJS = JsonNovella()
var body: some View {
ScrollView() {
VStack{
ForEach(novellaJS.slides, id: .self) { slides in
ZStack{
ForEach(slides.slide.action, id: .self){ slide in
ForEach(slide.map_action.transaction_elements ?? [], id: .self) { slide in
ForEach(slide.component_map?.elements ?? [], id: .self) { slide in
AsyncImage(url: URL(string: slide.image_url ?? "")) { phase in
if let image = phase.image{
image
.resizable()
.scaledToFit()
}
}
}
}
}
}
}
}
.task {
await novellaJS.getData()
}
}
}
}
#Preview{
NovellaViewer()
}
I’ve tried to select item from array like using [1], but it doesn’t work for me, I guess, this because it’s JSON, not just array. So I want to have call 1-2 objects from array, just to make character and background behind him.
Can somebody at least give me some info or links. Because im literally spend a week on it.
2
Answers
You need to remove all of the
id: .self
that is a violation of theForEach
API where theid
param must be a unique identifier of the data it cannot be the data itself.Best conform
Slide
toIdentifiable
, make avar id
then it will work withForEach
with noid
param required.class JsonNovella: ObservableObject
needs to be removed or at least not be@MainActor
. Since you are using async/await and not Combine it is best to just remove that class and do this:Where slides is
@State
andnovellaJS
is a controller in anEnvironmentKey
....I can't display one specific item from that array...
To get a specific
item
, for example the first image url from your models,you need to go through the different model structures dealing with possible
Optional
values (I use
if let ...
) and assign it to a var (eg:firstUrl
).The working example code shows how to display the first image you get from the server (red border),
using
func getFirstUrl()
or alternativelyfunc getFirstUrl2()
Note, you should take the advice regarding naming of models, and use
Identifiable
when needed.