Skip to content

Use @StateObject for iOS 15+ alert modifier. #1860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 24, 2023
Merged

Conversation

mbrandonw
Copy link
Member

Just a small win I found. Eventually we want to move a lot of @ObservedObjects to @StateObjects in the library, but can't until we drop iOS 13. However, these two instances can be done now.

@mbrandonw mbrandonw merged commit 6f33e07 into main Jan 24, 2023
@mbrandonw mbrandonw deleted the alert-state-object branch January 24, 2023 00:04
@johnrogers
Copy link

johnrogers commented Feb 8, 2023

Hey guys, we just updated our project to use 0.50.1 and have started to notice that some confirmation dialogs and alerts have ceased to function. As in, they don't appear at all. This only occurs on iOS 15 and 16 -- I have tested iOS 14 and the dialogs/alerts in the affected areas still appear correctly. Could this be anything to do with this change?

It's worth nothing that it is only in certain places in the app (particularly those with more complex navigation structures) that they're broken. I have tried to reproduce the issue in a sample project with a simple view and they appear correctly.

Edit: To determine whether this change is the culprit, I copied swift-composable-architecture into the project locally and reverted the changes to NewConfirmationDialogModifier and NewAlertModifier seen in this PR (viewStore becomes @ObservedObject again), and can confirm that after reverting the change, the dialogs and alerts that were previously not working are all working correctly again.

cc @mbrandonw @stephencelis

@mbrandonw
Copy link
Member Author

Hi @johnrogers, bummer! Do you know of a way to reproduce it so that we can see if it's possible to fix before reverting? Somehow the alert/dialog case study is working just fine, afaict.

Also, would you mind start a new discussion when replying rather than replying to this PR? I'm worried the conversation is going to get lost since this is such an old PR.

@tgrapperon
Copy link
Contributor

tgrapperon commented Feb 9, 2023

@johnrogers One thing that can be interesting to check if you open a discussion, is the way you're extracting your confirmationDialog/alert store. For example, something like:

isRegistered ? store.scope(state:) : store.scope(state:)

would now break. But it can also be something more subtle, or even a SwiftUI bug.

@mbrandonw
Copy link
Member Author

Hi @johnrogers, I just wanted to follow up with this and see if you got to the bottom of it? We're hoping to start embracing @StateObject more fully in the library once we can drop iOS 13 support, and so we'd like to know if there is something weird going on with them.

@tgrapperon
Copy link
Contributor

tgrapperon commented Feb 13, 2023

Hey @mbrandonw, yes we did: see https://pointfreecommunity.slack.com/archives/C04KQQ7NXHV/p1676257703922689?thread_ts=1675899910.360799&cid=C04KQQ7NXHV and below. In a few words, some store was scoped without using the state argument and was capturing an outer state value provided by a NavigationLink binding. StateObject was thus likely capturing a store that continuously returned this value. Refactoring the scope to use the state argument fixed the issue.

@alexito4
Copy link
Contributor

alexito4 commented Mar 9, 2023

Just commenting here to say that we also had issues with this, thanks for posting the solution, it was the same problem indeed.

Our case was that we had state being an enum and we had some code in the UI that switched on the enum, and in one case used a scope to reach into the state but since the staet inside the scope was the base it meant that reaching for the associated type resulted in optional. Even if in theory that code would run only if the outter switch matched, I don't trust SwiftUi to not run the code in weird scenarios. So for safety we decided to take the scoped state from the associated value of the outer switch instead of using the state inside the scope. It was the easiest thing back then.

Something like this:

WithViewStore(store) { viewStore in
            switch viewStore.stateEnum {
            case .oneCase:
                SomeView()
            case let .caseWithState(state):
                OtherViewThatNeedsState(
                    store.scope(
                        state: { _ in state }, // here, using the state in the closure is a PITA so we cheated and used the outer one!
                        action: ...n
                    )
                )
            }
        }

But now we have SwitchStore and CaseLet so we switched to that instead and it works quite well 🤞🏻

@tgrapperon
Copy link
Contributor

tgrapperon commented Mar 9, 2023

@alexito4 Indeed, store.scope(state: { _ in … is very likely pathological, and should be at the very least considered as a code smell. I'm not sure we can do things to force the argument to be used. Maybe the new ownership thing could help? I know that you're familiar with it (I'm not yet!)!

@alexito4
Copy link
Contributor

Thanks for confirming @tgrapperon
Solving this with ownership will be intersting indeed, but AFAIK there are no plans (yet?) to introduce "must use" types in Swift. From reading others deal with this in other language seems like is a big complication that is not always beneficial. We shall see! ^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants