How to Override Setter in Swift

How to override setter in Swift

What do you want to do with your custom setter? If you want the class to do something before/after the value is set, you can use willSet/didSet:

class TheSuperClass { 
var aVar = 0
}

class SubClass: TheSuperClass {
override var aVar: Int {
willSet {
print("WillSet aVar to \(newValue) from \(aVar)")
}
didSet {
print("didSet aVar to \(aVar) from \(oldValue)")
}
}
}

let aSub = SubClass()
aSub.aVar = 5

Console Output:

WillSet aVar to 5 from 0

didSet aVar to 5 from 0

If, however, you want to completely change how the setter interacts with the superclass:

class SecondSubClass: TheSuperClass { 
override var aVar: Int {
get {
return super.aVar
}
set {
print("Would have set aVar to \(newValue) from \(aVar)")
}
}
}

let secondSub = SecondSubClass()
print(secondSub.aVar)
secondSub.aVar = 5
print(secondSub.aVar)

Console output:

0

Would have set aVar to 5 from 0

0

Overriding getter in Swift

This works for me:

public class MyBaseClass {
private var _name: String = "Hi"
public internal(set) var name: String {
get {
return self._name
}
set {
self._name = newValue
}
}
}

public class MyDerivedClass:MyBaseClass {
override public var name: String {
get {
return "Derived - \(super.name)"
}
set {
super._name = newValue
}
}
}

MyDerivedClass().name

EDIT

This code works for me in a playground, placing it in the Sources -> SupportCode.swift file

public class MyBaseClass {
private var _name: String = "Hi"
public internal(set) var name: String {
get {
return self._name
}
set {
self._name = newValue
}
}
public init() {

}

}

public class MyDerivedClass:MyBaseClass {
override public var name: String {
get {
return "Derived - \(super.name)"
}
set {
// do nothing
}
}
public override init() {

}
}

It's a bit of a bodge because I get the same warning as you that internal(set) cannot be placed before the overridden subclass variable. It may well be a bug. And also I'm cheating to make sure the setter of the derived class does nothing.

A more common use of internal(set) or private(set) is to have code like this, which is similar to that in the documentation:

public class MyBaseClass {
public private(set) var _name: String = "Hi"
public var name: String {
get {
return self._name
}
set {
self._name = newValue
}
}
public init() {

}

}

public class MyDerivedClass:MyBaseClass {
override public var name: String {
get {
return "Derived - \(super.name)"
}
set {
super._name = newValue
}
}
public override init() {

}
}

Here the setter can be read directly with MyDerivedClass()._name but it cannot be altered, e.g. this MyDerivedClass()._name = "Fred" would raise an error but MyDerivedClass().name = "Fred" would be OK.

Is there a way to override property setters and getters in Swift?

While researching this question, I actually happened upon my answer, but still figured it should be put here for anyone else with the same question.


Yes, according to the Swift Programming Language documentation's page on Properties, getters and setters can be made in a similar way to how C# tackles the problem:

struct MyParentType {
var myPlainVar: MyType = MyType()

var myCustomGetterVar: MyType {
// do more stuff
return self.myCustomGetterVar
}

var myCustomGetterSetterVar: MyType {
get {
// do more stuff
return self.myCustomGetterSetterVar
}
set {
// do stuff
self.myCustomGetterSetterVar = newValue
// do more stuff
}
}

var myCustomFancyVar: MyType? {
willSet {
// do stuff with newValue
}
// actual setting is done automatically, so no set{}
didSet {
// do more stuff with oldValue
}
}
}

Note that variables with didSet must be initialized or optional, and cannot have a get, since properties which are computed with get can easily implement their own didSet and willSet if they so choose.

Swift override all setters and getters of a subclass

There is one hack to kind of attain what the poster is looking for, however possibly not advisable... Anyway; you can can create your own assignment operators that does whatever you want to do in realm prior to assigning the values

class MyType {        
var myInt : Int = 0
var myString : String = ""

init(int: Int, string: String) {
myInt = int
myString = string
}
}

infix operator === {}
func ===<T>(lhs: T, rhs: T) -> T? {
Realm() // replace with whatever Realm()-specific stuff you want to do
return rhs
}

protocol MyAddableTypes {
func + (lhs: Self, rhs: Self) -> Self
}

