0

I'm trying to retrieve data from a local JSON file like this:

[{
   "name": "John",
   "lastname": "Doe",
   "age": "30"
}, 
{
   "name": "Jane",
   "lastname": "Doe",
   "age": "20"
}
, 
{
   "name": "Baby",
   "lastname": "Doe",
   "age": "3"
}]

The user, using a datapicker can select name and/or lastname

import SwiftUI

struct ContentView : View {

    var names = ["John", "Jane", "Baby"]
    var lastnames = ["Doe", "Boe"]

    @State private var selectedNameItem = 0
    @State private var selectedLastNameItem = 0

    var body: some View {
       VStack {

          Picker(selection: $selectedNameItem, label: Text("Names:")) {
             ForEach(0 ..< names.count) {
               Text(self.names[$0]).tag($0)
             }
          }
          Text("Your choice: ")
           + Text("\(names[selectedNameItem])")
       }

       VStack {            
          Picker(selection: $selectedLastNameItem, label: Text("LastName:")) {
             ForEach(0 ..< lastnames.count) {
               Text(self.lastnames[$0]).tag($0)
             }
          }
          Text("Your choice: ")
           + Text("\(lastnames[selectedLastNameItem])")
       }

    }
}

Once selected the name/lastyname (as parameter) I want to show a text that said for example: "John Doe is 30 years old"

How I can read the data from JSON and return exactly the age of the user selected without list all the elements?

Thanks a lot, Fabrizio

1 Answer 1

1

To start, I recommend making a struct to represent your JSON structure. Try the following:

struct Person: Codable, Hashable {
    let name: String
    let lastname: String
    let age: String
}

typealias People = [Person]

I usually make a typealias to handle the array version of my data. Once you have defined your data structure to match your JSON, I usually extend my data to make loading from JSON easy.

extension Person {
    static func load(fromJson json: URL) -> People {
        guard let data = try? Data(contentsOf: json) else {
            preconditionFailure("Unable to read data from URL")
        }
        let jsonDecoder = JSONDecoder()
        var people = People()
        do {
            people = try jsonDecoder.decode(People.self, from: data)
        } catch {
            print(error)
        }
        return people
    }
}

There are more generic ways to do this to support a wider swath of models, but for your example, this is quick and easy.

Now that you have an easy way to work with your model, an easy solution to what you have in mind could be done by extending the Array type like so:

extension Array where Element == Person {
    func retrievePeople(byName name: String) -> People {
        return self.filter { $0.name == name }
    }

    func retrievePeople(byLastName lastname: String) -> People {
        return self.filter { $0.lastname == lastname }
    }

    func retrievePeople(byAge age: String) -> People {
        return self.filter { $0.age == age }
    }
}

This will allow you to query the entire range of objects by any of the elements and in turn, return the array of matches. If you're certain that there's only one return, you could use the following code to get the first element:

// Let's assume your array of people is stored in this variable
var myPeople: People
if let person = myPeople.retrievePeople(byName: "John").first {
   // Do the things you want with this person object here
}

The nice thing about this style of loading/working with data is that it's easy to generate quickly and it will support returning 0 objects, 1 object, and multiple objects. This will also allow you to move the model over to use the features of SwiftUI/Combine (which is what it looks like you're hoping to do above).

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks John for your help! I will continue to study hard Swift and deeper Combine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.