Swift: Cannot override mutable property with read-only property 'xxx' - swift

I don't understand the meaning of this error. This is what I am trying to do:
protocol P {
var testTitle: String? { get }
}
class MyViewController: UIViewController, P {
var testTitle: String? {
didSet {
self.title = testTitle
}
}
}
final class TestViewController: MyViewController {
var testTitle: String? { // Error occurs here
return "Test"
}
}
And I'm getting:
Cannot override mutable property with read-only property 'testTitle'
Could you help me understanding what's going on? Maybe it's trivial for some people but not for me.
Thanks for your help.

You cannot implement a subclass to be more restrictive than the base class (it would only work the other way round). So if the base class implements a read/write property, you cannot make it read only in your derived class.
Therefore:
final class TestViewController: MyViewController {
override var testTitle: String? { // No error occurs here
get {
return "Test"
}
set {
super.testTitle = newValue
}
}
}

The protocol requires that the adopting class implements a property testTitle with a getter but it does not forbid to declare the property with getter and setter.
To override a property you have to override the (entire) signature in the class, not the protocol requirement.
As testTitle is declared as read/write you cannot override it as read-only.

Related

Override property in Swift subclass

Can anyone explain the behaviour when subclassing properties? I am sure there is a good explanation for why 'override' does not actually override the property.
Why does Swift allow the surname property to be overridden but apparently still uses the super class's property and associated functions? They are not overridden.
It would seem that I would have to define some function that gets called in the didSet() method and override that to ensure the subclass does not inherit the super class's function as with the telephone property.
Is there any way to override a property's didSet() method? Creating some function that gets called seems to add an unnecessary extra complexity?
What is the correct way of achieving this?
import Cocoa
class BaseClass {
var _name: String?
var name: String? {
get {
return _name
}
set {
_name = newValue
print("BaseClass \(name)")
}
}
var surname: String? {
didSet {
print("BaseClass \(surname)")
}
}
var telephone: String? {
didSet {
telephoneSet()
}
}
func telephoneSet(){
print("BaseClass \(telephone)")
}
}
class SubClass: BaseClass {
override var name: String? {
get {
return _name
}
set {
_name = newValue
print("SubClass \(name)")
}
}
override var surname: String? {
didSet {
print("SubClass \(surname)")
}
}
override func telephoneSet(){
print("SubClass \(telephone)")
}
}
let object = SubClass()
object.name = "Jenny"
object.surname = "Jones"
object.telephone = "10810"
Generates the following output:
SubClass Optional("Jenny")
BaseClass Optional("Jones")
SubClass Optional("Jones")
SubClass Optional("10810")
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.

If I subclass a class, can I specify a certain subclass an instance variable should be?

