0

I have been trying to decode a JSON from a PHP that I have and then displaying the results as Text. I am getting either one of two errors with everything I have tried. The first issue would be "Expected to decode Array but found a dictionary instead." I changed some things up in the struct I had for this and resolved that issue. However, I then got an issue with having no data from the call.

Here is the relevant portion of the code that I have for this. I am currently using the secondtabview for the testing and the firsttabview as a control.

struct CallData: Decodable, Identifiable{
    
    private enum CodingKeys: String, CodingKey { case records}
    
    let id = UUID()
    let gdata: [Record]
    
    init(from decoder: Decoder) throws {
        
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let records = try container.decode([String:[String]].self, forKey: .records)
        gdata = records.map(Record.init).sorted{$0.iD < $1.iD}
    }
}

struct Record {
    
    let iD: String
    let data: [String]
}

class ContentModel: ObservableObject {
    
    @Published var records = [Record]()
    
    init() {
        getData()
    }
    
    func getData(){
        


        let url = URL(string: "https://not.the.real.php?route=Green")
        
        if let url = url {

            var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 10)
            request.httpMethod = "GET"
             


            URLSession.shared.dataTask(with: request){ (data, response, error) in
                if error == nil {
                    
                    do {
                        let decoder = JSONDecoder()
                        let result = try decoder.decode(CallData.self, from: data!)
                        print(result)
                        
                        DispatchQueue.main.async {
                            self.records = result.gdata
                        }
                    } catch {
                        print(error)
                    }
                }
            }.resume()
            
        }
    }
    
}

struct FirstTabView: View{
    
    @Namespace var mapScopeRed
    
    var body: some View {
        
        VStack{
            
            ScrollView{
                
                
                Text("Red \nred")
            }
            
            Map(scope: mapScopeRed){
                
                
                Marker("Here", coordinate: .You)
                
               
                
                
            }.mapStyle(.hybrid(elevation: .realistic))
            
            MapUserLocationButton(scope: mapScopeRed).tint(.red)
            
            
        }.mapScope(mapScopeRed)
        
        
    }
    
}

struct SecondTabView: View{
    
 
    @Namespace var mapScope
    
    @State var gmodel = [CallData]()
    
    
    var body: some View {
        
        
        VStack{
            
            List(gmodel) { route in
                ScrollView{
                    
                    let ginfo =  "\(route.gdata)"
                    Text(ginfo)
                }
            }
            Map(scope: mapScope){
                
                
                Marker("Here", coordinate: .Youtoo)
                
                
                
            }.mapStyle(.hybrid(elevation: .realistic))
            
            MapUserLocationButton(scope: mapScope).tint(.red)
            
            
        }.mapScope(mapScope)
        
    }
}

I will also include what the structure of the JSON results from the web results of the php.

{"records":[{"ID":"354","route":"Green","stop_name":"StopOne","lat":"-11.11","lng":"11.11","stop_order":"1","stop_time":"5","NewOrder":"1"},{"ID":"355","route":"Green","stop_name":"StopTwo","lat":"-11.11","lng":"11.11","stop_order":"2","stop_time":"60","NewOrder":"2"},{"ID":"356","route":"Green","stop_name":"StopThree","lat":"-11.11","lng":"11.11","stop_order":"3","stop_time":"30","NewOrder":"3"},{"ID":"357","route":"Green","stop_name":"StopFour","lat":"-11.11","lng":"11.11","stop_order":"4","stop_time":"42","NewOrder":"4"},{"ID":"358","route":"Green","stop_name":"StopFive","lat":"-11.11","lng":"11.11","stop_order":"5","stop_time":"54","NewOrder":"5"},{"ID":"359","route":"Green","stop_name":"StopSix","lat":"-11.11","lng":"11.11","stop_order":"6","stop_time":"95","NewOrder":"6"}]}

Now as stated previously I have tried a multitude of different things I have found online, but nothing actually results in Observable data. I would like to be able to display each stop_name and corresponding stop_time within the scrollview. I'd assume my issue is with how I have the CallData and/or Record structures I have set up (Or possibly my ContentModel Class). I am completely new to swift so any advice on this or swift in general would be helpful.

1

1 Answer 1

0

There are two issues:

The error occurs because the object for key records is an array of dictionaries [String:[String:String]] , not a dictionary with array values [String:[String]].

Another issue is that there is no key iD (lowercase I and uppercase D) in the records dictionaries

The straightforward solution is to adopt Decodable in Record

struct Record : Decodable {
    let ID: String
    let route: String
    let stop_name, lat, lng : String
}

and decode [Record].self

struct CallData: Decodable, Identifiable{
    
    private enum CodingKeys: String, CodingKey { case records}
    
    let id = UUID()
    let gdata: [Record]
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        gdata = try container.decode([Record].self, forKey: .records)
    }
}
2
  • Thank you very much for your input Vadian. I was able to collect the data. One last question would be is there a way to filter the data by specific keys to populate into a text field? As of now it is pulling everything in the records struct however I would like to only pull two of the fields for a particular text field.
    – HKelz
    Commented Jul 10 at 16:12
  • Yes, there is a filter API. For example please read sarunw.com/posts/swift-array-filter
    – vadian
    Commented Jul 10 at 16:45

Not the answer you're looking for? Browse other questions tagged or ask your own question.