As I said earlier, you're right that your IdentifiableIndices<Base> is an implementation detail, in the sense that the user will never explicitly initialize an instance of that type. However, "implementation detail" and "private" are two different concepts. In order to have a type to be private, you need to ensure that that type won't surface in any type signature and it's usually achieved by means of type erasure (either using an existential box or an opaque type).
Let me change a little bit your initializer and let's suppose you can use a static function instead — let's call it ForEach.index(of:) — having an opaque return type some View:
extension ForEach
where Data.Element: Identifiable, ID == Data.Element.ID, Content: View {
public static func index(
of data: Data,
@ViewBuilder content: @escaping (Data.Index) -> Content
) -> some View {
ForEach<IdentifiableIndices<Data>, Data.Element.ID, Content>(
IdentifiableIndices(base: data)
) { index in
content(index())
}
}
}
struct MyView: View {
var body: some View {
ForEach.index(of: data) { index in
Text("\(index)")
}
}
}
It should work even with a private IdentifiableIndices<Base>. However, if you don't use an opaque type, the function signature becomes
func index(
of data: Data,
@ViewBuilder content: @escaping (Data.Index) -> Content
) -> ForEach<IdentifiableIndices<Data>, Data.Element.ID, Content>
which cannot be exposed as public since it has a private type in it. You'd get
Method cannot be declared public because its result uses a private type
With an initializer, i.e. what you have now, there's no way to erase the return type, so, even if your type is implementation detail, you need to keep it public.
Side note: do you really need to make IdentifiableIndices<Base> private?
If you follow SwiftUI's path, you'll see that implementation detail views are all public. As an example, if I recall correctly, originally SwiftUI had _ModifiedContent with a leading _ (the view used when you apply a modifier to a view). Now it has been renamed to just ModifiedContent.