import SwiftUI
// MARK: This is just a very simple model
struct Person: Identifiable {
var id: Int
var name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
// MARK: This is a view model,where I can load data from server or modify an object
class Model: ObservableObject{
@Published var personList: [Person] = []
// This is a fake function, load data from service
func loadDataFromServer(){
DispatchQueue.main.asyncAfter(deadline: .now()+2){
self.personList.append(contentsOf: [
Person(id: 1, name: "Davy"),
Person(id: 2, name: "Bob"),
Person(id: 3, name: "Nancy")
])
}
}
// I need to modify the object via a network call, And then update view automatically,how can I do that???
func changeName(person: Person){
print("start change name")
// Mimic a netwotk call
DispatchQueue.main.asyncAfter(deadline: .now()+2){
// Yeah,changed
print("change successed")
// ⚠️⚠️⚠️ How can I change it to a new value and refresh the view?
// person.name = "A New Name"
}
}
}
struct ContentView: View {
@ObservedObject var model = Model()
var body: some View {
List {
ForEach(model.personList) { person in
Cell(person: person)
}
}
.environmentObject(model)
.onAppear(perform: {
model.loadDataFromServer()
})
}
}
// MARK: a very simple subview
struct Cell:View {
@EnvironmentObject var model: Model
var person:Person
var body: some View {
VStack{
Text(person.name)
Button("Change Name") {
model.changeName(person: person)
}
}
}
}
#Preview {
ContentView()
}
Here’s what I expected:when I clicked the button【Change Name】 below 【Davy】,【Davy】would change into 【A New Name】;When I clicked the button below 【Bob】,【Bob】 would change into 【A New Name】.
I’ve tried @Binding、@State、@Observable,etc,but it dose not work,Xcode just keep offering me errors and warnings,I’m so confusing😹😹😹😹😹
Is there anyone kind-hearted who can help me😁😁😁😁😁😁😁
Click to view the image
try this approach using in ContentView
@StateObject var vm: ViewModel = ViewModel()
and in func changeName(person: Person)
if let index = self.personList.firstIndex(where: {$0.id == person.id}) {
self.personList[index].name = "Mickey Mouse"
}
EDIT-1:
Alternative approach using @Observable class
// MARK: This is just a very simple model
@Observable class Person: Identifiable { // <--- here
var id: Int
var name: String
init(id: Int, name: String) {
self.id = id
self.name = name
}
}
// MARK: This is a view model,where I can load data from server or modify an object
@Observable class ViewModel { // <--- here
var personList: [Person] = [] // <--- here
// This is a fake function, load data from service
func loadDataFromServer(){
DispatchQueue.main.asyncAfter(deadline: .now()+2){
self.personList.append(contentsOf: [
Person(id: 1, name: "Davy"),
Person(id: 2, name: "Bob"),
Person(id: 3, name: "Nancy")
])
}
}
// I need to modify the object via a network call, And then update view automatically,how can I do that???
func changeName(person: Person) {
print("start change name")
// Mimic a netwotk call
DispatchQueue.main.asyncAfter(deadline: .now()+2){
// Yeah,changed
print("change successed")
// ⚠️⚠️⚠️ How can I change it to a new value and refresh the view?
person.name = "Mickey Mouse" // <--- here
}
}
}
struct ContentView: View {
@State private var vm: ViewModel = ViewModel() // <--- here
var body: some View {
List {
ForEach(vm.personList) { person in
Cell(person: person)
}
}
.environment(vm) // <--- here
.onAppear{
vm.loadDataFromServer()
}
}
}
// MARK: a very simple subview
struct Cell:View {
@Environment(ViewModel.self) var vm: ViewModel // <--- here
var person: Person
var body: some View {
VStack{
Text(person.name)
Button("Change Name") {
vm.changeName(person: person)
}
}
}
}
1