Swift correct use of getters and setters - ios

Can someone please help me understand the correct use of getters and setters in swift. I get the impression its not the same as say Java.
Is this the correct usage in Swift to store and access a class variable?
class Person {
private var name: String
init(name: String) {
self.name = name
}
func setName(name: String) {
self.name = name
}
func getName() -> String {
return name
}
}

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

If you assign to self., you will just be calling this method again. Also, there is no "get" like the old Java bean property pattern. Finally, if you actually need to use methods for property computation or actions after setting, they can be built right into the variable definition.
class Person
{
private var name: String;
init( name: String )
{
self.name = name
}
}
should be sufficient for your simple case, although you can also
private var name: String {
didSet {
// updating something after setting
}
};

This is how setter and getter works as in Java:
class Person {
private var _name
private var _age
// .... other properties
var name: String {
get {
return _name
}
set {
_name = newValue
}
}
var age: String {
get {
return _age
}
set {
_age = newValue
}
}
}

Related

Create a protocol with default implementation that gives name/title/self of Enum as string

I am trying to create a protocol with default implementation that returns enum itself as string. But not able to find the correct syntax.
This is the code I tried so far, but it is not taking default implementation
protocol TestSelf {
static var desc: String { get set }
}
extension TestSelf {
get {
static var desc: String {
String(describing: self)
}
set {
print(\(new value))
}
}
enum Test: TestSelf { }
Access
print(Test.desc)
Asking me to implement the desc in the enum saying 'static var' declaration requires an initializer expression or an explicitly stated getter. I don't want to initialize it again. It should work with Default Implementation.
Ask:
At all places I don't need to write
String(describing: Test.self)
instead I can directly use
Test.desc
I think you want something like this (this is playground code):
protocol SelfDescriber {
static var descriptionOfSelf: String {get}
}
extension SelfDescriber {
static var descriptionOfSelf: String {
String(describing: Self.self)
}
}
enum Test: SelfDescriber {}
print(Test.descriptionOfSelf) // "Test"

How do I use #keyPath() in Swift 4?

I think part of my problem is because Swift 4 has changed the way things like #objc work.
There are a lot of tutorials floating around, with a lot of different values, and I can't pick my way between what used to work in what version enough to figure out how to make it work in this version.
let delegate = UIApplication.shared.delegate as! AppDelegate
delegate.addObserver(self, forKeyPath: #keyPath(AppDelegate.session), options: [], context: nil)
// Warning: Argument of #keyPath refers to non-'#objc' property 'session'
Adding #objc to the var declaration just informs me that APISession can't be referenced in Objective-C. That seems to lead down the path towards requiring me to expose every class / variable I want to use this tool with to Obj-C, and that just seems backwards -- this is a newer feature, as I understand it, and it's just odd that Apple wouldn't make it work natively in Swift. Which, to me, suggests I'm misunderstanding or misapplying something, somewhere, somehow.
According to the docs:
In Objective-C, a key is a string that identifies a specific property of an object. A key path is a string of dot-separated keys that specifies a sequence of object properties to traverse.
Significantly, the discussion of #keyPath is found in a section titled "Interacting with Objective-C APIs". KVO and KVC are Objective-C features.
All the examples in the docs show Swift classes which inherit from NSObject.
Finally, when you type #keyPath in Xcode, the autocomplete tells you it is expecting an #objc property sequence.
Expressions entered using #keyPath will be checked by the compiler (good!), but this doesn't remove the dependency on Objective-C.
This is how I've applied #keyPath() in real project of mine. I used it to save & retrieve data to and from UserDefaults and I called that feature as AppSettings. Here's how things are going on...
1). I have a protocol called AppSettingsConfigurable It contains a couple of stuffs which are the setting features of my app...
//: AppSetting Protocol
#objc protocol AppSettingsConfigurable {
static var rememberMeEnabled : Bool { get set }
static var notificationEnabled : Bool { get set }
static var biometricEnabled : Bool { get set }
static var uiColor: UIColor? { get set }
}
2). I have class and I named it AppSettings. This is where saving and retrieving operation take place with UserDefaults
//: AppSettings
class AppSettings: NSObject {
fileprivate static func updateDefaults(for key: String, value: Any) {
// Save value into UserDefaults
UserDefaults.standard.set(value, forKey: key)
}
fileprivate static func value<T>(for key:String) -> T? {
// Get value from UserDefaults
return UserDefaults.standard.value(forKey: key) as? T
}
}
3). Here's where BIG things are happened. Conform AppSettings class to our protocol and lets implement the stuffs using #keyPath().
//: Conform to protocol
extension AppSettings:AppSettingsConfigurable{
/** get & return remember me state */
static var rememberMeEnabled: Bool {
get { return AppSettings.value(for: #keyPath(rememberMeEnabled)) ?? false }
set { AppSettings.updateDefaults(for: #keyPath(rememberMeEnabled), value: newValue) }
}
/** get & return notification state */
static var notificationEnabled: Bool {
get { return AppSettings.value(for: #keyPath(notificationEnabled)) ?? true }
set { AppSettings.updateDefaults(for: #keyPath(notificationEnabled), value: newValue) }
}
/** get & return biometric state */
static var biometricEnabled: Bool {
get { return AppSettings.value(for: #keyPath(biometricEnabled)) ?? false}
set { AppSettings.updateDefaults(for: #keyPath(biometricEnabled), value: newValue) }
}
/** get & return biometric state */
static var uiColor: UIColor? {
get { return AppSettings.value(for: #keyPath(uiColor)) }
set { AppSettings.updateDefaults(for: #keyPath(uiColor), value: newValue!) }
}
}
PS: Noticed something different with uiColor from the rest? Nothing wrong with it as it's optional and it's allowed to accept the nil
Usage:
//: Saving...
AppSettings.biometricEnabled = true
//: Retrieving...
let biometricState = AppSettings.biometricEnabled // true

Can we add static stored variable via extension, in Swift?

In the book Swift Programming Language 3.0, it says that we can't use extension to add stored property.
I tried it out with instance stored variable and Xcode displayed an error as expected.
But when I tried with static stored variable, everything compiled just fine.
Is there something that I'm missing or doing wrong?
class MyClass {}
extension MyClass {
static var one: Int {
return 1
}
static var two = 2 //compiled just fine
}
let myVariable = MyClass()
MyClass.two
You can't put stored properties in instances of an extension, you can cheat a little though and get the same effect with Objective-C associated objects. Give the following code a try:
private var associationKey: UInt8 = 0
var validationTypes: ValidationTypes {
get {
return objc_getAssociatedObject(self, &associationKey) as? ValidationTypes ?? []
}
set(newValue) {
objc_setAssociatedObject(self, &associationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
Obviously replacing ValidationTypes as appropriate.

Get and Set on each field like in Java

I tried to look all over the internet to find out what is the best practice of encapsulating data in Swift. I only found some information about get and set method of an instance variables. They used to work pretty much like C#'s did set, but now they work as computed properties and one can't set them in their setters and can't get them in their getters. The question is: do people still create getter and setter for each property in the class in swift ? Like:
private var age: Int = 0
public func getAge() -> Int {
return age
}
public func setAge(newAge: Int) {
self.age = newAge
}
If not, then what is considered to be the best practice ?
Thank You.
I use this type of syntax with computed vars to expose the getter and setters when the internals are more complex than what is shown here:
internal var _age: Int = 0
var age: Int {
get {
return _age
}
set {
_age = newValue
}
}
Plenty of syntaxic sugar to make it easy to hide complexity in some expressions.
But most of the time I am happy with just exposing the plain var and be done with it:
var age: Int = 0
The advantage of this is that both ways are easily swappable when you want to do more work inside the getters and setters.
I don't think you need to create getX/setX methods, because you can always change from a stored to a computed property without having to change the source code of the clients. Just like you could change the bodies of your getAge & setAge methods, so I do not see what is gained in swift by not letting clients use age.
if you want to access the property (Age) itself, you can always get/set it directly.
let theGirlsAge = Age; // Get
Age = theBoysAge; // Set
Getters and Setters are used for define relationship between the property and other properties.
public func getAge() -> Int {
return theBoysAge
}
public func setAge(newAge: Int) {
theGirlsAge = newAge
}

A static field inherited from the base class or protocol - how?

I want to be able to have the classes which have a static property (field) which is either inherited from the base class or "mixed" from a protocol. And every class should have it's own implementation of that property. Is it possible? Preferably, it to be immutable.
class C1 {
static let stProperty = "my prorepty1"
}
class C2 {
static let stProperty = "my prorepty2"
}
It's possible, but it's really hard to make this useful in Swift. How do you plan to refer to this property? Let's start with a super-simple implementation:
protocol SomeProtocol {
static var prop: String { get }
}
class C1: SomeProtocol {
static let prop = "This is One"
}
Great. So now I want a function that uses this:
func useProp(x: SomeProtocol) -> String {
return x.prop
// 'SomeProtocol' does not have a member named 'prop'
}
That doesn't work. x is an instance, but I want the type.
// Accessing members of protocol type value 'SomeProtocol.Type' is unimplemented
func useProp(x: SomeProtocol.Type) -> String {
return x.prop
}
This is probably how it will work some day given the word "unimplemented." But it doesn't work today.
func useProp(x: SomeProtocol) -> String {
// Accessing members of protocol type value 'SomeProtocol.Type' is unimplemented
return x.dynamicType.prop
}
Same thing.
Today, you really have to hang this on the object itself and not use static or class:
protocol SomeProtocol {
var prop: String { get }
}
class C1: SomeProtocol {
let prop = "This is One"
}
func useProp(x: SomeProtocol) -> String {
return x.prop
}
That's not so terrible in many cases, since the value for the class is probably also the value for any given instance of the class. And it's really all we can do today.
Of course your problem might be that you don't have an instance yet and you need this information to build an instance. That's really hard today and you should probably rethink your design. You'll generally have to use some other pattern like a Builder. See Generic Types Collection for more.
Now you also said:
or "mixed" from a protocol
I wouldn't say "mixed" here. If you really mean this like a Ruby "mixin", there is no such thing in Swift today. Swift folks often refer to this feature as "default implementation," and it's not currently possible (though I do expect it to come eventually). The only thing you can do in the protocol is say that the implementor has to provide this method somehow. You can't provide it for them.
Sure you can do that with a protocol:
protocol SomeProtocol {
static var foo: String { get }
}
class One: SomeProtocol {
class var foo: String {
get {
return "This is One"
}
}
}
Btw I agree with Rob Napier below that this is a bit off a oddball feature. I do think there are probably use-cases for it, but I also think those can be better implemented with other language features
protocol P {
class var stProperty: String { get }
}
class C1 {
class var stProperty: String {
return = "my property1"
}
}
class C2 {
class var stProperty: String {
return = "my property2"
}
}
Usage:
C2.prop //"my property2"
If you try:
C2.prop = "new value" //"cannot assign to the result of this expression"

Resources