ctietze,
@ctietze@mastodon.social avatar

The bad awakening in #SwiftUI StateObject trickery:

init(foo: Foo) {
_foo = .init(wrappedValue: foo)
}

If this view is in a ViewBuilder that tries to update a lot, the Foo created on the outside will be initialized and then discarded immediately after.

pyrtsa,

@ctietze This should be written as

init(foo: @'autoclosure () -> Foo) {
_foo = .init(wrappedValue: foo())
}

instead to make use of the lazy initialisation of StateObject. Or make the parameter a (non-auto)closure for making it more obvious to readers of code. Won't help with State though.

ctietze,
@ctietze@mastodon.social avatar

@pyrtsa Yes, thank you! Forgot to post the fix.

pyrtsa,

@ctietze Oops, and I forgot another @'escaping from that, autoclosure or not.

helge,
@helge@mastodon.social avatar

@ctietze It's underscore for a reason, people really shouldn't do those things. It is not necessary and if you do it, you have to understand all the implementation details.

ctietze,
@ctietze@mastodon.social avatar

@helge You certain you wanted to phrase it that way?

helge,
@helge@mastodon.social avatar

@ctietze I thought about that, and yes, I think that this is pretty correct. Maybe not the "implementation details", but "deep understanding how it works", as you bypass the wrapper.
There is one place where I think it is required: when passing them (e.g. Bindings) in public initialisers.
Using an _ is code smell, IMO. It can be correct if you know what you are doing 🙂

ctietze,
@ctietze@mastodon.social avatar

@helge 👍 I was confused by your reply because it was neither helpful, except as a warning, did not relate to the poster but addresses an anonymous audience that 'shall not do X', and it also sounded like you identified the property wrapper internal prefix _ with private Apple API prefix _, aka a do-not-use-this-marker.

It's public module API. I would love to get rid of this. We do have a init(ownedX:) convention as a marker to explicitly state that this will be 'owned' as a StateObject.

ctietze,
@ctietze@mastodon.social avatar

@helge I'm not 100% on this, but I do recall from @dimsumthinking 's video that the State macro will do away with the horrible workaround and use more common-sensical object lifecycle ownership.

But that may be wishful thinking.

Video is here: https://www.youtube.com/watch?v=rhqASksgJu0

helge,
@helge@mastodon.social avatar

@ctietze @dimsumthinking Oh nice, love that 😉

helge,
@helge@mastodon.social avatar

@ctietze @dimsumthinking So this is actually a #SwiftUI + #Observation thing I've been wondering about this week. There doesn't seem to be a replacement for @StateObject!
Yes, there is @State, but @State specifically doesn't have the delayed init behaviour of @StateObject. Instead (while the first object in the View state sticks), it gets recreated (and thrown away) on every View refresh.
Sample test app: https://gist.github.com/helje5/7ebe7cd87ba410958d96448635b43f22

P.S.: State is still a property wrapper, not a macro.

dimillian,
@dimillian@mastodon.social avatar

@helge @ctietze @dimsumthinking but the first object stick and keep its values right? It’s like StateObject if you init it manually without using the autoclosure?

helge,
@helge@mastodon.social avatar

@dimillian @ctietze @dimsumthinking Yes, it does! Correct, it specifically lacks the auto closure, i.e. the feature that a state object is only ever created once.
In practice a lot of people trigger a lot of things in init (e.g. start fetches). That's not going to fly anymore, it needs to be cheap and probably has to have a resume like operation triggered from the body.
(I.e. it has exactly the issue shown in Christian's original post)

dimillian,
@dimillian@mastodon.social avatar

@helge @ctietze @dimsumthinking TBH, it doesn’t matter for 99% or apps and developers. If you don’t do anything in the init then it’s just wasting a bit or CPU and memory. 100% Apple fault to have shipped it in such a state. I’m so sad with the state of this and how easy is to trigger performances bottleneck without noticing.

helge, (edited )
@helge@mastodon.social avatar

@dimillian @ctietze @dimsumthinking I'm sure that will be a very confusing transition for a lot of people and I honestly don't understand why this was done, there could have been a @State overload for Observable or AnyObject. Why create a state object all the time, it just doesn't make any sense. (a escaping closure is probably still an object, so also does a malloc/free, but doesn't have to init all ivars, which even an empty init still has to do).
Upd: There was an overload, got removed.

