Limitation Of #State - swiftui

I saw in some article that #state has many limitations
that we shouldn't use it with complex models and its preferable to use it with the simple property like string, bool, ... etc.
and we should use it inside the view itself.
I tried to make a struct model and mark it with #state in the contentview (ParentView) and pass this model to its child views by wrapping it with #binding it worked just fine,
so i don't understand why we still need #objectbinding as we can pass the same value to these child views and if one change the others will change also? or what are the limitations of #state that #objectbinding solve?

I recommend you watch WWDC 2019 session: Data Flow in SwiftUI. It is very well explained. It describes in which scenarios #State is perfectly acceptable and where ObjectBinding/EnvironmentObject is necessary instead. The session is only 37 minutes long, but it will be a before and after in your understanding of bindings. Please do watch it, it will save time in the long run.
It all comes down to understanding where is the "source of truth" of your data. This is a concept that is also explained in the video. In a few words, the source of truth is were your data is born. If the data of your variable can be derived from some other variable, then it is not a source of truth.
What are the difference between #State and #BindableObject?
#State: It is local to the view. It has to be a value-type (e.g., a struct, Int, String, Array, etc) and its storage is managed by the framework.
#BindableObject: It is external to the view, it is a reference value (e.g., a class) and its storage is managed by you, giving you more flexibility to implement your own logic.
Note that #State variables are also great while prototyping your app. For example, if you are working on the layout of your view, you can initially use a #State variable to make things easier. Once your view does what you want, you can concentrate on creating your #BindableObject and replace your #State.

Related

#ObservedObject reinit impact on view rendering

From reading one user's answer to #ObservedObject model lifecycle?, where the user says
Every time this view is created, it instantiates a new instance of TestModel. SwiftUI views are really more like view descriptions which are created and destroyed a lot during your app lifecycle
But I also learned from this article https://blog.scottlogic.com/2020/01/28/Exploring-SwiftUI-3-View-Updates.html that SwiftUI checks if the newly computed view is different from the previous state before rendering it.
So, let's say that the ObservedObject gets recreated, how does that affect the view update? Does SwiftUI compare the previously created view with the new one to see if they are different before updating it, or does a ObservedObject recreation always leads to a new view update.
Hope I am making this question clear.
Always leads to a new view update. The reason is the View appears changed because the property for the object will almost certainly have a different pointer compared to last time.
It's like having a memory leak and should be avoided. I'm sure a future version will throw a compilation error if a mistake like #ObservedObject var obj = SomeObject() is present. Because the object is instantly dealloced so any async thing it is doing just stops or crashes.

SwiftUI problem (With list and simulator)

I'm starting to learn swiftUI and I have a strange problem,
I have created a simple list that displays perfectly in the preview... But not in the simulator.
Does anyone have an idea?
https://www.dropbox.com/s/eo1ldvdbgntzr8v/Enregistrement%20de%20l%E2%80%99%C3%A9cran%202021-06-04%20%C3%A0%2018.42.03.mov?dl=0
Project : https://github.com/maxupcreation/Dfi/tree/main/Dfi
to answer more correctly, you'd better show us yout whole source code.
it seems second view(maybe AddChallengeView.swift file)'s input cannot reflect to original view(=ContentView.swift)'s List. so there might be two problems
because you refer your "items" property from another view, you'd better check if you use ObservableObject protocol and #ObservedObject property wrapper properly.
at ForEach statement, you use nil coalescing, so check whether item value is nil

In Swiftui, how to minimize passing functions to sub-views