extension String : MyAddableTypes {}
extension Int : MyAddableTypes {}

infix operator +== {} // ... -== similarily
func +==<T: MyAddableTypes>(lhs: T, rhs: T) -> T? {
Realm() // replace with whatever Realm()-specific stuff you want to do
return lhs+rhs
}

func Realm() {
// ...
print("Called realm")
}

var a = MyType(int: 1, string: "foo")
a.myString === "bar" // calls Realm(). After operation: a.myString = "bar"
a.myInt +== 1 // calls Realm(). After operation: a.myInt = 2

I thought I'd also mention that if you only want to do "Realm stuff" when a value is set (from your example: prior to setting a value, specifically), then the willSet method, used with stored properties, doesn't need to look so messy (nested closures), and personally, I would prefer this method

func Realm() {
print("Called realm")
}

class MyType {

// This isn't so messy, is it?
var myInt : Int = 0 { willSet { priorToSetValue(newValue) } }
var myString : String = "" { willSet { priorToSetValue(newValue) } }
var myDouble : Double = 0.0 { willSet { priorToSetValue(newValue) } }

private func priorToSetValue<T> (myVar: T) {
// replace with whatever Realm()-specific stuff you want to do,
// possibly including doing something with your new value
Realm()
}

init(int: Int, double: Double, string: String) {
myInt = int
myDouble = double
myString = string
}
}

var a = MyType(int: 1, double: 1.0, string: "foo")

a.myString = "bar"
print(a.myString) // calls Realm(). After operation: a.myString = "bar"
a.myInt += 1 // calls Realm(). After operation: a.myInt = 2

Override a setter in swift

By writing this...

set(suit) {
self.suit = suit
}

... you introduce an infinite loop, because you call the setter from within the setter.

If your property isn't computed, you should take advantage of willSet and didSet notifiers to perform any additional work before/after the property changes.

By the way, you should remove te getter as well. It will cause another infinite loop when accessing the 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 }
}

How can I override a setter from a SuperClass in Swift with Xcode 6.3 Beta2?

If all we want it do some extra functionality after the selected property as been set, we can simply override to add a property observer:

override var selected: Bool {
didSet {
if self.selected {
// do something
}
}
}

Updated Answer for Swift 5:

override var isSelected: Bool {
didSet {
if self.isSelected {
// do something
}
}
}

If we care about what the previous value of selected was, we can access it via oldValue:

didSet {
if self.selected == oldValue {
return
}
// do something
}

Updated Answer for Swift 5:

didSet {
if self.isSelected == oldValue {
return
}
// do something
}

And don't forget, we can use willSet as well if we need to do something just before the value is changed.


I was curious what would happen when we've got a large hierarchy of classes each adding their own stuff in willSet and didSet property observers, so I created the following test:

class ClassA {
var _foo: Int = 0
var foo: Int {
set(newValue) {
println("Class A setting foo")
self._foo = newValue
}
get {
return self._foo
}
}
}

class ClassB: ClassA {
override var foo: Int {
willSet {
println("Class B will set foo")
}
didSet {
println("Class B did set foo")
}
}
}

class ClassC: ClassB {
override var foo: Int {
willSet {
println("Class C will set foo")
}
didSet {
println("Class C did set foo")
}
}
}

Now, if we create an object of ClassC and set its foo property:

var c: ClassC = ClassC()  
c.foo = 42

We get the following output:

Class C will set foo
Class B will set foo
Class A setting foo
Class B did set foo
Class C did set foo

So, it's important to note a few things from this...

  • A child class's willSet is called before its parent's willSet.
  • A child class's didSet is called after its parent's didSet.
  • Creating an override for the sake of adding property observers does not replace any property observers in the parent classes.

The first two points make a bit of sense. And actually, this makes property observers much more appealing. Effectively, Swift forces our hand into going up and down the heirarchy in the appropriate manner and nicely splits it out into two separate methods. Swift also prevents us (I believe) from overriding a parent class's property, but also still lets us observe changes to that property--this is much better than Objective-C's approach.

But the third point is probably the most important. Be careful--you can easily get bogged down in a massive heirarchy of didSet and willSet code that's slowing down what should be a pretty quick process: setting a property's value.



Related Topics



Leave a reply



Submit