How to parse single JSON object from URL in SwiftUI using combine?

Viewed 1845

Currently, I am trying to parse a single object and decode it ready for an Output into a Text() in my SwiftUI view, but I cannot create an instance without parameters.

I have found many resources for dynamically creating lists, but this is just one standalone object. A lot of resources have shown how to create a @Published variable using arrays, but I only want one object.

The decoding structs I have built are:

// MARK: - Welcome
struct Welcome: Codable {
    let verse: Verse
}

// MARK: - Verse
struct Verse: Codable {
    let details: Details
    let notice: String
}

// MARK: - Details
struct Details: Codable {
    let text, reference, version: String
    let verseurl: String
}

I created the following fetcher:

public class VerseFetcher: ObservableObject {
    @Published var verse = Welcome()

    init(){
        load()
    }

    func load() {
        let url = URL(string: "https://beta.ourmanna.com/api/v1/get/?format=json")!

        URLSession.shared.dataTask(with: url) {(data,response,error) in
            do {
                if let d = data {
                    let webData = try JSONDecoder().decode(Verse.self, from: d)
                    DispatchQueue.main.async {
                        self.verse = webData
                    }
                }else {
                    print("No Data")
                }
            } catch {
                print ("Error here")
            }

        }.resume()
    }
}

I have tried this solution to get an Output:

struct DailyVerseView: View {
    @ObservedObject var fetcher = VerseFetcher()

    var body: some View {

        Text(self.fetchVerse.todos.verse.details.text)
            .fontWeight(.semibold)
            .font(.caption)
            .foregroundColor(.secondary)
            .padding(.leading, 16)
            .padding(.trailing, 16)
            .padding(.top, 8)
            .padding(.bottom, 8)
    }
}

The JSON I am reading from the URL at https://beta.ourmanna.com/api/v1/get/?format=json is:

{
  "verse": {
    "details": {
      "text": "The world and its desires pass away, but the man who does the will of God lives forever.",
      "reference": "1 John 2:17",
      "version": "NIV",
      "verseurl": "http://www.ourmanna.com/"
    },
    "notice": "Powered by OurManna.com"
  }
}

How can I create the Verse struct without parameters, as it is dynamic from the URL response?

1 Answers

The code mentioned in your question does not compile. To make your code working I have made changes to your code and added some comments as well.

struct DailyVerseView: View {
    @ObservedObject var fetcher = VerseFetcher()
var body: some View {

    Text(self.fetcher.verse?.verse.details.text ?? "") // Changed fetchVerse to fetcher
        .fontWeight(.semibold)
        .font(.caption)
        .foregroundColor(.secondary)
        .padding(.leading, 16)
        .padding(.trailing, 16)
        .padding(.top, 8)
        .padding(.bottom, 8)
}}

public class VerseFetcher: ObservableObject {
@Published var verse: Welcome?

init(){
    load()
}

func load() {
    let url = URL(string: "https://beta.ourmanna.com/api/v1/get/?format=json")!

    URLSession.shared.dataTask(with: url) {(data,response,error) in
        do {
            if let d = data {
                //Decoding Welcome instead of Verse directly
                let webData = try JSONDecoder().decode(Welcome.self, from: d)
                DispatchQueue.main.async {
                    self.verse = webData
                }
            }else {
                print("No Data")
            }
        } catch {
            print ("Error here")
        }

    }.resume()
}}


// MARK: - Welcome
struct Welcome: Codable {
    let verse: Verse
}

// MARK: - Verse
struct Verse: Codable {
    let details: Details
    let notice: String
}

// MARK: - Details
struct Details: Codable {
    let text, reference, version: String
    let verseurl: String
}
Related