Marco
Marco

Reputation: 501

SwiftUI parse Multilayer JSON Data from URL and build list

First an information: I am actually on the way learning swiftUI, I'm a total newbie. For my first project i decided to create a small app that loads articles from a joomla website. My API will respond to a query in the following structure:

{
"source": "my AppConnector for Joomla!",
"offset": 0,
"count": 0,
"results": [
    {
        "id": "8",
        "title": "Article 1",
        ...
    },
    {
        "id": "8",
        "title": "Article 2",
        ...
    }
]

}

In the future the API will return more complex structures but actually i'm struggling already with that one. All swiftUI examples & videos i've found are just explaining how to retreive an array of items or are to old and shows depreacet code examples (with the one-dimensional examples i have already successfully created a list view of items but that's not what i want).

I've created the following structs:

struct Welcome: Codable {
    let source: String
    let offset, count: Int
    let results: [Result]
}


// MARK: - Result
struct Result: Codable {
    let id, title, alias, introtext: String
    let fulltext, state, catid, created: String
    let createdBy, createdByAlias, modified, modifiedBy: String
    let checkedOut, checkedOutTime, publishUp, publishDown: String
    let images, urls, attribs, version: String
    let ordering, metakey, metadesc, access: String
    let hits, metadata, featured, language: String
    let xreference, note, slug, articleID: String
    let introImage, fullImage: String
}

and the following fetcher:

import Foundation
import SwiftUI
import Combine


public class ArticlesFetcher: ObservableObject {
    @Published var articles = [Welcome]()
    
    init(){
        load()
    }
    
    func load() {
        let url = URL(string: "https://nx-productions.ch/index.php/news")! //This is a public demo url feel free to check the jsondata (SecurityToken temporary disabled)
    
        URLSession.shared.dataTask(with: url) {(data,response,error) in
            do {
                if let d = data {
                    let decodedLists = try JSONDecoder().decode([Welcome].self, from: d)
                    DispatchQueue.main.async {
                        self.articles = decodedLists
                    }
                }else {
                    print("No Data")
                }
            } catch {
                print ("Error")
            }
            
        }.resume()
         
    }
}

My view looks like this:

struct ContentView: View {

    @ObservedObject var fetcher = ArticlesFetcher()
    
    var body: some View {
        VStack {
            List(fetcher.articles.results) { article in
                VStack (alignment: .leading) {
                    Text(article.title)
                    Text(article.articleId)
                        .font(.system(size: 11))
                        .foregroundColor(Color.gray)
                }
            }
        }
    }
}

What i don't understand is the view part - i am not able to point into the fields, with the example above i get compiler errors like "Value of type '[Welcome]' has no member 'results'" or "Value of type 'Int' has no member 'title'"

I think i may just not understand something aboutmy structure or how to loop through it. Thanks for any advise.

Upvotes: 0

Views: 784

Answers (1)

vadian
vadian

Reputation: 285064

The JSON starts with a { so it's a dictionary. And the type of articles is wrong.

Replace

@Published var articles = [Welcome]()

with

@Published var articles = [Result]()

and replace

let decodedLists = try JSONDecoder().decode([Welcome].self, from: d)
DispatchQueue.main.async {
   self.articles = decodedLists
}

with

let decodedLists = try JSONDecoder().decode(Welcome.self, from: d)
DispatchQueue.main.async {
   self.articles = decodedLists.results
}

Finally but not related replace meaningless

print ("Error")

with

print(error)

Upvotes: 1

Related Questions