In the following example
import SwiftUI
class AbstractOverride {
open func configurationView() -> AnyView {
if Features.TEST_VERSION {
return AnyView(Text("override configurationView()"))
} else {
return AnyView(EmptyView())
}
}
open func someConfigurationView() -> some View {
if Features.TEST_VERSION {
return AnyView(Text("override someConfigurationView()"))
} else {
return AnyView(EmptyView())
}
}
}
class SubclassOverride: AbstractOverride {
override func configurationView() -> AnyView { // no compiler error
return AnyView(Text("Test"))
}
override func someConfigurationView() -> some View { // compiler error "Method does not override any method from its superclass"
return AnyView(Text("Test"))
}
}
I get a compiler error compiler error "Method does not override any method from its superclass"
at override func someConfigurationView() -> some View
.
There is no compiler error returning AnyView
.
can anybody explain what is happening here? Why does Swift not recognise that the method signatures are the same?
This is iOS 13, Xcode 11.5
The language design has dictated that it is completely impossible for the programmer to statically be provided with information about the relationship of two opaque types, such as whether they exist in a class inheritance chain.
var instanceOfType1: some ExpressibleByBooleanLiteral = true
var anotherInstanceOfType1 = instanceOfType1 as! Bool // "fine" 😝
// Cannot assign value of type 'some ExpressibleByBooleanLiteral' to type 'Bool'
anotherInstanceOfType1 = instanceOfType1
var instanceOfType2: some ExpressibleByBooleanLiteral = instanceOfType1 // fine
type(of: instanceOfType1) == type(of: instanceOfType2) // true
// Cannot assign value of type 'some ExpressibleByBooleanLiteral' (type of 'instanceOfType1')
// to type 'some ExpressibleByBooleanLiteral' (type of 'instanceOfType2')
instanceOfType2 = instanceOfType1
Originally, using opaque types in non-final declarations was forbidden.
opaque result types cannot be used for a non-final declaration within a class…
This restriction could conceivably be lifted in the future, but it would mean that override implementations would be constrained to returning the same type as their super implementation, meaning they must call super.method() to produce a valid return value.
The restriction has been lifted, but it is currently impossible to make use of it. E.g. even the simplest possible override will not compile.
override func someConfigurationView() -> some View {
super.someConfigurationView()
}
Opaque types can provide no protection against breaking the 'L'-rule in SOLID. some View
may actually be the same or a subtype of some View
(as your AnyViews
are), but there's no way for the language to enforce that.
…for example, that contract-breaking happens if you write without AnyView
, as you should be be doing:
class AbstractOverride {
@ViewBuilder func someConfigurationView() -> some View {
if Features.TEST_VERSION {
Text("override someConfigurationView()")
}
}
}
final class SubclassOverride {
func someConfigurationView() -> some View {
Text("Test")
}
}
The least painful solution is to just completely disallow yourself from using class inheritance in Swift. It has never been idiomatic—it's really only there for interaction with Objective-C.
struct ContentView<
ConfigurationViewProvider: ModuleName.ConfigurationViewProvider
>: View {
var body: some View {
ConfigurationViewProvider.someConfigurationView
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView<SubclassOverride>()
}
}
protocol ConfigurationViewProvider {
associatedtype ConfigurationView: View
@ViewBuilder static var someConfigurationView: ConfigurationView { get }
}
enum AbstractOverride: ConfigurationViewProvider {
static var someConfigurationView: some View {
if Features.TEST_VERSION {
Text("override someConfigurationView()")
}
}
}
enum SubclassOverride: ConfigurationViewProvider {
static var someConfigurationView: some View {
Text("Test")
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With