Skip to content
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

Segue 'id' ergonomics: isPresented regardless of ID, and access ID inside fragment #3

Closed
sebj opened this issue Feb 24, 2022 · 5 comments

Comments

@sebj
Copy link

sebj commented Feb 24, 2022

I'd like to optionally provide a segue 'id' to present a screen as a sheet and be able to retrieve that id from within the screen, but I'm not sure Helm is setup to allow for this, unless I'm missing something.

The two issues preventing this currently are:

Both isPresented functions that produce a Binding (to be able to show a sheet) seem to be overly specific – one matches against a screen with no ID, the other matches a screen with a particular ID, but there isn't a way to match a particular screen with 'any ID'. It'd be useful to have some sort of wildcard isPresented for this.

There isn't an easy way to access the ID for a presented fragment. I suppose helm.presentedFragments.last!.id works, but doesn't feel as nice as (hypothetically) @Environment(\.presentedID) var id

Any thoughts on resolving these? Thanks!

@valentinradu
Copy link
Owner

@sebj Can you checkout .matchFirst(fragment:) ? Or .matchAll(fragment:) if there's a chance to present multiple overlapping sheets containing different ids (probably not the case tho).

@sebj
Copy link
Author

sebj commented Feb 24, 2022

Thanks @valentinradu – unfortunately it seems like both matchFirst and matchAll will only match instances of a fragment that have an ID (unless I'm missing something, let me know! 😅 ). I'd like to match the last instance of fragment, whether or not it has an ID, and get that ID as an optional value.

Having said that, I've found this works (super rough and untidy though – need to go over it properly!):

extension Helm {

    func isAnyPresented<ID>(_ fragment: N) -> Binding<PresentedFragmentIdentifier<ID>?> {
        Binding(
            get: {
                if let fragment = self.presentedFragments.first(where: { $0.wrappedValue == fragment }) {
                    return (fragment.id?.base as? ID).map(PresentedFragmentIdentifier.identifier) ?? PresentedFragmentIdentifier.none
                } else {
                    return nil
                }
            }, set: { item in
                // TODO: Dismiss
            }
        )
    }
}

enum PresentedFragmentIdentifier<Identifier>: Hashable where Identifier: PathFragmentIdentifier {
    case none
    case identifier(Identifier)
}

extension PresentedFragmentIdentifier: Identifiable where Identifier: Identifiable {
    var id: AnyHashable {
        switch self {
        case .none: return self
        case let .identifier(value): return value
        }
    }
}

@valentinradu
Copy link
Owner

I think I get it, but the problem I see here is that usually fragments have ids or don't have ids. Meaning, if you have a .details fragment showing each item of a list, it should not be possible to show a .details without any id. I wished I could have enforced this at compile time, but it's not possible at the moment.

What's your use case for this? Might be that the fragments need a bit of redesigning instead?

@sebj
Copy link
Author

sebj commented Feb 25, 2022

What's your use case for this? Might be that the fragments need a bit of redesigning instead?

I've got a screen that functions as both 'create' and 'edit' – there's almost no UI difference between the two, so I'd rather not create two separate screens if I can avoid it. If an item ID is passed into the screen, I fetch the item to edit and populate the UI (edit mode), otherwise I assume it is in create mode.

@valentinradu
Copy link
Owner

Sorry for the late reply, been busy these days. These are 2 different screens from the router's point of view (although you might map them to the same view and use maybe a flag to differentiate, if that's what you wish)

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

No branches or pull requests

2 participants