Override property in Swift subclass - swift

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.

Related

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

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.

Why does a property observer run when a member of the existing value is changed?

Please consider this Swift code. I have a class which wraps an instance of another class. When I set a property on the held value, the wrapper class's property observer is run.
protocol MyProtocol {
var msgStr: String? { get set }
}
class MyClass: MyProtocol {
var msgStr: String? {
didSet {
print("In MyClass didSet")
}
}
}
class MyWrapperClass {
var myValue: MyProtocol! {
didSet {
print("In MyWrapperClass didSet")
}
}
}
let wrapperObj = MyWrapperClass()
wrapperObj.myValue = MyClass() // Line1
wrapperObj.myValue.msgStr = "Some other string" // Line2
The output of above code is:
In MyWrapperClass didSet
In MyClass didSet
In MyWrapperClass didSet
I know that didSet is called when the value of the variable changes.
So when above code at "Line1" executes I understand that "In MyWrapperClass didSet" is printed, and that is fine.
Next when Line2 executes, I expect "In MyClass didSet" to be printed which correctly happens, but I am not sure why "In MyWrapperClass didSet" is printed, as the property myValue is not changed. Can someone explain why?
Swift needs to treat the mutation of myValue.msgStr as having value semantics; meaning that a property observer on myValue needs to be triggered. This is because:
myValue is a protocol-typed property (which also just happens to be optional). This protocol isn't class-bound, so conforming types could be both value and reference types.
The myStr property requirement has an implicitly mutating setter because of both (1) and the fact that it hasn't been marked nonmutating. Therefore the protocol-typed value may well be mutated on mutating though its myStr requirement.
Consider that the protocol could have been adopted by a value type:
struct S : MyProtocol {
var msgStr: String?
}
In which case a mutation of msgStr is semantically equivalent to re-assigning an S value with the mutated value of msgStr back to myValue (see this Q&A for more info).
Or a default implementation could have re-assigned to self:
protocol MyProtocol {
init()
var msgStr: String? { get set }
}
extension MyProtocol {
var msgStr: String? {
get { return nil }
set { self = type(of: self).init() }
}
}
class MyClass : MyProtocol {
required init() {}
}
class MyWrapperClass {
// consider writing an initialiser rather than using an IUO as a workaround.
var myValue: MyProtocol! {
didSet {
print("In MyWrapperClass didSet")
}
}
}
In which case the mutation of myValue.myStr re-assigns a completely new instance to myValue.
If MyProtocol had been class-bound:
protocol MyProtocol : class {
var msgStr: String? { get set }
}
or if the msgStr requirement had specified that the setter must be non-mutating:
protocol MyProtocol {
var msgStr: String? { get nonmutating set }
}
then Swift would treat the mutation of myValue.msgStr as having reference semantics; that is, a property observer on myValue won't get triggered.
This is because Swift knows that the property value cannot change:
In the first case, only classes can conform, and property setters on classes cannot mutate self (as this is an immutable reference to the instance).
In the second case, the msgStr requirement can only either be satisfied by a property in a class (and such properties don't mutate the reference) or by a computed property in a value type where the setter is non-mutating (and must therefore have reference semantics).
Alternatively, if myValue had just been typed as MyClass!, you would also get reference semantics because Swift knows you're dealing with a class:
class MyClass {
var msgStr: String? {
didSet {
print("In MyClass didSet")
}
}
}
class MyWrapperClass {
var myValue: MyClass! {
didSet {
print("In MyWrapperClass didSet")
}
}
}
let wrapperObj = MyWrapperClass()
wrapperObj.myValue = MyClass() // Line1
wrapperObj.myValue.msgStr = "Some other string" // Line2
// In MyWrapperClass didSet
// In MyClass didSet
I suspect this is happening because your protocol is not specified to be a class protocol. Because of that, MyProtocol could be a struct and thus didSet is triggered when the object is changed in any way (which is correct behavior for a value type).
If you change your protocol to:
protocol MyProtocol: class {
var msgStr: String? { get set }
}
then Swift knows that MyProtocol represents a reference type, so didSet will not be called for myValue in MyWrapperClass when the string is set.
It looks like a bug, see: https://bugs.swift.org/browse/SR-239
Also workaround is predefining variable, like:
protocol MyProtocol {
var msgStr: String? { get set }
}
class MyClass: MyProtocol {
var msgStr: String? {
didSet {
print("In MyClass didSet")
}
}
}
class MyWrapperClass {
var myValue: MyProtocol! {
didSet {
print("In MyWrapperClass didSet")
}
}
}
let wrapperObj = MyWrapperClass()
wrapperObj.myValue = MyClass() // Line1
var obj = wrapperObj.myValue!
obj.msgStr = "Some other string" // Line2

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.

Is there a way to initialize a class in swift by using a variable which contains the name of the class?

Suppose I have a simple factory which returns various subclasses of a custom ModelObject class like:
class func testModelObject(className: String) -> ModelObject
{
let obj = // instance of the subclass of ModelObject specified by className
return obj
}
Is there a way to do this? Will Swift freak out when I try to call any methods of that object? Should I have something else for my return type?
For best type safety, you should let testModalObject to accept a meta-type like:
class ModelObject {
required init() {}
}
class Subclass: ModelObject {
required init() { super.init() }
}
func testModalObject(_ type: ModelObject.Type) -> ModelObject {
return type.init()
}
testModalObject(Subclass.self)
If you really need a string input, you will need to rely on the Objective-C runtime and do some casting (see how to create instance of a class from a string in swift 3):
#objc
class ModelObject: NSObject { // <---
required override init() {}
}
#objc(MOSubclass) // <-- tell ObjC the name is "MOSubclass" instead of "????.Subclass".
class Subclass: ModelObject {
required init() { super.init() }
}
func testModalObject(_ typeName: String) -> ModelObject? {
let cls = NSClassFromString("MO\(typeName)") as? ModelObject.Type
return cls?.init()
}
testModalObject("Subclass")!

Calling same property from different classes in Swift?

If I have two explicit classes that both have the same property name, is there a way to call the property without having to define which class I'm using?
class firstClass {
var name = “Name”
init…..
}
class secondClass {
var name = “Another name”
init….
}
now another function can call
//does not work... I get an error saying AnyObject doesn't have property
func printNameOf(object: AnyObject) {
println(object.name)
}
//works but my software has a lot of classes, which means a ton of code
func printNameOf(object: AnyObject) {
if object is firstClass {
println((object as firstClass).name)
}
if object is secondClass {
println((object as secondClass).name)
}
}
You could do this by creating a protocol that both your classes conform to:
protocol NameProtocol {
var name: String {get set}
}
class firstClass: NameProtocol {
var name = "Name 1"
}
class secondCLass: NameProtocol {
var name = "Name 2"
}
func printNameOf(obj: NameProtocol) {
// You know obj has property name
println(a.name)
}