Idiomatic Swift: Getter & Setter boiler plate coding - swift

While migrating some projects from Objective-C to Swift, I find myself replacing the #property/#synthesize syntax with a lot more boiler-plate code.
For example, for every #property I am implementing, I currently use this pattern:
var foo: Foo? {
get {
return _foo
}
set {
_foo = newValue
}
}
var _foo: Foo?
It ends up adding quickly. Compare this with the 2 lines in Objective-C. That doesn't feel right.
Should I just replace the code using _foo in my ported classes with foo and skip the abstraction of each public var behind an interface entirely?

As #Amit89 pointed out, you can simply use _foo.
In your example, adding foo does exactly nothing, as you simply recreated the get and set functionality already supplied with _foo.
If you want to control how _foo is set you can use property observers:
var _foo: Foo? {
didSet { }
willSet { }
}
If you want to control how _foo is retrieved, then it would indeed make sense to create a private version of _foo, and then accessing it via a computed property:
private var _foo: Foo?
var foo: Foo? {
get {
// return `_foo` in some special way
}
// add a setter if you want to set `_foo` in some special way
}

Related

How to pass a class object to a function but prevent mutation?

I can't see where in the Swift language is the facility to pass a class object to a function yet prevent that function from mutating the object by either calling functions that will implicitly mutate it or setting public variables. I'm gathering that this facility just does not exist, can anyone confirm?
That is to say, all objects are always mutable everywhere they can be seen.
This is extremely common throughout Cocoa. You create an immutable class and a mutable subclass. For examples, see AVComposition/AVMutableComposition, CBService/CBMutableService, CNContact/CNMutableContact.
In ObjC, this is common practice with collections as well (arrays, dictionaries, etc), but since those are value types in Swift, there's no need to use the classes (NSArray/NSMutableArray).
In Swift, rather than creating two classes, you create an immutable protocol and a class:
protocol Person: AnyObject {
var name: String { get }
var address: String { get }
}
class MutablePerson: Person {
var name: String = ""
var address: String = ""
}
Now, any function that accept Person will have an immutable object, and any function that accepts MutablePerson will be able to mutate it. This is a general pattern you can use to give different parts of your program access to different slices of the object's API. It's much more general and flexible than just const.
That said, this is not as common a pattern in Swift as it is in ObjC, since in most cases where this is useful, the type should be a struct anyway. But it is absolutely available if needed.
To your question about doing this with two classes, as in ObjC, it's possible, as long as you define both in the same file. It's just a bit tedious:
public class Person {
public fileprivate(set) var name: String = ""
public fileprivate(set) var address: String = ""
}
public class MutablePerson: Person {
public override var name: String {
get { super.name }
set { super.name = newValue }
}
public override var address: String {
get { super.address }
set { super.address = newValue }
}
}
It's possible a property wrapper could improve this, but I haven't been able to figure out how.
There's no way I can think of to allow usage of methods, but properties are no problem**. Just use an Immutable as a function parameter.
final class Class {
var property = true
}
var object = Immutable(Class())
object.property = false // Cannot assign to property: 'object' is immutable
/// An affordance for accessing the properties of an object
/// without the ability to mutate them.
#dynamicMemberLookup
public struct Immutable<Object: AnyObject> {
private let object: Object
}
// MARK: - public
public extension Immutable {
init(_ object: Object) {
self.object = object
}
subscript<Value>(dynamicMember keyPath: KeyPath<Object, Value>) -> Value {
object[keyPath: keyPath]
}
}
** The getters could be mutating, and they could return mutating closures. 😜 But that's an issue with the protocol approach as well. The best that we can do right now is a generally accurate hack.
What you are looking for are value types (such as structs). If you mutate any properties of a value type, you mutate the instance itself.
This means that when you pass a value type to a function, the function won't be able to mutate any of the properties of said value type.
On the other hand, classes are reference types, so mutating any of their properties doesn't mutate the class instance itself. Because of this, you cannot ban functions from modifying mutable properties of the class (unless you make them setter of said properties private).

Why does EnvironmentKey have a defaultValue property?

