来自本地 JSON 文件的 SwiftUI 搜索字段

SwiftUI search field from a local JSON file

我正在尝试从本地 JSON 文件中检索数据,如下所示:

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

用户,使用数据选择器可以select姓名and/or姓氏

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[[=12=]]).tag([=12=])
             }
          }
          Text("Your choice: ")
           + Text("\(names[selectedNameItem])")
       }

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

    }
}

一旦 select 编辑了 name/lastyname(作为参数),我想显示一个文本,例如:"John Doe is 30 years old"

如何在不列出所有元素的情况下从 JSON 和 return 准确读取用户 select 的年龄?

非常感谢, 法布里齐奥

首先,我建议创建一个结构来表示您的 JSON 结构。尝试以下操作:

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

typealias People = [Person]

我通常做一个typealias来处理我的数据的数组版本。一旦你定义了你的数据结构来匹配你的 JSON,我通常会扩展我的数据,以便从 JSON 加载。

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
    }
}

有更通用的方法可以支持更广泛的模型,但对于您的示例来说,这既快速又简单。

现在您已经有了一种使用模型的简单方法,可以通过像这样扩展 Array 类型来轻松解决您的想法:

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

    func retrievePeople(byLastName lastname: String) -> People {
        return self.filter { [=12=].lastname == lastname }
    }

    func retrievePeople(byAge age: String) -> People {
        return self.filter { [=12=].age == age }
    }
}

这将允许您通过任何元素查询整个对象范围,进而 return 匹配数组。如果您确定只有一个 return,您可以使用以下代码获取第一个元素:

// 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
}

这种带数据的loading/working风格的好处在于它易于快速生成,并且支持returning 0个对象、1个对象和多个对象。这也将允许您移动模型以使用 SwiftUI/Combine 的功能(这就是您希望在上面执行的操作)。