Variable with getter/setter cannot have initial value, on overridden stored property - swift

When creating a stored property with Observing Accessors, I can specify a default value. However, when overriding a stored property and its Accessors I cannot specify a default value.
Variable with getter/setter cannot have initial value.
Which seems very strange, as this is NOT a computed property with a getter/setter, but a set of Observing Accessors on a stored property!
class FirstViewController: UIViewController {
internal var test: Float = 32.0 {
willSet {
}
didSet {
}
}
The first view controller compiles fine, with a stored property initialized to 32.0
class SecondViewController: FirstViewController {
override var test: Float = 64.0 {
willSet {
}
didSet {
}
}
The second view controller does not compile, as the 'computed property' is being given an initial value

In swift you are able to override properties only with computed properties (which are not able to have default values) with same type. In your case, if you wish override test property in SecondViewController you need write something like this:
override var test: Float {
get {
return super.test
}
set {
super.test = newValue
}
}
And there is no way to override didSet/willSet observers directly; you may do this by write other methods invoked in observers and just override them:
FirstViewController:
internal var test: Float = 32.0 {
willSet {
test_WillSet(newValue)
}
didSet {
test_DidSet(oldValue)
}
}
func test_WillSet(newValue: Float) {}
func test_DidSet(oldValue: Float) {}
SecondViewController:
override func test_WillSet(newValue: Float) {
super.test_WillSet(newValue)
}
override func test_DidSet(oldValue: Float) {
super.test_DidSet(oldValue)
}

I know that this has been asked a long time ago but I came up with a slightly different solution and it works exactly as you wanted. You have a property in the first ViewController then in the inherited one you override it and have observers set on it in the form of didSet.
So in the FirstViewController you have a property like in the example below:
var myNumber: Double = 20.00
Then in the SecondViewController which inherits from FirstViewController you override it as follows:
override var myNumber: Double {
didSet {
//Here you can update UI or whatever you want to do once the property changes
//Print its value
print("Value of myNumber is : \(myNumber)")
}
I hope this will help someone with the above issue as this is a nice and easy way to solve the problem mentioned above.

Related

Why is conformance to an object required for read/write extension properties to work on let variables?

I know the title may be confusing, but this should clear it up.
Say I define the following extension on UIView...
extension UIView {
var isVisible:Bool {
get { return !isHidden }
set { isHidden = !newValue }
}
}
In code, I can do this without issue...
let myView = UIView()
myView.isVisible = true
But if I try pulling out the extension into a reusable protocol (so I can apply it to both UIView and NSView without having to duplicate the code) like so...
public protocol ExtendedView {
var isHidden: Bool { get set }
}
public extension ExtendedView {
var isVisible: Bool {
get { return !isHidden }
set { isHidden = !newValue }
}
}
extension UIView: ExtendedView {}
extension NSView: ExtendedView {}
...then while I can read it like so...
let myView = UIView()
if myView.isVisible {
....
}
...This line will not compile!
myView.isVisible = true
It gives the following compile-time error...
cannot assign to property: 'myView' is a 'let' constant
To fix it, I have to either change the variable to a var (not what I want to do), or conform the protocol to AnyObject, like so...
public protocol ExtendedView : AnyObject {
var isHidden: Bool { get set }
}
My question is why? I mean the compiler knows at compile time the type of item the extension is being applied to so why does the protocol have to conform to AnyObject? (Yes, I do acknowledge that extending UIView (or NSView) implies an object, but still... doesn't the call site know it's not a value type?)
doesn't the call site know it's not a value type?
That doesn't matter. Protocol members allows for mutation of self. For example, if you don't constrain the protocol to AnyObject, this will always compile:
set { self = newValue as? Self ?? self }
I.e. protocols provide the only way to be able to change a reference internally. Even though you're not actually doing that in your code, the possibility of the reference mutation is there.
And even if you don't actually cause any mutation, property observers are still going to be triggered by mutating protocol members.
var myView = UIView() {
didSet {
print("Still the same \(myView) after `isVisible` changes, but that's not provable at compile-time.")
}
}
Your particular issue is due to the default of set accessors.
{ get set }
is shorthand for
{ nonmutating get mutating set }
If you change the get to be mutating as well, you'll run into the same issue.
public protocol ExtendedView {
var isHidden: Bool { get }
}
public extension ExtendedView {
var isVisible: Bool {
mutating get { !isHidden }
}
}
// Cannot use mutating getter on immutable value: 'myView' is a 'let' constant
let myView = UIView()
myView.isVisible
I have to either change the variable to a var (not what I want to do), or conform the protocol to AnyObject
Although it's not apparent why you shouldn't be constraining to AnyObject or something more restrictive, you can just use
var isHidden: Bool { get nonmutating set }
That's enough to be able to make myView a constant. However, it's more accurate to mark isVisible completely nonmutating as well, which will stop property observers triggering.
nonmutating set { isHidden = !newValue }
Ultimately, constraining as much as possible is going to make working with any protocol easier. Especially when it allows you to enforce reference semantics.
public enum OldUIFramework { }
#if os(macOS)
import AppKit
public extension OldUIFramework {
typealias View = NSView
}
#else
import UIKit
public extension OldUIFramework {
typealias View = UIView
}
#endif
extension OldUIFramework.View: ExtendedView { }
public protocol ExtendedView: OldUIFramework.View {
var isHidden: Bool { get set }
}
If you really need ExtendedView to apply to value types sometimes, then make a constrained extension for the other cases, calling the value type code.
any should be some, here, but the compiler has bugs that make it not work right now.
public extension ExtendedView where Self: OldUIFramework.View {
var isVisible: Bool {
get {
let `self`: any ExtendedView = self
return `self`.isVisible
}
nonmutating set {
var `self`: any ExtendedView = self
`self`.isVisible = newValue
}
}
}
I mean the compiler knows at compile time the type of item the extension is being applied to
I know, it looks like the compiler knows that, especially when you write the lines next to each other like that:
let myView = UIView()
myView.isVisible = true
But command-click on isVisible in that code, and where do you end up? In the protocol ExtendedView. In other words, isVisible is not ultimately a property declared by UIView; it's a property declared by ExtendedView.
And nothing about the protocol itself guarantees that the adopting object will be a reference type — unless you guarantee it by qualifying the protocol, either directly or in an extension of the protocol, by saying what kind of object can adopt it.
I would just like to add that the situation you've posited is extremely specialized: the issue only arises in exactly the situation you've created, where a protocol extension injects a computed property implementation into its adopters. That's not a common thing to do.

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.

Swift variable observers not called before super.init called

Okay so I was reading up on how willSet/didSet are used in swift and I came across a note on apples swift docs that just doesn't make any sense to me and I hope someone can explain. Here's the note:
The willSet and didSet observers of superclass properties are called
when a property is set in a subclass initializer, after the superclass
initializer has been called. They are not called while a class is
setting its own properties, before the superclass initializer has been
called.
From: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
What confuses me is that they point out that the observers on superclass A properties in a subclass B aren't called before the super.init call by B to A.
class A {
var p: Bool
init() {
p = false
}
}
class B: A {
override var p: Bool {
didSet {
print("didSet p")
}
}
override init() {
p = true // Compiler error
super.init()
}
}
However the property is never even accessible in that time from either A nor B, so who's gonna call the observers anyway? Attempting to read/write the property will even result in a compiler error so it's never even possible to do it by mistake in Swift. Am I missing something or is this just a misleading note that points out the wrong thing?
They are talking about following scenario:
class A {
var p: Bool {
didSet {
print(">>> didSet p to \(p)")
}
}
init() {
p = false // here didSet won't be called
}
}
class B: A {
override init() {
// here you could set B's properties, but not those inherited, only after super.init()
super.init()
p = true // here didSet will be called
}
}
B()
It will print following:
>>> didSet p to true
While to you it might seems natural, the documentation has to explicitly document this behavior.

Accessing a computed property outside of a method

I have a generic function inside a class in which a computed property is declared:
class CalculatorBrain {
var internalProgram = [AnyObject]()
var accumulator: Double = 0.0
var variableValues: Dictionary<String,Double> = [:]
func setOperand<T> (operand: T) {
if operand is Double {
accumulator = operand as! Double
internalProgram.append(operand as AnyObject)
}
else if variableName == operand as? String {
var dictionaryValue: Double? {
get {
return variableValues[variableName!]
}
set {
accumulator = newValue!
internalProgram.append(newValue! as AnyObject)
}
}
}
}
I want to set dictionaryValue to the value shown in the display from the view controller:
private var brain = CalculatorBrain()
#IBAction func setVariableValue(_ sender: UIButton) {
brain.dictionaryValue = displayValue
}
Obviously I can't, because dictionaryValue is locally defined and "Value of type CalculatorBrain has no memeber dictionaryValue" Now the question is, how can I make a computed property global, and make changes to it from inside a class method? Or, how can I access a computed property defined inside a class method from outside the function?
The problem is dictionaryValue is not a computed property of your class, it is just a variable declared in the setOperand function, so it is not accessible from outside the function.
You should declare it as a stored property of your class and change it when setOperand is called.

swift setter causing exc_bad_access

I have a simple class below
import Foundation
public class UsefulClass: NSObject{
var test:NSNumber{
get{return self.test}
set{
println(newValue)
self.test = newValue
}
}
override init() {
super.init()
self.test = 5;
}
}
and I'm initializing it here
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var testClass = UsefulClass()
}
}
But it results in xcode printing out 200 5s and then crashing due to EXC_BAD_ACCESS code = 2. Why does this happen?
#vadian has provided a solution in his answer, which should fix your problem. Let me just explain what's happening.
You have created a computed property, i.e. a property which is not backed by a variable, instead both the getter and the setter do some processing, usually on another stored property, in order to respectively return a value and set a new value.
This is your computed property:
var test: NSNumber {
get { return self.test }
set {
println(newValue)
self.test = newValue
}
}
Look at the getter implementation:
return self.test
What does it do? It reads the test property of the current instance, and returns it. Which is the test property? It's this one:
var test: NSNumber {
get { return self.test }
set {
println(newValue)
self.test = newValue
}
}
Yes, it's the same property. What your getter does is to recursively and indefinitely calling itself, until a crash happen at runtime.
The same rule applies to the setter:
self.test = newValue
it keeps invoking itself, until the app crashes.
Swift variables are synthesized properties by default.
In the most cases this is sufficient (it's recommended to prefer Swift types)
var test: Int
override init() {
super.init()
test = 5
}
If you need to do something after a variable is set, use
var test: Int {
didSet{
println("\(oldValue) - \(newValue)")
}
}
your code sets the variable permanently by calling the setter which calls the setter which …
It's an infinite loop; your setter is recursively calling itself.
var test: NSNumber {
set {
test = newValue
}
}
This compiles fine, and an Objective-C programmer might expect no loop due to instead setting a "backing ivar" such as _test rather than re-calling the setter method.
But property-backing instance variable _ivars do not exist in Swift for computed properties unless you create them yourself.