I have an NSObject subclass, BaseClass. BaseClass is a placeholder class for a couple subclasses, SubClassA and SubClassB. There is an instance variable that I have present on both of the subclasses. They're the same name, and are both of a corresponding subclass of another object. They're often used in very similar ways, so I wanted to move some functionality from my SubClassA and SubClassB to the BaseClass. However, I need access to that variable.
If I move the variable into the BaseClass, I am unable to specify the proper subclass of it in SubClassA and SubClassB, saying I can't override it. If I use the common parent class of this instance variable in the BaseClass, I lose some access to things that aren't common between how SubClassA and SubClassB work.
This is a more primitive example, but the basics of what I'm trying to do. This example obviously does not work. Are my only options to choose having to define common functionality within SubClassA and SubClassB or is there a proper way to achieve my goal here?
class BaseClass: NSObject {
var myObject: MyObject
}
class SubClassA: BaseClass {
override var myObject: MyObjectA
}
class SubClassB: BaseClass {
override var myObject: MyObjectB
}
class MyObject: NSObject { }
class MyObjectA: MyObject { }
class MyObjectB: MyObject { }
This gives me the error:
Property 'myObject' with type 'MyObjectA' cannot override a property with type 'MyObject'
How about using generic? For simplicity, I removed NSObject
class MyObject {
}
class MyObjectA: MyObject {
}
class MyObjectB: MyObject {
}
class BaseClass<T> where T : MyObject {
var myObject: T?
}
class SubClassA: BaseClass<MyObjectA> {
}
class SubClassB: BaseClass<MyObjectB> {
}
Instead of putting the myObject related code into BaseClass, you could put it into a protocol extension. Consider this:
class BaseClass {
}
class SubClassA: BaseClass, HasMyObject {
var myObject: MyObjectA
}
class SubClassB: BaseClass {
var myObject: MyObjectB
}
class MyObject { }
class MyObjectA: MyObject { }
class MyObjectB: MyObject { }
protocol HasMyObject {
associatedtype MyObjectClass
var myObject: MyObjectClass { get set }
}
This is conceptually very similar to using generics, but would separate your myObject related code from the rest of the code in your class. Whether this is actually preferable over generics depends on your coding style and specific use-cases.
You can create a class function in your BaseClass that returns a class that inherits from a BaseObject (myObject) and override it for whichever class you need.
class BaseClassObject: NSObject {
}
class BaseClass: NSObject {
func generateClass() -> NSObject {
return BaseClassObject()
}
}
class BranchedObject: BaseClassObject {
}
class SubClassA: BaseClass {
var myObject: NSObject?
override func generateClass() -> NSObject {
return BranchedObject()
}
override init() {
super.init()
self.myObject = self.generateClass()
}
}

Accessing Class var on AnyClass variable in Swift 4

In Swift 3.2 this (let id = row.tableViewCellClass?.reuseIdentifier) worked:
class DrillDownTableViewCell {
class var reuseIdentifier: String
{
return String(describing: self)
}
}
class RowViewModel: NSObject
{
var tableViewCellClass: AnyClass?
}
class Foo {
var row : RowViewModel?
func setup() {
row = RowViewModel()
row?.Class = DrillDownTableViewCell.self
}
func doThings() {
let id = row?.tableViewCellClass?.reuseIdentifier
}
}
After my Swift 4 update, it's showing "Instance member 'reuseIdentifier' cannot be used on type 'AnyObject'.
How would I access a class variable on a class who's metaType information is stored in an AnyClass variable?
(I assume you mean to have a ? after row in doThings(). I assume that's a typo and not part of the question. There are several other ? missing here and some other typos that I won't dive into.)
If you expect tableViewCellClass to have a reuseIdentifier class property, then it isn't of type AnyClass. There are many classes that don't have that property. You want classes that conform to a protocol:
protocol Identifiying {
static var reuseIdentifier: String { get }
}
So your model requires an Identifying class:
class RowViewModel: NSObject {
var tableViewCellClass: Identifiying.Type?
}
Then you can use this as you're expecting.

Polymorphism when overriding methods or properties in Swift

I want to ensure by compiler that CarViewController only receives a Car in the vehicle property.
Given the following swift example code:
class Vehicle {
func doSomething(){}
}
class Car: Vehicle {
func doCarThings(){}
}
class VehicleViewController : UIViewController {
var vehicle : Vehicle!;
override func viewDidLoad() {
super.viewDidLoad();
vehicle.doSomething();
}
}
class CarViewController:VehicleViewController {
var vehicle: Car!
override func viewDidLoad() {
super.viewDidLoad();
vehicle.doCarThings();
}
}
I get the following error: Cannot override mutable property 'vehicle' of type 'Vehicle!' with covariant type 'Car!'
I tried with a generics-based approach:
class Vehicle {
func doSomething(){}
}
class Car: Vehicle {
func doCarThings(){}
}
class VehicleViewController<T:Vehicle> : UIViewController {
var vehicle : T!;
override func viewDidLoad() {
super.viewDidLoad();
vehicle.doSomething();
}
}
class CarViewController:VehicleViewController<Car> {
override func viewDidLoad() {
super.viewDidLoad();
vehicle.doCarThings();
}
}
It is correct but using generics in storyboard classes results in errors (since they get compiled to objective-c).
How can I do this without using generics?
Thanks!
I'm really not sure about the design here, but to accomplish what you want you could do:
class CarViewController: VehicleViewController {
var vehicleAsCar: Car { return self.vehicle as! Car }
override func viewDidLoad() {
super.viewDidLoad();
vehicleAsCar.doCarThings();
}
}
But this seems very smelly. Safer night be:
class CarViewController: VehicleViewController {
override var vehicle: Vehicle! {
didSet {
assert(vehicle is Car, "Attempt to set vehicle to non-Car")
}
}
var vehicleAsCar: Car { return self.vehicle as! Car }
override func viewDidLoad() {
super.viewDidLoad();
vehicleAsCar.doCarThings();
}
}
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.

