i'm try to learn SwiftUI, i'm try to update my list automatically once I insert the value. but i'm getting a big issue, my list not update when I use a sheet or a navigation view to insert data. it only work if I load data from my contentView.
(and I don't understand why, the class DataManager is ObservableObject and more over it work perfectly if I load data with 3 textfield in the content view)
here below my project: I have a Data Model
import Foundation
import Combine
class DataModel: Codable, Identifiable {
var id: UUID = UUID()
var airportName : String
var metar : String
var taf : String
init(airportName: String, metar: String, taf: String) {
self.airportName = airportName
self.metar = metar
self.taf = taf
}
}
I have a DataManager
import SwiftUI
import Combine
class DataManager: ObservableObject {
let objectWillChange = PassthroughSubject<Void, Never>()
var storage : [DataModel] = [] {
willSet {
objectWillChange.send()
}
}
typealias Storage = [DataModel]
var filePath : String = ""
init() { caricaDati() }
func caricaDati() {
// creiamo il percorso al file
filePath = cartellaDocuments() + "test.plist"
// usiamo NSFileManager per sapere se esiste un file a quel percorso
if FileManager.default.fileExists(atPath: filePath) {
// se c'è de-archiviamo il file di testo nell'array
// serve il blocco do try catch
do {
// proviamo a caricare il file dal percorso creato in precedenza
let data = try Data(contentsOf: URL(fileURLWithPath: filePath))
// creiamo il decoder
let decoder = PropertyListDecoder()
// proviamo a decodificare il file nell'array
storage = try decoder.decode(Storage.self, from: data)
} catch {
// se non ce la fa scriviamo in console l'errore
debugPrint(error.localizedDescription)
}
}
}
func salva() {
objectWillChange.send()
let encoder = PropertyListEncoder()
encoder.outputFormat = .xml // impostiamo l'output corretto
// serve il blocco do try catch
do {
// proviamo a codificare l'array
let data = try encoder.encode(storage)
// proviamo a salvare l'array codificato nel file
try data.write(to: URL(fileURLWithPath: filePath))
} catch {
// se non ce la fa scriviamo in console l'errore
debugPrint(error.localizedDescription)
}
}
func newData (nomeApt: String, metar: String, taf: String) {
let newadd = DataModel(airportName: nomeApt, metar: metar, taf: taf)
objectWillChange.send()
storage.append(newadd)
objectWillChange.send()
salva()
}
func cartellaDocuments() -> String {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
//print(paths[0])
return paths[0]
}
}
as you can see Datamanager is ObservableObject. and I have a simple function (newData) to add the value to the array storage.
now my problem is: if I add data via 3 textfield in the ContentView() my List update automatically with no issue at all
import SwiftUI
struct ContentView: View {
@ObservedObject var dm : DataManager
@State var isAddPresented : Bool = false
@State var nomeApt : String = ""
@State var metar : String = ""
@State var taf : String = ""
var body: some View {
VStack {
Button(action: {
self.dm.newData(nomeApt: self.nomeApt, metar: self.metar, taf: self.taf)
}) {
Text("Add from below")
}
TextField("name apt", text: $nomeApt)
TextField("name apt", text: $metar)
TextField("name apt", text: $taf)
Button(action: {
self.isAddPresented = true
}) {
Text("open view to add")
}.sheet(isPresented: $isAddPresented) {
Add(dm: DataManager(), dismissFlag: self.$isAddPresented)
}
List(dm.storage) { item in
HStack {
Text(item.airportName)
Text(item.metar)
Text(item.taf)
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(dm: DataManager())
}
}
but if I use a sheet to add the same data, using the same function the List not update immediately!
here my second view:
import SwiftUI
struct Add: View {
@ObservedObject var dm : DataManager
@State var nomeApt : String = ""
@State var metar : String = ""
@State var taf : String = ""
@Binding var dismissFlag: Bool
var body: some View {
VStack {
Spacer()
TextField("name apt", text: $nomeApt)
TextField("name apt", text: $metar)
TextField("name apt", text: $taf)
Button(action: {
self.dm.newData(nomeApt: self.nomeApt, metar: self.metar, taf: self.taf)
self.dismissFlag = false
}) {
Text("Aggiungi")
}
Spacer()
}
}
}
struct Add_Previews: PreviewProvider {
static var previews: some View {
Add(dm: DataManager(), dismissFlag: bindBool())
}
}
func bindBool() -> Binding<Bool> {
var boolVariable : Bool = true
let boolVariableBinding : Binding<Bool> = Binding(get: { boolVariable },
set: { boolVariable = $0 })
return boolVariableBinding
}
what I totally don't understand is, why if I load the data from the content view my list update automatically, but if I use a navigation view of a sheet view this not work.
thanks in advance for the help
I have a similar working solution for this.
In your case I would modify your DataModel
as follows:
class DataModel: Codable, Identifiable, ObservableObject {
var id: UUID = UUID()
@Published var airportName : String = ""
@Published var metar : String = ""
@Published var taf : String = ""
// ...
}
Then in your DataManager
:
class DataManager: ObservableObject {
@Published var storage : [DataModel] = [] // No longer need forobjectWillChange.send()
// ...
}
Then just use it in your View
:
struct ContentView: View {
@ObservedObject var dm : DataManager
// ...
var body: some View {
VStack {
// ...
List(dm.storage) { item in
// ...
}
}
}
}
The problem was that a lot had changed since the WWDC videos.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.