Skip to content

Add advanced introspect(_:onOrAfter:...) modifier #284

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

Closed
wants to merge 6 commits into from

Conversation

davdroman
Copy link
Collaborator

@davdroman davdroman commented Jun 29, 2023

The discussion with @paescebu at #280 made me realize there might be a chance for an additional introspection modifier for advanced users such as library developers, with the following shape:

import SwiftUI
@_spi(Advanced) import SwiftUIIntrospect

struct ContentView: View {
    @State var name = ""

    var body: some View {
        TextField("Name", text: $name)
            .introspect(.textField, onOrAfter: .iOS(.v13)) { textField in
                // do something with textField
            }
    }
}

This is more concise, and will run on future unreleased versions (such as iOS 18 at the time of this writing) in contrast with the standard:

import SwiftUI
import SwiftUIIntrospect

struct ContentView: View {
    @State var name = ""

    var body: some View {
        TextField("Name", text: $name)
            .introspect(.textField, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { textField in
                // do something with textField
            }
    }
}

However it shouldn't be taken lightly or as a shortcut. It should only serve to circumvent potential future breakage of libraries leveraging introspection at risk of not being actively maintained.

In an active codebase, the ideal modifier is still the exhaustive on: version, hence the guarding of onOrAfter: behind an @_spi(Advanced) flag to hide it away from most users.

I'd love to know people's thoughts on this, especially @paescebu if you see this.

@davdroman davdroman force-pushed the experiment/on-or-after branch from 66527d6 to 2c572aa Compare June 29, 2023 15:30
@davdroman davdroman changed the title wip Add advanced introspect modifier with onOrAfter platform parameter Jun 29, 2023
@paescebu
Copy link
Contributor

Very nice to see that the discussion grinded some gears 😁

Love to see the effort!

Personally, I'm still a bit torn in between whether it's necessary for the library to offer this flexibility out of the box. I mean, it already does and proofs that "advanced" use cases like mine can already be achieved without any hacks. And for me it was quite intuitive to find my solution already.

It surely would be a nice addition! The necessity, is probably really questionable.

@davdroman
Copy link
Collaborator Author

davdroman commented Jun 29, 2023

@paescebu thank you for your prompt thoughts!

I agree it's somewhat on the line of overindulging. I guess the main purpose of this inclusion is to avoid littering most libraries with a lot of the same platform version extensions you've included in yours, and I was planning to include in mine.

Whilst it might've felt intuitive for you to extend it (and I'm glad!), it may not feel as intuitive for other developers, or it might be annoying for them having to redefine platform versions for every view type just to ensure long term passive maintainability of their work. I know I certainly would be annoyed about polluting my code with error prone platform checks that already exist in the original library in the first place 😄

I'm going to let this branch marinate for a few days, try it out in one of my libraries and come back with a decision. In the meantime, I encourage you to try this branch in your own library and let me know how it feels, if it's not too much to ask.

@paescebu
Copy link
Contributor

paescebu commented Jun 29, 2023

Definitely playing around with it right now.

What this approach makes me think though, and I'm not sure if this thought makes sense as of now:
If this introspection modifier was added, would we consequently also need to have a onOrBefore or even just a before and after option?

@davdroman davdroman changed the title Add advanced introspect modifier with onOrAfter platform parameter Add advanced introspect(_:onOrAfter:...) modifier Jun 30, 2023
@davdroman
Copy link
Collaborator Author

davdroman commented Jun 30, 2023

@paescebu I'd say before and after are counterproductive, because they allow too many absurd scenarios such as:

struct ContentView: View {
    var body: some View {
        List {
            Text("Item 1")
            Text("Item 2")
        }
        .introspect(.list, before: .iOS(.v13)) { tableView in
            // will never be called
        }
    }
}
struct ContentView: View {
    var body: some View {
        List {
            Text("Item 1")
            Text("Item 2")
        }
        .introspect(.list, after: .iOS(.v17)) { collectionView in
            // will only be called on iOS 18, which is unreleased, so why would anyone want this?
        }
    }
}
struct ContentView: View {
    var body: some View {
        List {
            Text("Item 1")
            Text("Item 2")
        }
        .introspect(.list, before: .iOS(.v16)) { collectionView in
            // this will also never be called, because the view type is UITableView on iOS 15 and below. not very intuitive.
        }
    }
}
struct ContentView: View {
    var body: some View {
        List {
            Text("Item 1")
            Text("Item 2")
        }
        .introspect(.list, after: .iOS(.v15)) { tableView in
            // this will also never be called, because the view type is UICollectionView on iOS 16 and above. not very intuitive.
        }
    }
}

As for onOrBefore:, there's more wiggle room for justifying its inclusion, especially for situations such as:

struct ContentView: View {
    var body: some View {
        List {
            Text("Item 1")
            Text("Item 2")
        }
        .introspect(.list, onOrBefore: .iOS(.v15)) { tableView in

        }
        .introspect(.list, onOrAfter: .iOS(.v16)) { collectionView in

        }
    }
}

But I think now we're truly overstepping the line of indulgence, because we're not solving any real problems with that syntax, we're only really making it more concise by sacrificing clarity. I think ultimately this is perfectly fine to write and maintain:

struct ContentView: View {
    var body: some View {
        List {
            Text("Item 1")
            Text("Item 2")
        }
        .introspect(.list, on: .iOS(.v13, .v14, .v15)) { tableView in

        }
        .introspect(.list, onOrAfter: .iOS(.v16)) { collectionView in

        }
    }
}

And if say, in a hypothetical future iOS 19 version the type changes to something else, it's as easy as:

struct ContentView: View {
    var body: some View {
        List {
            Text("Item 1")
            Text("Item 2")
        }
        .introspect(.list, on: .iOS(.v13, .v14, .v15)) { tableView in

        }
        .introspect(.list, on: .iOS(.v16, .v17, .v18)) { collectionView in

        }
        .introspect(.list, onOrAfter: .iOS(.v19)) { fooBarView in

        }
    }
}

Would love to know your thoughts as well.

@paescebu
Copy link
Contributor

Exactly! You perfectly summarized my thoughts. Was just worried that If you end up offering 'onOrAfter', people start asking for those special cases as well.

But I wouldn't go down that path either. I'm just thinking if this can be approached differently overall. Maybe even without the need of an overloaded introspection modifier.

@davdroman

This comment was marked as outdated.

@davdroman
Copy link
Collaborator Author

Closing in favor of #285.

@davdroman davdroman closed this Jun 30, 2023
@davdroman davdroman deleted the experiment/on-or-after branch June 30, 2023 13:31
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.

2 participants