Altering (refining) delegate type in swift

I have a (swift) class which has a weak pointer to a delegate like this:
import UIKit
#objc public protocol DRSlidingPanelViewControllerDelegate : class {
optional func didSlidePanel(panelHidden : Bool , sender : DRSlidingPanelViewController) -> Void
}
public class DRSlidingPanelViewController: UIViewController {
public weak var delegate : DRSlidingPanelViewControllerDelegate?
///other stuff...
}
Now i make a subclass with another protocol which extends the first, and i want to alter the inherited 'delegate' property
#objc public protocol DRTableViewControllerDelegate : DRSlidingPanelViewControllerDelegate {
optional func someFunction(sender : DRTableViewController) -> Void
}
public class DRTableViewController: DRSlidingPanelViewController {
// public weak var delegate : DRTableViewControllerDelegate?
}
^ this (re)declaration of delegate in the subclass gives me 3 errors when I uncomment it.
Property 'delegate' with type 'DRTableViewControllerDelegate?' (aka 'Optional') cannot override a property with type 'DRSlidingPanelViewControllerDelegate?' (aka 'Optional')
Getter for 'delegate' with Objective-C selector 'delegate' conflicts with getter for 'delegate' from superclass 'DRSlidingPanelViewController' with the same Objective-C selector
Setter for 'delegate' with Objective-C selector 'setDelegate:' conflicts with setter for 'delegate' from superclass 'DRSlidingPanelViewController' with the same Objective-C selector
Now i understand the nature of these errors, and that they are different facets of the one error (attempting to change the 'type' on the delegate pointer.) Can anybody give me a clue how to do this? It obviously can be done, look at how UITableView alters the delegate pointer which it inherits from UIScrollView. In objC I would get a warning which could be silenced with an #dynamic.
Thanks and best regards
edit / addition
Thanks Matt, I do see this previous question, but unfortunately it is closed and I would personally not accept that answer as the definitive answer because it is a compromise.
If I right click on UITableView in xCode and 'jump to definition' I see this
#available(iOS 2.0, *)
public class UITableView : UIScrollView, NSCoding {
public init(frame: CGRect, style: UITableViewStyle) // must specify style at creation. -initWithFrame: calls this with UITableViewStylePlain
public init?(coder aDecoder: NSCoder)
public var style: UITableViewStyle { get }
weak public var dataSource: UITableViewDataSource?
**weak public var delegate: UITableViewDelegate?**
//plenty other stuff..
}
So I respectfully submit that there definitely is a way to do this.
I would say that as things stand you can't do it. This is disappointing to say the least. You'll just have to call the delegate variable in the subclass something else.
So, this is legal, but of course it totally fails to meet your requirements:
#objc protocol P1 {
}
#objc protocol P2 : P1 {
}
public class VC1: UIViewController {
weak var delegate : P1?
}
public class VC2: VC1 {
weak var delegate2 : P2?
}
If you really hate the multiplication of storage you can make the delegate2 a computed variable that accesses the inherited delegate:
#objc protocol P1 {
}
#objc protocol P2 : P1 {
}
public class VC1: UIViewController {
weak var delegate : P1?
}
public class VC2: VC1 {
weak var delegate2 : P2? {
set {
super.delegate = newValue
}
get {
return super.delegate as? P2
}
}
}