Swift Protocols: Difference between { get } and { get set } with concrete examples?
protocol
— is a requirement of some minimal interface of the type implementing it.
var name: Type { get }
requires type to have property with at least agetter
(accessible from outside of the type, notprivate
), i.e. outside code should be able to read value of the property. In the implementing type it could belet name: Type
,var name: Type
,private(set) var name: Type
,fileprivate(set) var name: Type
, etc.var name: Type { get set }
requires type to have property with both accessiblegetter
andsetter
, i.e. outside code should be able to read and write to the property. Here onlyvar name: Type
would be allowed.
If protocol
requires for getter
but you also provide a setter
— it's not against protocol
requirements.
But if protocol
requires for both getter
and setter
— you must provide both, and not having any of them won't be valid implementation.
Your Person
class defined both properties as var
(with accessible getter
and setter
) therefore you can change them both. But PersonProtocol
haven't required ability to set firstName
.
And as @JoakimDanielson shows, if you will use just interface required by protocol
you won't be to change the firstName
value.
Difference between 'get' and 'get set' in Swift
This protocols requires conforming types (classes, structs or enums) to have two properties:
numberOfWheels
, which must provide at least a getter. This means it's either alet
property, avar
property, or a computed property with at least a getter (the setter is optional).wheelSize
, which must provide a getter and a setter. This means it has to be either avar
property, or a computer property with both a getter and a setter.
Swift correct use of getters and setters
Swift provides a much more structured approach to getters
and setters
than Java.
You can, but you should not, write setters and getters as you did in your code.
Instead (if you are using stored properties
) just declare the property with a visibility non private
(e.g. internal
in my example). This way callers outside of your class will be able to see the property and change it.
class Person {
var name: String {
willSet(newValue) {
print("\(self.name) is going to be renamed as \(newValue)")
}
didSet(oldValue) {
print("\(oldValue) has been renamed as \(self.name)")
}
}
init(name: String) {
self.name = name
}
}
Ok but in java getter and setters do allow me to add custom logic to be executed before or after the value is changed.
Right! In Swift, you can do this using the willSet
and didSet
observers.
willSet(newValue)
You write here the code you want to run before a new value is written in the property.
Here you can access the current value (that is going to be overwritten) with self.name
while the new value is available with newValue
.
didSet(oldValue)
You write here the code you want to run after a new value is written in the property.
Here you can access the old value (that has been overwritten) with oldValue
while the new value is available in self.name
.
Both willSet
and didSet
are optional [I am not talking about Optional Type! I mean you are not forced to write them :)].
If you don't need to run some code just before
or after
the property has been changed, just omit them.
Example
let aVerySmartPerson = Person(name: "Walter White")
aVerySmartPerson.name = "Heisenberg"
// > Walter White is going to be renamed as Heisenberg
// > Walter White has been renamed as Heisenberg
Property getters and setters
Setters and Getters apply to computed properties
; such properties do not have storage in the instance - the value from the getter is meant to be computed from other instance properties. In your case, there is no x
to be assigned.
Explicitly: "How can I do this without explicit backing ivars". You can't - you'll need something to backup the computed property. Try this:
class Point {
private var _x: Int = 0 // _x -> backingX
var x: Int {
set { _x = 2 * newValue }
get { return _x / 2 }
}
}
Specifically, in the Swift REPL:
15> var pt = Point()
pt: Point = {
_x = 0
}
16> pt.x = 10
17> pt
$R3: Point = {
_x = 20
}
18> pt.x
$R4: Int = 10
How to call a method once two variables have been set
You could make your properties optional
and check they both have values set before calling your function
.
var varA: String? = nil {
didSet {
if varA != nil && varB != nil {
myFunc()
}
}
}
var varB: String? = nil {
didSet {
if varA != nil && varB != nil {
myFunc()
}
}
}
Or you can call your function
on each didSet
and use a guard
condition at the start of your function
to check that both of your properties have values, or bail out:
var varA: String? = nil {
didSet {
myFunc()
}
}
var varB: String? = nil {
didSet {
myFunc()
}
}
func myFunc() {
guard varA != nil && varB != nil else { return }
// your code
}
Related Topics
Make a Uibarbuttonitem Disappear Using Swift iOS
How to Create a String from Utf8 in Swift
Rxswift Merge Different Kind of Observables
Why Can't We Use Protocol 'Encodable' as a Type in the Func
How Is Optional Binding Used in Swift
How to Get Random Element from a Set in Swift
How to Use Sf Rounded Font in Swiftui
Why Unsaferawpointer Shows Different Result When Function Signatures Differs in Swift
How to Bind Different Associated Values in a Swift Enum to the Same Var
"Generic Parameter Could Not Be Inferred" in Swiftui Uiviewrepresentable
How to Test Whether Generic Variable Is of Type Anyobject
Extending Collection with a Recursive Property/Method That Depends on the Element Type
Swiftui - Wait Until Firestore Getdocuments() Is Finished Before Moving On
Self' Captured by a Closure Before All Members Were Initialized
Can an Enum Contain Another Enum Values in Swift
Swiftyjson - Call Can Throw, But It Is Marked with 'Try' and the Error Is Not Handled