Swiftui: Problem with Adding a Property to Data Struct

swiftui: problem with adding a property to data struct

Guessing here but var property : Property should be

@Binding var property : Property

let means that the variable won't change. @Binding is a two-way connection.

You will connect them in your previous view with something like this.

ForEach($data.properties, id:\.id){ $property 
YourViewName(property: $property)
}

This question marks denote the two-way connection. Without them you are not going to be able to change the variable.

SWIFTUI: Why adding @State to property doesn't update data model

This is similar to your previous question. Change @State var property : Property to

It is all about the connections. You break them in a few places along the way I made some changes and put comments along the line.

import SwiftUI

struct MetersParentView: View {
//Change to StateObject
@StateObject var data = PropertyData()

var body: some View {
NavigationView {
List {
ForEach($data.properties, id:\.id) {$property in
NavigationLink(destination: NavigationLinkView(data: data, property: $property)) {
Text(property.name)
}
}
}
}
}
}

struct NavigationLinkView: View {
@ObservedObject var data : PropertyData
//Make Binding there is no connection without it
@Binding var property : Property

var body: some View {
TabView {
MetersView(data: data, property: $property)
.tabItem{
VStack {
Image(systemName: "scroll")
Text("Utitity")
}
}
}
}
}

struct MetersView: View {
@ObservedObject var data: PropertyData
@State var selectedMeter: Meter = .init()
//Change to Binding
//State is a source of truth, it breaks the connection
@Binding var property : Property
@State private var addMeters = false

var body: some View {
VStack {
HStack {
Picker("Meters", selection: $selectedMeter) {
//Use object not index
ForEach(property.meters, id:\.id) {meter in
//Tag adjustment
Text(meter.name).tag(meter as Meter)
}
}.onAppear(perform: {
selectedMeter = property.meters.first ?? Meter()
})
.pickerStyle(SegmentedPickerStyle())
Button{
print(property.meters)
addMeters.toggle()
} label: {
Image(systemName: "gear")
}
}.padding()
Spacer()
}.sheet(isPresented: $addMeters){
AddMetersView(data: data, property: $property)

}
}
}

struct AddMetersView: View {
@ObservedObject var data : PropertyData
@Binding var property : Property
@State var newMeter: String = ""
@Environment(\.presentationMode) var presentationMode

var body: some View {
VStack {
Form{
Section {
TextField("Add another meter", text: $newMeter)
.autocapitalization(.none)
Button{
if newMeter != "" {
print(property.meters.count)
property.meters.append(Meter(name: newMeter))
print(property.meters.count)
data.save()
print(property.meters.count)
}

} label: {
Text("Add a meter")
}
}
//Dont use index
ForEach(property.meters, id:\ .id) {meter in
Text(meter.name)
}
Section() {
Button("That's enough"){
print(property.meters)
presentationMode.wrappedValue.dismiss()}
}
}
}
}
}
struct MetersParentView_Previews: PreviewProvider {
static var previews: some View {
MetersParentView()
}
}

Swift/SwiftUI property initializer error in struct

You are using name before the struct is initialized. If you make message a computed properties it should work.

struct Greeting {
var name = "Bob"

var message: String {
"Hi, " + name
}
}

Mutating error when attempting to append an array of a struct

The issue is that a struct cannot change its own properties without the function that is changing those properties being marked mutating. You can’t mark body as mutating, but you can make children a @State var. @State variables are mutable, but only from within your view’s body property.

SwiftUI: Binding on property of struct derived from environment object

No, you're not necessarily doing something against best practices. I think in SwiftUI, the concepts of data model storage and manipulation quickly become more complex than, for example, what Apple tends to show in its demo code. With a real app, with a single source of truth, like you seem to be using, you're going to have to come up with some ways to bind the data to your views.

One solution is to write Bindings with your own get and set properties that interact with your ObservableObject. That might look like this, for example:

struct TaskView : View {
var taskIdentifier: String // Passed from parent view

@EnvironmentObject private var taskStore: TaskStore

private var taskBinding : Binding<Task> {
Binding {
taskStore.tasks[taskIdentifier] ?? .init(identifier: "", title: "", tags: [])
} set: {
taskStore.tasks[taskIdentifier] = $0
}
}

var body: some View {
TextField("Task title", text: taskBinding.title)
}
}

If you're averse to this sort of thing, one way to avoid it is to use CoreData. Because the models are made into ObservableObjects by the system, you can generally avoid this sort of thing and directly pass around and manipulate your models. However, that doesn't necessarily mean that it is the right (or better) choice either.

You may also want to explore TCA which is an increasingly popular state management and view binding library that provides quite a few built-in solutions for the type of thing you're looking at doing.

Changes to a struct not being published in SwiftUI

This is caused by the:

@ObservedObject var weatherViewModel = WeatherViewModel()

being owned by the WeatherView itself.

So what happens is the weather view model changes which forces a re-render of the view which creates a new copy of the weather view model, which changes forces a re-render...

So you end up with an endless loop.

To fix it you need to move the weather view model out of the view itself so either use an @Binding and pass it in or an @EnvironmentObject and access it that way.

setting computed property in a SwiftUI view doesn't compile

Aha... I see. Just use non mutating set

  private var s : String
{ get { _s }
nonmutating set (new)
{ _s = "no test"
_s2 = false
// do something else
}
}

SwiftUI - Add values from two textfields using var or let

Use a computed-property.

private var sumValues: Int { (Int(value1) ?? 0) + (Int(value2) ?? 0) }

Swift Struct with Lazy, private property conforming to Protocol

Because accessing the lazy data variable mutates AStruct, any access to it must be marked as also mutating the struct. So you need to write:

struct AStruct : Example {
// ...
var var1: String {
// must declare get explicitly, and make it mutating:
mutating get {
// act of looking at data mutates AStruct (by possibly initializing data)
return self.data.var1
}
}

var varArray:[String] {
mutating get {
return self.data.varArray
}
}
}

However, you'll find now that Swift complains you aren't conforming to Example, because its get var1 isn't marked as mutating. So you'd have to change it to match:

protocol Example {
var var1:String { mutating get }
var varArray:[String] { mutating get }
}


Related Topics



Leave a reply



Submit