I am working on a todo app that interacts with an API to store data. My view structure looks like this (simplified):
ContentView: List of { ProjectView }
ProjectView: List of { TaskView }
The ContentView has a webServer variable that provides functions like storeTask(task: Task) and deleteTask(task: Task).
So now I want my TaskView to be able to modify tasks. The most straightforward pattern seems to provide storeTask and deleteTask to TaskView to keep the view ignorant of the implementation details. That means that I have to pass these two functions through the whole tree of sub-views: ContentView -> ProjectView -> TaskView. But then, a minor change in function signature requires a changes for every view that's part of the view hierarchy.
This feels like an inefficient pattern as it's already complex for the simple app I am building. What are other approaches? For context, I remember this same issue from working on React, and using approaches like Redux to deal with this. I'm wondering whether there's a standard approach for simplifying this in SwiftUI?
The most common architecture pattern in SwiftUI is MVVM (Model-View-ViewModel). There are many articles online explaining it, but essentially the layers break into:
Model - manage a piece of data
View - manage the UI
ViewModel - manage the data (models) to be displayed in the UI (view)
To answer your question, you would need to create ViewModel layer that manages your data and pass that ViewModel throughout your Views (as #Asperi mentioned in the comments).
Here is a great article explaining MVVM: https://matteomanferdini.com/mvvm-pattern-ios-swift/
Here is one of my Github repo's that uses MVVM Architecture in SwiftUI to make a todo-list app, similar to your project. If you take a look at the ViewModel layers you will find examples of what you're trying to do. https://github.com/ndsarno/TodoList-MVVM-Advanced

Best practices for SwiftUI state variable encapsulation/ Too many state variables in a complicated view

I'm use to have a class for several complex subviews in a view. Each can encapsulate it's own state. SwiftUI will let you refactor a long view function into subviews but I have not found anything other than passing #Binding variables through the views.
I keep ending up with a lot of state variables on the main view. Ideally what I would like would be if I could reference a subview of the main and directly set it's state like this
mainView.bottomPane.odometer.speed = 55
Is this possible? If not are there other ways around large chunks of State variables in the main form?

#Environment vs #EnvironmentObject

What's the difference between #Environment and #EnvironmentObject in SwiftUI?
From what I found from the documents, #Environment seems to be meant to be used for global dependencies like ColorScheme. But I couldn't find any precise difference between them. For example, can they be used interchangeably?
Here is the notes I have prepared for myself. Could be useful,
#EnvironmentObject
Its similar like #ObservedObject
The model should conform to the ObservableObject protocol
We need to mark properties in this model as #Published to notify changes to view which actively using the object
The model object should be as class for sure
No need for default value, because it can read default value from environment. If object is not available in environment, app will crash.
Another major difference is, say we have 5 views(V1…V5), if we want to pass a object directly from V1 to V5 we could use #EnvironmentObject rather than #ObservedObject. Set data to be passed in V1 and retrieve it in V5(or wherever needed). Code will be much simple.
It will hold only one type of instance at same time environment.
Its purely based on views. If a parent view sets environment object all its child can make use of it. If another parent view set another env object, their child’s can make us of it. Eg: If you set environment object in your ContentView in SceneDelegate all its child views can make use of it.
#Environment
We can use this to get system related values like whether apps is running in light or dark mode, core data's managed object context, size classes, etc...
We need to provide proper keys to access its value, because it holds same datatype against multiple keys.
I want to add something to others' answer.
#Environment is value type but #EnvironmentObject is reference type.
You can only use a single instance of objects in #EnvironmentObject.If you add another instance of an object, it will replace the previous one.
But as, #Environment key value pair, just make sure key is different.
You just need to use #EnvironmentObject var object: Object to make an object retrieve the instance from the environment, and inject the instance by .environmentObject(Object())
On the other hand, there are many predefined #Environment system-managed environment values. You can also create custom one. It needs to be struct type and conforms to EnvironmentKey.
Here is an example,
struct SunlightKey: EnvironmentKey {
static var defaultValue: Double = 1.09
}
Then add it to the EnvironmentValues as an extension of it.
Here is a basic example
extension EnvironmentValues {
var sunlight: Double {
get { self[SunlightKey.self] }
set { self[SunlightKey.self] = newValue }
}
}
Then, use it like, #Environment(\.sunlight) var sunlight in view file and inject value by .environment(\.sunlight, 4.05)
Hope, this helps
#Enviroment gives you access to values of properties such as user settings (e.g colour scheme, layout direction etc.) or properties of the view such as EditMode or PresentationMode. #EnviromentObject is defined by you and available to all views and changes to it drive updates to your views.
Both of them #Environment and #EnvironmentObject are property wrappers while #Environment keeps values with predefined keys, #EnvironmentObject keeps not only predefined keys but also arbitrary values. For example if you need to keep information about your User object which includes name, age, gender etc. you need to use #EnvironmentObject, whereas if you would like to keep whether device is in dark or light mode, system local language, calendar preferences, edit mode it is great for using #Environment.
#Environment(\.locale) var locale: Locale
#EnvironmentObject var user: User // is an object where you keep user-related information
#Environment is a key/value pair, whereas #EnvironmentObject is just a value identified by its type. Both are variable storage property wrappers.