Search code examples
jsonswiftswiftuipicker

Filling picker from json, SwiftUI


there is json (link below), you need to put the data from it into picker swiftui, is this possible?

This is the class that loads the necessary data.

class pickerAPI: ObservableObject {

    @Published var groupModel: GroupModel = [GroupModelElement]()

    init() {
        loadPickerData()
    }

    func loadPickerData() {
        guard let url: URL = URL(string: "https://gist.githubusercontent.com/lisindima/a3246c9eebae2e152c1f8211d10d4255/raw/30ee8647261b839c3a00024a851a340295300787/group") else { return }
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            do {
                guard let json = data else { return }
                let swift = try JSONDecoder().decode(GroupModel.self, from: json)
                DispatchQueue.main.async {
                    self.groupModel = swift
                    print(self.groupModel)
                }
            }
            catch {
                print(error)
            }
        }
        .resume()
    }
}
public struct GroupModelElement: Codable, Hashable {
    public let startYear: Int?
    public let name: String?
    public let facultyID: String?
    public let specialityID: String?
    public let groupBr: Int?
    public let id: String?

    enum CodingKeys: String, CodingKey {
        case startYear
        case name
        case facultyID
        case specialityID
        case groupBr
        case id
    }

    public init(startYear: Int?, name: String?, facultyID: String?, specialityID: String?, groupBr: Int?, id: String?) {
        self.startYear = startYear
        self.name = name
        self.facultyID = facultyID
        self.specialityID = specialityID
        self.groupBr = groupBr
        self.id = id
    }
}

public typealias GroupModel = [GroupModelElement]

It is necessary that only the "name" field from json get into the picker

let group: GroupModelElement
Picker(selection: $session.choiseGroup, label: Text("Выбранная группа")) {
                        ForEach(0 ..< group.name.count) {
                            Text(self.group.name[$0])
                        }
                    }

An error appears at all in another place that is not related to this code.


Solution

  • The problem is that name is optional. You must unwrap it in order to make it work.

    
    struct ContentView: View {
        var elements:[GroupModelElement] = [
            GroupModelElement(startYear: 1990, name: "name1", facultyID: "1", specialityID: "2", groupBr: 3, id: "abc1"),
            GroupModelElement(startYear: 1991, name: "name2", facultyID: "10", specialityID: "20", groupBr: 30, id: "abc2"),
            GroupModelElement(startYear: 1992, name: "name3", facultyID: "100", specialityID: "200", groupBr: 300, id: "abc3")
        ]
        @State var selectedIndex = 0
    
        var body: some View {
            NavigationView {
                Form {
                    Section {
                        Picker(selection: $selectedIndex, label: Text("elements")) {
                            ForEach(0 ..< elements.count) {
                                Text(self.elements[$0].name ?? "unknown")
                            }
                        }
                    }
                }.navigationBarTitle("Select your name")
            }
        }
    }
    
    

    What I can suggest you do in your GroupModelElement class. Make a computed property for each optional variable as follows:

    public struct GroupModelElement: Codable, Hashable {
        public let startYear: Int?
        public var wrappedStartYear:Int{
            startYear ?? -1
        }
        public let name: String?
        public var wrappedName:String{
            name ?? "unknown name"
        }
        //...
    
    

    Then its much easier to work with optional values and you can call inside of the picker Text(self.elements[$0].wrappedName).