Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Why does a variable with a setter must also have a getter?

Quick question: why?


Context:

Am using Swinject dependency injection to add small-s singleton model classes to my views thus:

defaultContainer.registerForStoryboard( MyViewController.self )
{
    responder, viewController in
    viewController.proxy = responder.resolve( MyProxy.self )!
}

I want to guard against the risk of accidentally overwriting this singleton instance by using a computed property thus:

private var _proxy: MyProxy!

var proxy: MyProxy 
{ 
    set { if _proxy == nil { _proxy = newValue } } 
}

But. I can't. Because I must also declare a getter.

Frustrating!

For now I am using the following hack. But really...

var proxy: MyProxy?
{
    get { return nil }
    set { if _proxy == nil && newValue != nil { _proxy = newValue } }
}
like image 441
Joseph Beuys' Mum Avatar asked Jan 08 '16 12:01

Joseph Beuys' Mum


People also ask

Can we use getters without setters?

Depends on if the value you are talking about is something you want to let other classes modify - in some cases the answer is yes, in some it is no. If the answer is no then there is no reason to add a setter method and in fact it might harm things.

What is setter and getter in Swift?

To create computed properties, Swift offers you a getter and (an optional) setter method to work with. A getter method is used to perform a computation when requested. A setter method is an optional method. It can be used to modify a related property.

What is willSet and didSet in Swift?

willSet is called before the data is actually changed and it has a default constant newValue which shows the value that is going to be set. didSet is called right after the data is stored and it has a default constant oldValue which shows the previous value that is overwritten.

What is the difference between computed property and lazy property?

But what is the difference between a lazy variable and a computed property? A lazy variable is a stored property whose initialization is delayed. The initial value is computed only one time. A computed property is a value that is not stored anywhere.

Can a swift property have an instance variable?

A Swift property does not have a corresponding instance variable, and the backing store for a property is not accessed directly. This approach avoids confusion about how the value is accessed in different contexts and simplifies the property’s declaration into a single, definitive statement.

How do you define a static variable in Swift?

In C and Objective-C, you define static constants and variables associated with a type as global static variables. In Swift, however, type properties are written as part of the type’s definition, within the type’s outer curly braces, and each type property is explicitly scoped to the type it supports.

What is the difference between a getter and a setter?

The getter is used to read the value, and the setter is used to write the value. The setter clause is optional, and when only a getter is needed, you can omit both clauses and simply return the requested value directly. That said, if you provide a setter clause, you must also provide a getter clause.

Do initializers inherit from superclass Swift?

Although properties and methods declared in the superclass are inherited by the current class, designated initializers declared in the superclass are only inherited when the subclass meets the conditions described in Automatic Initializer Inheritance. Swift classes don’t inherit from a universal base class.


2 Answers

Quick answer for the quick question "Why?":

Once you add a get (or set) to a property, it turns into a Computed Property. Computed properties may be read-only, thus requiring only a getter, but they may not be write-only. So if you add a setter to a property, you turn it into a computer property hence you must add a getter as well.

EDIT: Following comments, Why can Computed Properties be read-only, but not write-only?

I can't find official documentation to back this up, so the following explanation is solely based on my logical point of view:

Computed Properties don't store actual data on their variables; instead they compute data to display.

var a:Int = 5 // Stored property
var timesTen:Int { // Computed Property
    get {
        return a * 10
    }
}

From the example above: a is a Stored property. Therefore, its get method automatically returns a's value. Now think of timesTen: What's its value? Since it doesn't store any value in timesTen variable, you must tell the computed property how and where to read its "value" from. So that's why you can't use a computed property for writing-only purposes.


Workaround / How to avoid it

For simple properties you may use didSet or willSet to achieve the desired purpose:

var proxy: MyProxy?
{
    willSet { if _proxy == nil && newValue != nil { _proxy = newValue } }
}

If you are using Swift 2.x, you may use the guard statement to for a cleaner code:

var proxy: MyProxy?
{
    willSet { 
         guard (_proxy == nil && newValue != nil) else { return }
         _proxy = newValue
    }
}

Another observation

If _proxy is not required to be private you may remove it completely, using only one variable/property instead of two:

var proxy: MyProxy!
{
    willSet { 
         guard (proxy == nil && newValue != nil) else { return }
         // By not preventing `willSet` to continue, `newValue` will automatically assigned to `proxy`
    }
}
like image 95
mathielo Avatar answered Nov 16 '22 01:11

mathielo


Why cant you just return _proxy in your getter? What does that matter? If you are afraid of exposing your getter you can set your getter to private, thus exposing it only in the containing class.

private(get) var proxy: MyProxy? {
  set { if _proxy == nil && newValue != nil { _proxy = newValue } }
}
like image 44
tskulbru Avatar answered Nov 16 '22 02:11

tskulbru