Home > Software design >  Errors while trying to create a list based on JSON in SwiftUI
Errors while trying to create a list based on JSON in SwiftUI

Time:01-08

I need my JSON data to be translated to a list as an output for an app I am creating.

I have found this tutorial, which seems to be using some old elements like "BindableObject":

https://www.youtube.com/watch?v=ri1A032zfLo

As far as I've checked several times, I went word for word in the NetworkingManager file being described from 1:52 in the video (just changing the names and the URL). My code:

import Foundation
import SwiftUI
import Combine

/*BindableObject was renamed to ObservableObject */

class NetworkingManager: ObservableObject{
    var didChange: PassthroughSubject <NetworkingManager, Never>
    
    var gitHubList = Root(items: []){
        didSet{
            didChange.send(self)
        }
    }
    
    init() {
        guard let url = URL(string: "https://api.github.com/search/repositories?q=CoreData&per_page=20") else { return }
        
        URLSession.shared.dataTask(with: url) {(data, _, _) in guard let data = data else { return }
            
            let gitHubList = try! JSONDecoder().decode(GitHubAPIlist.self, from: data)
            
            DispatchQueue.main.async {
                self.gitHubList = gitHubList
            }
        }.resume()
    }
}

I get 2 errors:

'self' captured by a closure before all members were initialized

on the line with URLSession and

Return from initializer without initializing all stored properties

on the line with the } after the .resume() command

Are there some obsolete syntaxes in the code or am I missing something?

CodePudding user response:

You declared the subject but didn't initialize it

var didChange = PassthroughSubject<NetworkingManager, Never>()

However actually you don't need the subject, mark gitHubList as @Published (and delete the subject).
And if you are only interested in the items declare

@Published var gitHubList = [Repository]()

and assign

DispatchQueue.main.async {
     self.gitHubList = gitHubList.items
}

The @Published property wrapper will update the view.


In the view declare NetworkingManager as @StateObject

@StateObject private var networkManager = NetworkingManager()

and in the rendering area refer to

List(networkManager.gitHubList...

Notes:

  • Forget the tutorial , the video is more than two years old which are ages in terms of Swift(UI)'s evolution speed.
  • Ignoring errors and force unwrapping the result of the JSON decoder is very bad practice. Handle the potential URLSession error and catch and handle the potential decoding error.

CodePudding user response:

try something like this approach, to get you data from github. Works very well for me:

class NetworkingManager: ObservableObject{
    @Published var gitHubList: [Item] = []

    init() {
        loadData()
    }

    func loadData() {
        guard let url = URL(string: "https://api.github.com/search/repositories?q=CoreData&per_page=20") else { return }
        URLSession.shared.dataTask(with: url) {(data, _, _) in
            guard let data = data else { return }
            do {
                let response = try JSONDecoder().decode(Root.self, from: data)
                DispatchQueue.main.async {
                    self.gitHubList = response.items
                }
            } catch {
                print("error: \(error)")
            }
            
        }.resume()
    }
    
}

struct Root: Codable {
    let totalCount: Int
    let incompleteResults: Bool
    let items: [Item]

    enum CodingKeys: String, CodingKey {
        case totalCount = "total_count"
        case incompleteResults = "incomplete_results"
        case items
    }
}

struct Item: Identifiable, Codable {
    let keysURL, statusesURL, issuesURL: String
    let id: Int
    let url: String
    let pullsURL: String
    // ... more properties

    enum CodingKeys: String, CodingKey {
        case keysURL = "keys_url"
        case statusesURL = "statuses_url"
        case issuesURL = "issues_url"
        case id
        case url
        case pullsURL = "pulls_url"
    }
}

struct ContentView: View {
    @StateObject var netManager = NetworkingManager()
    
    var body: some View {
        List {
            ForEach(netManager.gitHubList) { item in
                Text(item.url)
            }
        }
    }
}
  •  Tags:  
  • Related