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.
Related
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.
I'm building a SwiftUI app for macOS, then I came with the following challenge.
Given a view hierarchy like:
Which basically shows a collection of elements in ElementListView. Then the FiltersView is used to search new elements to possibly add to the element list, the results of that search are shown in rows like FilterRow.
When a user clicks on FilterRow, that means I want to add a new element to the ElementListView, but I am not sure of How to propagate events between the two branches of the view hierarchy in SwiftUI?
If I were to implement this in pure AppKit, I would probably use the responder chain, but I guess it's not the SwiftUI way of doing it.
You can have a shared ObservableObject for filters and list views. As soon as ObservableObject changes SwiftUI updates views connected to it.
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.
I have the following problem with ember.
I have a table with a set of datas. I have an event that returns me the current element of the table. Then it opens another view by transitioning into a new state and writes the selected table data in a textfield.
click: function(e) {
var element = $(e.target).closest("td");
App.tee = element.text().trim();
var router;
router = this.get('controller.target.router');
router.transitionTo('newRoute')
As you can see I have some other routes in my router as well.
The last two routes(home and profile) are part of a nav-tab. They work perfectly beside I click on the table. Then i get the following error when i click on a tab: Uncaught TypeError: Cannot read property 'enterStates' of undefined
Ok i give it another try to explain what i wanted to do.
What i want to do is to create a table with some data (for example persons).
When i click on a specific person in the table the corresponding table-data should be shown in the textfields that appear below. Whenever i click on another person the textfields below should change to the informations of the current clicked person in the table. When i click "New" Button a more detailed Tabview appears on the right side with some additional informations. I was playing around with ember and so far i just implemented some of the views and the routing. Im stucked as i have tried to implement the event that updates the textfield below the table. It updates once but after it has transitioned into the new state(newRoute) nothing happens. I know the code is very raw, but it is just a test to understand how this works in ember.
Ok the solution was easier than i thought. The problem was not the state changing. It was more a problem of how to access the data and how to effect the change of binded data. I realised too late that i needed to understand how the variable access works in Ember and what exactly the App.initialize() method does. So App.initialize() initializes all Controller classes in the router. If you want to access any variables within a controller you have to get the access over the router like
{{view Ember.TextField valueBinding="App.router.tableController.person"}}
Secondly i wasnt familiar with the usage of the set and get methods in Ember and the difference between extend and create. I wondered before where ember instantiates my object.
So my problem had nothing to do with states it was just a totally misunderstanding of the ember framework. Im a noob thats all.
Ok, this is the first shot of the answer.
I think the main issue is just a typo gotoHome instead of goToHome in the template.
By the way I get rid of some deprecation warnings by using <button {{action }}></button> instead of Ember.Button.
There is some other warnings when I click on the table, because you are referencing some properties which don't exist.
here is the corresponding fiddle: http://jsfiddle.net/Sly7/rKw9A/25/
Since I don't understand how it should work exactly, I'm not sure of the overall behavior. I let you explain me the flow (by editing the question please).
Any other comment is welcome :)
I just having a little trouble to implement a special kind of view for Ember, I'm digging on the source for days but can't find how to make it work... Can you take a look and tell me what's wrong? It's a small code, a specific problem when rendering one view from another (it's not doing the binds right...).
The sample code (with comments) that demonstrate the problem is here: http://jsfiddle.net/wilkerlucio/rUUuN/
Edit:
Just to clarify, I'm trying to do a view that dynamically render another view. It can be useful for a lot of implementations, like tabs for example. On tabs you have the tabs and the container that shows current tab, so, the view that I'm trying to accomplish is like this current tab container. Each tab has it own view, and I need that my view be able to render the view for the current tab.
I know I can do things like just hide a view and show the other, but it's not the way I want it right now. This CardView that I'm creating should have a binding to a property that will return a view instance, and the CardView will render this view, and will update if the property that points the view updates.
You can see a more full covered example about what I'm trying to do here: http://jsfiddle.net/wilkerlucio/Ztdpb/
Thanks
I think that you need to specify the template as such:
App.CardView = Ember.View.extend({
defaultTemplate: SC.Handlebars.compile('{{App.obj.value}}')
});
or
App.CardView = Ember.View.extend({
templateName: 'sample'
});
If you are planning to have many child views, then you may want to try to use a collection view.
This link is a bit old, but it is still not bad: http://guides.sproutcore20.com/using_handlebars.html
I've also blogged about how it implemented CRUD operations with Ember (SC2) here.
Hope this helps.