Overriding Superclass Property With Different Type in Swift

Overriding superclass property with different type in Swift

Swift does not allow you to change the class type of any variables or properties. Instead you can create an extra variable in the subclass that handles the new class type:

class Chassis {}
class RacingChassis : Chassis {}

class Car {
var chassis = Chassis()
}
class RaceCar: Car {
var racingChassis = RacingChassis()
override var chassis: Chassis {
get {
return racingChassis
}
set {
if let newRacingChassis = newValue as? RacingChassis {
racingChassis = newRacingChassis
} else {
println("incorrect chassis type for racecar")
}
}
}
}

It seems one cannot declare a property with the let syntax and override it with var in it’s subclass or vice-versa, which may be because the superclass implementation might not be expecting that property to change once initialized. So in this case the property needs to be declared with ‘var’ in the superclass as well to match the subclass (as shown in the snippet above). If one cannot change the source code in the superclass then its probably best to destroy the current RaceCar and create a new RaceCar every time the chassis needs to be mutated.

Overriding superclass property with different type

The reason it works with UIScrollView and UITableView and their delegates is that they are generated Swift interfaces from the original Objective-C headers.

Objective-C lets you do this. While you can't create Swift classes that do this directly, Swift class interfaces generated from an Objective-C bridging header can result is the case you see here.

Override property with different type

The way it works for Objective C is because of the dynamic nature of the language. You can redeclare the type of super class and instead of synthesizing the property you would make it a dynamic type, which would let you redeclare it.

@protocol MyTableViewDelegate<UITableViewDelegate>

- (void)demoDelegateMethod;

@end


@interface WrapperTableView: UITableView

@property (nonatomic, weak, nullable) id <MyTableViewDelegate> delegate;


@end

@implementation WrapperTableView

@dynamic delegate;

@end

But, I doubt this would be possible with Swift, since, you are changing the type completely. It is because of Swift being strong static language.

Answer Edited

I got your approach. I write above approach in Objective-C and then inherit this class in Swift.
So if I have to override SuperClass property with different type I need to create a wrapper class in Objective-C inherit from desired SuperClass and then finally instead of inheriting directly from desired super class I should inherit from the newly created WrapperClass which is written in Objective-C

class MyTableView: WrapperTableView {
//Now Here the delegate object is of type MyTableViewDelegate
}

This approach is far better then

class MyTableView: UITableView {
private var myDelegate: MyTableViewDelegate?
override var delegate: UITableViewDelegate {
set{
myDelegate = newValue
}
get{
return myDelegate
}

}
}

Overriding variable property with different type

The problem is very simple. You can't do that. UIViewController, the class you're inheriting from, has this property under lock and key. You'll need to create yourself a new solution depending on what you're trying to achieve:

Condition A: Child is a subclass of UIViewController

In this case, you want a way to make the child view controllers of ParticipantsListViewController always conform to Child. One way to do this would be the following computed property:

var listChildren: [Child] {
return children.filter { $0 is Child }
}

Condition B: Child is NOT a subclass of UIViewController

You're trying to override something that the system needs to be there. Things in the children array have to be instances or subclasses of UIViewController. It's strict.

Your solution here is easy. Name the property differently and get rid of the override. Sure, it won't have the nicest, simplest name children, but that's the way it goes.

Swift: Override Subclass Properties that are also Subclasses of the Parent Class Property Class

Taken from here:

Overriding Property Getters and Setters

You can provide a custom getter (and setter, if appropriate) to
override any inherited property, regardless of whether the inherited
property is implemented as a stored or computed property at source.
The stored or computed nature of an inherited property is not known by
a subclass—it only knows that the inherited property has a certain
name and type. You must always state both the name and the type of the
property you are overriding, to enable the compiler to check that your
override matches a superclass property with the same name and type.

Seems like you cant do that.

Why disallow subclasses to override superclass properties with a derived type?

This is not actually limited to Swift. This would break polymorphism. See the subtyping rules on wikipedia: Subtyping

Basically, consider the following example:

class A {
var x: NSObject?
}

class B: A {
override var x: NSNumber?
}

and consider:

let b = B(x: 1)
let a: A = b // it's a subclass, polymorphism allows to assign it to A
a.x = NSObject() // let's assign NSObject() because A.x takes NSObject
print(b.x) // b.x should be a NSNumber now but we have assigned a NSObject?

You have to realize that a property is a combination of two functions, a setter and a getter. You could add a more specific type (covariant type) to the getter (return value of a function) but you cannot do that for a setter (parameters of an overriden function require contravariance).

That also tells you that this would work with a readonly property:

class A {
var x: NSObject? {
return NSObject()
}
}

class B: A {
override var x: NSNumber? {
return NSNumber()
}
}

Override property in Swift subclass

Let’s reduce the example:

class BaseClass {
var surname: String? {
didSet { print("BaseClass \(surname)") }
}
}

class SubClass: BaseClass {
override var surname: String? {
didSet { print("SubClass \(surname)") }
}
}

Then:

let object = SubClass()
object.surname = "Jones"

Will produce:

BaseClass Optional("Jones")

SubClass Optional("Jones")

The above is not overriding the stored property, surname, with another stored property. There is only the stored property of the base class and the subclass is simply adding its own observer to this property. I refer you to The Swift Programming Language: Inheritance: Overriding, which says:

Overriding Property Observers


You can use property overriding to add property observers to an inherited property. This enables you to be notified when the value of an inherited property changes, regardless of how that property was originally implemented.

In your example of name, you are overriding the computed property with the subclasses’ own computed property. Likewise, in your example of telephoneSet, you are also overriding the method with the subclasses’ own method. But with surname, you’re not overriding the base classes’ property, but merely letting the subclass add an observer to the base classes’ stored property.

How to override readonly property in Swift subclass, make it read-write, and assign to superclass?

super.inputAccessoryViewController is not settable.

Your overridden implementation in the subclass, self.inputAccessoryViewController is.

By adding a setter to the property in a subclass, you don't automatically also add the same thing in the superclass. What's in the subclass stays in the subclass.

So it's not that you can't override a property by adding a setter, you just can't set this here:

override var inputAccessoryViewController: UIInputViewController? {
get { super.inputAccessoryViewController }
set { super.inputAccessoryViewController = newValue }
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}

You can do other things, like:

override var inputAccessoryViewController: UIInputViewController? {
get { super.inputAccessoryViewController }
set { print("I just go set to \(newValue)") }
}

But that's not very useful. What you want is probably:

private var myInputAccessoryController: UIInputViewController?

override var inputAccessoryViewController: UIInputViewController? {
get { myInputAccessoryController }
set { myInputAccessoryController = newValue }
}


Related Topics



Leave a reply



Submit