Let's step back a bit and see why this won't work. The function you just made looks like this:
extension Picker where SelectionValue: PickerEnum {
init(selection: Binding<SelectionValue>, label: Label)
}
Now there are still three generic parameters, Data, ID, and Content. So caller needs to specify them, either by type inference or explicit annotation.
Since you're not constraining any of the generic parameters, you're telling the caller that they can use any type they want. Which includes calling it like this:
Picker<[Int], Int, Text>(selection: ..., label: ...)
You can see that this most definitely won't be the view you want. This is caused by the fact that your constraint is too lenient. Before laying down the API, let's see, we want a function that:
- Returns a
Picker<Label, SelectionValue, Content>,
- Caller decides the type of
SelectionValue,
- Caller decides the type of
Label,
- Callee decides the type of
Content,
- Specifically,
Content isForEach<
SelectionValue.AllCases,
SelectionValue,
ModifiedContent<Text, _TraitWritingModifier<TagValueTraitKey<SelectionValue>>>>
We can start as a global function, and see where this would fit. Since SelectionValue and Label is specified by the caller, we can use generic parameter:
func makePicker<SelectionValue, Label>(selection: Binding<SelectionValue>, label: Label) -> ??
Now, what can we use for ??? Not a lot, actually. Remember, we want to return a picker but want to conceal Content, so we need to hide it. some is a good tool for this
func makePicker<SelectionValue, Label>(selection: Binding<SelectionValue>, label: Label) -> some View
Since in all honesty, you probably won't care that it's a Picker, and not just some View.
So now we get the function outline, it has two type parameters SelectionValue and Label, which is selected by the caller, and returns some view whose type the caller doesn't care (other than the fact that it conforms to View). Perfect, just what we want.
Then we can put in the implementation:
func makePicker<SelectionValue, Label>(selection: Binding<SelectionValue>, label: Label) -> some View {
Picker(selection: selection, label: label) {
ForEach(SelectionValue.allCases, id: \.self) {
Text($0.displayName).tag($0)
}
}
}
Now there no weird as! anymore, since the type makes sense with the function signature.
The question is, can we put it somewhere else? Maybe in Picker? The answer is likely no. Remember, you need to know the full type to call a function. Even if we make makePicker a static member of Picker. It's still
Picker<Label, SelectionValue, Content>.makePicker(...) -> some View
Now it doesn't looks like it has anything to do with Picker. Worse, we're now back to the problem with specifying Content.
We could gather functions like this in one place, like Publishers did (notice the s).
All in all, we could make a global function, or put it in a namespace. You can also add constraint to that Picker.init of yours:
extension Picker where SelectionValue: PickerEnum, Content == ...
but I think that's a little too much burden on the caller side, to know the type of this Content.
Anyhow, I'd suggest that you investigate why you simply can't move pickerStyle outside.
It works just find on Swift Playground.
protocol PickerEnum: Hashable, CaseIterable where AllCases: RandomAccessCollection {
var displayName: String { get }
}
// A generic Picker that choose value of type PickerEnum
struct MyPicker<S: PickerEnum, Label: View>: View {
var selection: Binding<S>
let label: Label
var body: some View {
Picker(selection: selection, label: label) {
ForEach(S.allCases, id: \.self) {
Text($0.displayName).tag($0)
}
}
}
}
enum InputMode: PickerEnum {
case grid, spectrum, sliders
var displayName: String {
String(describing: self).capitalized
}
}
PlaygroundPage.current.setLiveView(
MyPicker (selection: .constant(InputMode.grid), label: Text("Test"))
.pickerStyle(SegmentedPickerStyle()))
Good thing you follow up. There's nothing more dangerous than (you're) confused and (it) compiles. Adding things to please ye almighty compiler definitely fits the bill. It's a recipe for disaster essentially everywhere.