Search code examples
iosswiftswiftuiplist

SwiftUI: Having a plist to display


I am trying to read a plist and having it displayed as a List on SwiftUI. It compiles with no errors or warnings, but nothing gets displayed. I am not sure what I am doing wrong or the mistake I am making here. I've tried multiple things, but I still get a blank display.


import Foundation

struct PresidentModel: Decodable{
    var Name: String
    var Number: Int
    var StartDate: String
    var EndDate: String
    var Nickname: String
    var PoliticalParty: String
    
    
    enum CodingKeys: String, CodingKey{
        case Name = "Name"
        case Number = "Number"
        case StartDate = "Start Date"
        case EndDate = "End Date"
        case Nickname = "Nickname"
        case PoliticalParty = "Political Party"
    }
}

class PresidentViewModel: ObservableObject{
    @Published var PresArray: [PresidentModel] = []
    @Published var name: String = ""
    @Published var number: Int = 0
    @Published var startDate: String = ""
    @Published var endDate: String = ""
    @Published var nickname: String = ""
    @Published var politicalParty: String = ""
    
    
    func loadProperityListData(){
        guard let path = Bundle.main.path(forResource: "presidents", ofType: "plist"), let xml = FileManager.default.contents(atPath: path) else {
            fatalError("Unable to access property list states.plist")
        }
        
        do{
            PresArray =  try PropertyListDecoder().decode([PresidentModel].self, from: xml)
            name = PresArray[0].Name
            number = PresArray[0].Number
            startDate = PresArray[0].StartDate
            endDate = PresArray[0].EndDate
            nickname = PresArray[0].Nickname
            politicalParty = PresArray[0].PoliticalParty
        }
        
        catch {
            fatalError("Unable to decode property list states.plist")
        }
    }//func
}//class

Where the plist will be displayed as a List :

import SwiftUI

struct ContentView: View {
    
    let presidents = PresidentViewModel()
    
    var body: some View {
    
        List(presidents.PresArray.indices, id: \.self){ president in
            Text(presidents.PresArray[].Name)
        }
    }//Body
}//View
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


Solution

  • First of all please conform to the naming convention and declare the struct member names with starting lowercase letters and add an id property

    struct PresidentModel: Decodable, Identifiable {
        let id = UUID()
        let name: String
        let number: Int
        let startDate: String
        let endDate: String
        let nickname: String
        let politicalParty: String
        
        private enum CodingKeys: String, CodingKey {
            case name = "Name"
            case number = "Number"
            case startDate = "Start Date"
            case endDate = "End Date"
            case nickname = "Nickname"
            case politicalParty = "Political Party"
        }
    }
    

    The main problem is that you don't load the data, a good place is the init method of the observable class. The properties are not needed because the array contains all data

    class PresidentViewModel: ObservableObject{
        @Published var presArray: [PresidentModel] = []
    
       init() {
          loadProperityListData()
       }
        
        func loadProperityListData(){
            guard let url = Bundle.main.url(forResource: "presidents", withExtension: "plist"), 
                  let data = try? Data(contentsOf: url) else {
                fatalError("Unable to access property list states.plist")
            }
            
            do {
                presArray =  try PropertyListDecoder().decode([PresidentModel].self, from: data)
            } catch {
                print(error)
                presArray = []
            }
        }//func
    }//class
    

    The second main problem is that PresidentViewModel is not declared as @StateObject. And due to the added id property you get rid of dealing with indices and specifying id: \.self

    import SwiftUI
    
    struct ContentView: View {
        
        @StateObject var model = PresidentViewModel()
        
        var body: some View {
        
            List(model.presArray) { president in
                Text(president.name)
            }
        }//Body
    }//View