skip to Main Content

I am trying to decode a JSON file from a URL; I am having issues initially. My product array is not receiving the JSON data; my code is below.

import SwiftUI
import SDWebImageSwiftUI

class DataManager: ObservableObject {
    struct Returned: Decodable {
        let product: [products]
    }

    @Published var productsArray: [products] = []

    func fetchData(completion: @escaping () -> Void) {
        guard let url = URL(string: "https://compassjobsapp.net/DummyJSONData.json") else {
            print("Invalid URL")
            completion()
            return
        }

        URLSession.shared.dataTask(with: url) { data, _, error in
            if let error = error {
                print("Network error: (error)")
                completion()
                return
            }

            if let data = data {
                    print("Received Data: (String(data: data, encoding: .utf8) ?? "Unable   to convert data to string")")
                do {
                    let decodedData = try JSONDecoder().decode(Returned.self, from: data)
                    DispatchQueue.main.async {
                        self.productsArray = decodedData.product
                        print("Decoded Data: (decodedData)")
                        print("Products Array: (self.productsArray)")
                    }
                    print("Decoded Data: (decodedData)")
                    completion()
                } catch {
                    print("Error decoding JSON: (error)")
                    completion()
                        
                }
            }
        }.resume()
    }
}

struct ProductListView: View {
    @ObservedObject private var dataManager = DataManager()

    var body: some View {
        NavigationView {
            if dataManager.productsArray.isEmpty {
                Text("No products available")
            } else {
                List(dataManager.productsArray) { products in
                    NavigationLink(destination: ProductDetailView(product: products)) {
                        VStack(alignment: .leading, spacing: 8) {
                            WebImage(url: URL(string: products.thumbnail))
                                .resizable()
                                .scaledToFit()
                                .frame(height: 150)
                                .cornerRadius(8)

                            Text(products.title)
                                .font(.headline)
                                .foregroundColor(.primary)

                            Text("Price: $(String(format: "%.2f", products.price))")
                                .font(.subheadline)
                                .foregroundColor(.secondary)
                        }
                        .padding(10)
                    }
                }
                .navigationTitle("Products")
                .onAppear {
                    dataManager.fetchData {
                        print("Products Array: (dataManager.productsArray)")
                        // Print the products array to the console
                        print(dataManager.productsArray)
                    }
                }
            }
        }
    }
}

struct ProductDetailView: View {
    let product: products

    var body: some View {
        Text("Product Detail View")
    }
}

I have tried debugging the code with print statements at all places in the code and debugging with the console logs. No, one of the print statistics I created points to the Xcode counsel. My iOS simulator displays the tab titled "No products available."

Where am I getting wrong?

2

Answers


  1. Change

     @ObservedObject private var dataManager = DataManager()
    

    To

     @StateObject private var dataManager = DataManager()
    

    Then move the onAppear

    Text("No products available")
            .onAppear {
                    dataManager.fetchData {
                        print("Products Array: (dataManager.productsArray)")
                        // Print the products array to the console
                        print(dataManager.productsArray)
                    }
                }
    

    You should see the print statements now.

    With your current setup the else never runs so onAppear never runs.

    Login or Signup to reply.
  2. Try this approach, where you have the .onAppear{...} attached to the NavigationStack
    (note NavigationView is deprecated). Also decode the json data using a valid Returned model and
    using @StateObject for your dataManager.

    Works well for me, using MacOS 14.2, with Xcode 15.1, tested on real ios17 devices and MacCatalyst.

    You will need to consult the docs of the API to determine if any of the properties
    of Returned and Product are optional, and add ? to those.

    Full working code:

    struct ContentView: View {
        var body: some View {
            ProductListView()
        }
    }
    
    class DataManager: ObservableObject {
        
        @Published var productsArray: [Product] = []  // <--- here
    
        func fetchData(completion: @escaping () -> Void) {
            guard let url = URL(string: "https://compassjobsapp.net/DummyJSONData.json") else {
                print("Invalid URL")
                completion()
                return
            }
    
            URLSession.shared.dataTask(with: url) { data, _, error in
                if let error = error {
                    print("Network error: (error)")
                    completion()
                    return
                }
    
                if let data = data {
                        print("Received Data: (String(data: data, encoding: .utf8) ?? "Unable   to convert data to string")")
                    do {
                        let decodedData = try JSONDecoder().decode(Returned.self, from: data)
                        DispatchQueue.main.async {
                            self.productsArray = decodedData.products  // <--- here `s`
                            print("Decoded Data: (decodedData)")
                            print("Products Array: (self.productsArray)")
                        }
                        print("Decoded Data: (decodedData)")
                        completion()
                    } catch {
                        print("Error decoding JSON: (error)")
                        completion()
                            
                    }
                }
            }.resume()
        }
    }
    
    struct ProductListView: View {
        @StateObject private var dataManager = DataManager()  // <--- here
    
        var body: some View {
            NavigationStack {  // <--- here
                if dataManager.productsArray.isEmpty {
                    Text("No products available")
                } else {
                    List(dataManager.productsArray) { products in
                        NavigationLink(destination: ProductDetailView(product: products)) {
                            VStack(alignment: .leading, spacing: 8) {
                                // commented for my testing
    //                            WebImage(url: URL(string: products.thumbnail))
    //                                .resizable()
    //                                .scaledToFit()
    //                                .frame(height: 150)
    //                                .cornerRadius(8)
    
                                Text(products.title)
                                    .font(.headline)
                                    .foregroundColor(.primary)
    
                                Text("Price: $(String(format: "%.2f", products.price))")
                                    .font(.subheadline)
                                    .foregroundColor(.secondary)
                            }
                            .padding(10)
                        }
                    }
                    .navigationTitle("Products")
                }
            }
            .onAppear {  // <--- here
                dataManager.fetchData {
                    print("Products Array: (dataManager.productsArray)")
                    // Print the products array to the console
                    print(dataManager.productsArray)
                }
            }
        }
    }
    
    struct ProductDetailView: View {
        let product: Product  // <--- here
    
        var body: some View {
            Text("Product Detail View")
            Text(product.title)
        }
    }
    
    struct Returned: Codable {
        let products: [Product]   // <--- here, note `s`
        let total, skip, limit: Int
    }
    
    struct Product: Identifiable, Codable {  // <--- here
        let id: Int
        let title, description: String
        let price: Int
        let discountPercentage, rating: Double
        let stock: Int
        let brand, category: String
        let thumbnail: String
        let images: [String]
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search