helge,
@helge@mastodon.social avatar

@dimillian @ctietze @dimsumthinking But funny enough that might drive people into the direction I'd like to see SwiftUI apps architected 🙈 I.e. keep the controller/"vm" tree outside of the View tree and never put an object into State, except for the root View (app or scene).

dimillian,
@dimillian@mastodon.social avatar

@helge @ctietze @dimsumthinking debatable on this. I often couple small VM with small views. So you have a lot of independent components. But with initializing StateObject myself as I need to pass parameters I always had to be careful.

dimillian, (edited )
@dimillian@mastodon.social avatar

@helge @ctietze @dimsumthinking but if you never profile your app or put a breakpoint you won’t notice right? The behaviour doesn’t change. (Trying to grasp my head around it he impact)

helge,
@helge@mastodon.social avatar

@dimillian @ctietze @dimsumthinking The behaviour does change significantly! It only doesn't change if your OO init already doesn't do anything and doesn't have side effects.
I obviously have no stats, but I'm sure a lot of people do trigger things in the init, e.g. start an async image fetch and similar things.
I.e. previously you got the "resume" behaviour as part of the StateObject. Now you have to explicitly call it in the body, e.g. in onAppear (which is also later).

dimillian,
@dimillian@mastodon.social avatar

@helge @ctietze @dimsumthinking alright we do agree on this. Yes if your init have side effect you’re in for a bad day. That’s why we have task and other modifier on those to trigger all the nice view lifecycle related events. Personally never doing anything in the init because of how StateObject was built.

ctietze,
@ctietze@mastodon.social avatar

@dimillian In our app, I just found out that something leaks memory. It's related to objects ab-using this pattern, so I'm suspicious. Will check on Monday.

It appears during transitions, from one scene/view to another and back:
A1 → B1 → A2
Where A2 is the second real instance requested.

When A2 is created, A1's StateObject is freed. So free'ing is lazy?

When we change a property (e.g. selection), it's not free'd at all.

That's worrysome. But it still looks ok!

@helge @dimsumthinking

dimillian,
@dimillian@mastodon.social avatar

@ctietze @helge @dimsumthinking this is very interesting because I found a bug where I get two instance of the same observable object hold as a state object on the same view doing the same action twice and both triggering the same view refresh. I still don’t know how this is possible.

helge,
@helge@mastodon.social avatar

@dimillian @ctietze @dimsumthinking If you look at my sample and trace you’ll notice that SwiftUI seems to hang on for one throw away instance per iteration. might be related

ctietze,
@ctietze@mastodon.social avatar

@helge @dimillian @dimsumthinking I'm stuck at this point of the conversation myself.

Why is this possible, why is this so wasteful by default, and is the intended use case to create these objects internally once, period, and good luck figuring out what to do with real apps?

I know one of @helge's answer to this, MVC, but it's so odd that the defaults are full of trip-wires --

And I'm stuck thinking this over to understand what the author's intentions could've been. It's philosophy BA again.

  • All
  • Subscribed
  • Moderated
  • Favorites
  • SwiftUI
  • DreamBathrooms
  • mdbf
  • ngwrru68w68
  • magazineikmin
  • thenastyranch
  • rosin
  • khanakhh
  • osvaldo12
  • Youngstown
  • slotface
  • Durango
  • kavyap
  • InstantRegret
  • GTA5RPClips
  • provamag3
  • ethstaker
  • cisconetworking
  • tester
  • modclub
  • everett
  • cubers
  • tacticalgear
  • Leos
  • megavids
  • normalnudes
  • anitta
  • JUstTest
  • lostlight
  • All magazines