Here's the definition of EnvironmentKey:
public protocol EnvironmentKey {
associatedtype Value
static var defaultValue: Self.Value { get }
}
I'm confused as to the purpose of the defaultValue property. Or perhaps I'm confused about the intended use of #Environment. Let me explain...
Based on my testing, I know that the defaultValue is being accessed. If you, for example, define the defaultValue as a getter:
protocol MyInjector: EnvironmentKey {
static var defaultValue: Foo {
print("get default value!")
return Foo()
}
}
you'll find that SwiftUI frequently calls this property (because of this, in practice I define my defaultValue as static let).
When you add a breakpoint on the print statement and move down the stack it stops on the arrow below:
extension EnvironmentValues {
var myInjector: Foo {
get { self[MyInjector.self] } <-----------------
set { self[MyInjector.self] = newValue }
}
}
Further down in the stack is the line in my component's init where I access the following variable:
#Environment(\.myInjector) var foo: Foo
That is, it seems to access defaultValue any time a SwiftUI view with an #Environment variable is re-rendered.
I think that the internal implementation of #Environment needs to set this default value before it determines the actual value as a way to avoid making the variable declaration optional.
Finally, I also experimented with adding #Environment to an ObservableObject in the hopes that it would have access to the same "environment" as the SwiftUI view tree.
class Bar: ObservableObject {
#Environmenet(\.myInjector var foo: Foo
}
Unfortunately, that was not the case and the instance that Bar received was different than the instance I had injected via View.environment(\.myInjector, Foo()).
I also found, btw that I could use #Environment to inject a global singleton by using the shared variable pattern:
protocol MyInjector: EnvironmentKey {
static let defaultValue = Foo()
}
and the same instance was available to SwiftUI views and any other class, but I'm not sure how that could be useful in any way.
So what is the purpose of defaultValue? Is it simply, as I suspect, so that the internal implementation can assign an arbitrary value to the variable before the true value is determined, or is there something else going on?
IMO there are two reasons to have defaultValue in this pattern (and I assume this was the intention):
1) to specify corresponding value type
2) to make environment value always valid (in usage of #Environment)
In SwiftUI: Set Status Bar Color For a Specific View post you can find example of usage of this defaultValue feature to easily connect via environment different parts of application.

How to get class type statically in swift

As the question may seem duplicated, I point out first that this is not asking for how to get a class type from an instance ( answer is using type(of:) ).
protocol Owner {
static func name() -> String
}
extension Owner {
static func name() -> String {
return "\(self)"
}
}
class Foo {
var ownerName: String
init(with owner: Owner.Type) {
ownerName = owner.name()
}
}
class Bar: Owner {
var foo = Foo(with: Bar.self)
}
The code above simply works, but imagine that I want to rename the class Bar to BarBar then I need to change the initialization of foo to Foo(with: BarBar.self) manually. Is there any keyword that can be used instead of ClassName.self (e.g. Bar.self BarBar.self) to get the class type? Something like Self or Class (they don't compile actually)
My guess is that your whole question is a red herring and that type(of:self) is exactly what you want to say. In that case, the problem is merely that var foo is declared as an instance property. There are special rules for when you are allowed to say self while initializing an instance property (rightly, because self is exactly what does not yet exist during initialization). However, there are ways around that, as I have explained elsewhere; you can make this a computed instance property, or a lazy instance property, which is initialized by a function to be executed later, and then you are allowed to say type(of:self).

Why do we use the keyword "get" in Swift variables declaration?

This might sound dumb, but I can't figure out why programmers declare variables in Swift as follows:
class Person: NSObject {
var name: String { get }
}
Why is the keyword "get" used? Why is "set" missed? I thought we used them like this:
class Person: NSObject {
var name: String {
get {
// getter
}
set {
// setter
}
}
}
This might be a spam question, but I am interested in the theoretical definition of { get }
Sentences such as var name: String { get } are normally used in protocols not in classes. In a protocol it means that the implementation must have a variable of type String which should be at least read only (hence the get). Had the curly brackets bit been { get set } the variable would have been read write.
Actually as per Earl Grey answer, var name: String { get } will not compile inside a class.
The first code example strictly speaking doesn't make sense. You do not declare variables like this in a class.. (You do it in a protocol like this.)
The second is an example of computed property,though the getter and setter implementation is missing. (it seems to be implied at least so I won't object about validity of the code example.)

Why is it not possible to define property observers for computed propertys?

I am currently playing around with swift and there is one thing i don't understand.
Lets take a look at the following non compiling code snippet
class A {
var test : String {
get {
return "foo"
}
set {
self.test = newValue
}
willSet {
}
didSet {
}
}
}
For some reason the Compiler is complaining. So i am not able to implement all of them: get and set, didSet and willSet. I thought that observing computed properties is maybe not possible.
So i played around a little bit more and then i discovered that subclasses can override the property observers of computed propertys.
What the hack? This doesnt makes sense to me
import UIKit
class A {
var test : String {
get {
return "name"
}
set {
self.test = newValue
}
}
}
class B : A {
override var test : String {
willSet {
}
didSet {
}
}
}
Why i am not able to add property observers in the first code snippet but i am able to overwrite these observers inside a subclass?
In the first code, you don't need observers, because you already are writing the code that sets the property (set). Thus, if you want to do something before/after the property gets set, you can just do it right there in the setter handler (set):
class A {
var test : String {
get {
return "foo"
}
set {
// will set
self.test = newValue
// did set
}
}
}
Thus, by a kind of Occam's Razor principle, it would be redundant and unnecessary to have separate setter observers: you are the setter, so there is no need to observe yourself.
In your subclass override, on the other hand, where you didn't supply a whole new computed property, the setting is going on behind your back, as it were, so as compensation you are allowed to inject set observation into the process.
“You don’t need to define property observers for non-overridden computed properties, because you can observe and respond to changes to their value in the computed property’s setter.”
Excerpt From: Apple Inc. “The Swift Programming Language (Swift 2.1).” iBooks. https://itunes.apple.com/cn/book/swift-programming-language/id881256329?l=en&mt=11