3

iOS13_onChange.swift

 3 months ago
source link: https://gist.github.com/jegnux/c3aee7957f6c372bf31a46c893a6e2a2
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

iOS13_onChange.swift · GitHub

Instantly share code, notes, and snippets.

Last active February 1, 2024 14:51
  • Star 38 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist

Save jegnux/c3aee7957f6c372bf31a46c893a6e2a2 to your computer and use it in GitHub Desktop.

Author

This version of onChange(of:perform:) is a full iOS 13 compatible drop-in replacement of iOS 14's eponym modifier.

Thanks to @_disfavoredOverload this method will be used in favor of native modifier only where the iOS 13 compatibility is required. This is why in the if #available(iOS 14, *) I can safely call the original method without causing a recursive loop.

Nice solution. One thing you might want to add: Just in line 22 requires import Combine, otherwise it won't build.

Author

@bennokress thanks, it's done :)

I have my own implementation of this (similar but different) but one issue I found (and I imagine its the same here) is that the behaviour is ever so slightly different.

onReceive is called essentially after the change, whereas onChange is called before, allowing the consumer to compare the previous value with the newValue if required.

I never found a way to implement it this way and mostly is a non-issue. Just mentioning here for others in case its unclear that the behaviour is not identical to that of onChange and this may be important in some cases 👍

Author

@shaps80 Apple's implementation of onChange achieve this by allowing to capture the "oldValue" as part of the closure as described in the documentation (see below). My implementation should work the same.

CleanShot 2022-02-04 at 09 37 45@2x

@jegnux That's true but I tested this and both yours and my own solution don't appear to work this way. Essentially it seems the @State properties have already changed in our implementations. Not sure how Apple's achieving this tbh. I'd love it if you can prove otherwise but in my own testing it didn't behave that way.

Author

@shaps80 I just tried it in a sample project and it seems to work as I described it.
If you're able to setup a sample project showcasing your issue, I'd be happy to help you.

CleanShot 2022-02-11 at 16 58 57@2x

Interesting, I'll definitely re-test then as I'm 99% (was 100% hahaha) certain this didn't behave this way for me. I'd be super happy to be wrong here mind you 👍

Ok I can see my issue. I thought it was a shadowed property but actually that wasn't it.

Essentially I've also ported the .task(id:) modifier on top of this. However it was there that the task id change was triggering correctly, however the value appeared to always be the previous value.

I've now identified its because in my TaskModifier implementation, I had passed my id in as a @State property which was a mistake. Removing that property wrapper/annotation now fixed my issue.

So the onChange implementation (both yours and mine) were not at all at fault. My mistake 👍

What is the purpose of the onAppear() on line 22?

Also i'm getting [SwiftUI] Modifying state during view update, this will cause undefined behavior. on line 27, which is fixable by adding .receive(on: DispatchQueue.main) operator to the Just on line 13

-> .onReceive(Just(newValue).receive(on: DispatchQueue.main)) { newValue in

+1 To what is the purpose of onAppear

Author

@KoCMoHaBTa @kuanfajardo I don't remember well but it was probably a workaround to some bug.

This is such a beautiful solution...... wow......

Great solution! Saved me. Just one question: What's the idea of putting action into state? Seems, the action never changes within the struct lifecycle

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK