CustomView()
.doSomething() // ← should only be available on CustomView
.doSomethingElse() // ← should only be available on CustomView
AnyOtherView()
.doSomething() // ← should not compile
Pretty much like SwiftUI's Text implementation has that exact functionality:
struct CustomView: View {
...
}
extension CustomView {
func doSomething() -> some CustomView {
self.environment(\.someKey, someValue)
}
func doSomethingElse() -> some CustomView {
self.environment(\.someOtherKey, someOtherValue)
}
}
I get the following error: "An 'opaque' type must specify only 'Any', 'AnyObject', protocols, and/or a base class".
extension CustomView {
func doSomething() -> CustomView {
self.environment(\.someKey, someValue)
}
func doSomethingElse() -> CustomView {
self.environment(\.someOtherKey, someOtherValue)
}
}
I get the following error: Cannot convert return expression of type 'some View' to return type 'CustomView'. Xcode provides the following fix:
extension CustomView {
func doSomething -> CustomView {
self.environment(\.someKey, someValue) as! CustomView
}
}
But force casting does not really look like a great solution.
How can I fix this?
I only want to extend CustomView
. I don't want to extend View
and return some View
because that would expose functionality to all views (which is not what I want).
If I extend CustomView and simply return some View, then I cannot use both functions at the same time.
I am building up a Swift Package to provide CustomView
to multiple projects.
And to make CustomView
easy to use I wanted to make it configurable with view modifiers instead of a simple initializer.
I could use my provided CustomView
like that:
CustomView(value1: someValue, value2: someOtherValue)
... but I wanted to make it more SwiftUI-Like in the way of optional view modifiers like that:
CustomView()
.value1(someValue)
.value2(someOtherValue)
That would look nice if I needed other view modifiers on that view like tint(...)
or fixedSize()
, etc. Much like you would configure Text
, which you customize with view modifiers instead of the initializer, since customizing is optional.
As others have pointed out, you cannot pipe any modifier in your functions that have a return type other than your CustomView
With that said, you can do something like:
struct CustomView: View {
@State var myBackground = Color.clear
var body: some View {
Text("Hello World!")
.background(myBackground)
}
}
extension CustomView {
func customBackground(_ newBackground: Color) -> some CustomView {
self.myBackground = newBackground
return self
}
func clearBackground() -> some CustomView {
self.myBackground = .clear
return self
}
}
And use it as needed:
struct AnotherView: View {
var body: some View {
VStack {
CustomView()
.customBackground(.blue)
.clearBackground()
.customBackground(.red)
// ... and so on
Text("Hi everyone!")
.customBackground(.blue) // <--- this will fail compiling
}
}
}
But you cannot use any other modifier that erases your type. To reach your desired behavior, all the changes done in your functions/modifiers must be done only on properties accessible by your struct directly and have a return value that you guarantees is your view's type