I have no technical ability. I know nothing about Swift macros. But I have confidence in my taste, and the ability to express what I feel.

Introducing Slots - convenient view slots without the init explosion https://github.com/kylebshr/slots

TikTok - Make Your Day

@fatbobman I think you might like this!
@kylebshr The exquisite design greatly improves the efficiency of building custom components!
@fatbobman that’s very nice of you, thank you! Big fan of your blog, I’ve learned a lot from it
@kylebshr yesss this is great
@kyleve thought you might like it. did market ever start using SwiftUI?
@kylebshr yep! was mostly at feature parity by the time I got got
@kyleve still can't believe they got you
@kylebshr its for the best, im with the lord now
@kyleve @kylebshr he died doing what he loved (??)
@kyle @kyleve it was hard to tell honestly
@kylebshr @kyleve Great idea! Any plans on expanding this for ViewBuilders and EmptyView slots? That’s far and away my most common use of multiple initializers, for example when I make a custom View that has an optional header and footer requiring 3 extra inits to handle the situations where one or both are EmptyView.

@mergesort I think you'll find that's exactly what this handles? Except you define optionality with true optionals, and the init extensions use Never to express optional slots instead of EmptyView (something I never thought of until Claude suggested it).

I suggest reading the readme, I don't know if my snippet fully captures the functionality. Curious if you still think this doesn't solve your use case.

@mergesort also, reading this back sounds pretty snarky, I definitely didn't mean it that way, and I'm sorry for assuming you hadn't read it!

@kylebshr I did read through the readme before responding. (Twice actually. 🙂)

I’m a little unsure as to how it would handle the EmptyView scenario I mentioned, given you wouldn’t actually store it as nil but as a “slotted type”. In the icon example why is the annotation `.text`? I would expect something like `.empty`, but perhaps I’m completely misunderstanding how this is supposed to work.

@mergesort maybe you can describe what you’re trying to accomplish with EmptyView? Not sure *I* fully understand, but if I do I’ll try to explain it better

@mergesort I think I understand what you’re trying to do, and instead of something like `.empty` I’ve used the type system for this; optional view properties get init variations where they’re omitted from the init, and they get set to nil (and constrained to Never, though you could constraint to EmptyView for the same effect).

I prefer optional views instead of views that may be EmptyView, because you can customize the layout based on their presence instead of checking the type.

@mergesort also, sounds like I need a much better readme
@kylebshr Had to get a code snippet for you, but here's a real world (though oversimplified) example of what I mean, based no how SwiftUI provides multiple initializers for containers like this. https://gist.github.com/mergesort/085eb9986a64f904c17d3c452877d5f9
gist:085eb9986a64f904c17d3c452877d5f9

GitHub Gist: instantly share code, notes, and snippets.

Gist

@mergesort how would you feel if headerView was optional, and instead of where == EmptyView you used where == Never and assigned nil? Ignoring slots for a moment, I think this is a slightly better design that accomplishes the same thing, because internally you might want to adjust layout based on headerViews presence, e.g., in the body:

if let headerView {
Spacer()
headerView
}

@mergesort now, assuming you’re ok with this design (which is just a design choice) yes, slots does exactly what you did in the gist, but automatically
@mergesort another cool think about optional views is you can just place them in the view hierarchy and nil is transformed by the ViewBuilder into EmptyView
@kylebshr I haven’t had a chance to get to a computer and test this but my hesitation is that the pattern SwiftUI uses is the where == EmptyView constraint I’ve chosen, rather than providing nullable views. I’m not sure there is enough difference to skip this suggestion, but:
1. I haven’t considered every scenario so I’m not sure these two options being equivalent always holds true.
2. This scenario feels like something that a macro can handle as well, and IMO may be worth considering for Slots.
@mergesort yeah both patterns are valid, I could easily add a .empty option to slots for anyone who prefers that pattern!
@kylebshr Would love that, would make Slots an instant integration for me on top of the other features it provides. 🙂
Release 0.0.9 · kylebshr/slots

What's New SlotResolver Protocol A new SlotResolver protocol for custom slot resolution strategies, giving you full control over how slots are resolved from child views. Unlabeled Support Added .un...

GitHub
@mergesort you'll find extensions with constraints to optionals, so I think it'll work fine. I think there are internal tools to introspect if a view is EmptyView more easily than we have (or had, we have that new Group API now), so optional wasn't as necessary for internal views.
@kylebshr No shit, never noticed that so it seems like their usage is mixed. But I don't think you need anything special, I just do a check to see if the view is EmptyView.self.
@mergesort yeah I hate runtime type checks like that personally, feels like a code smell/bad practice
@mergesort replied on the gist with how Slots does exactly this!

@kylebshr @mergesort A more resilient approach I like to use for components like this is to use Group(subviews:transform:) to resolve subview proxies, so you can check if the view is _actually_ empty (beyond just inspecting types).

Unsure if that’s something a macro could help with — seems less likely.

@willing @mergesort that’s nice if you can target 18+!