Stored properties in Swift can't refer to each other? - swift

What is the reason I can't give a value to a stored property that depends on the value of another one in Swift 2.0?
The code below gives an error saying:
Something.Type does not have a member named 'foo'
class Something {
let foo = "bar"
let baz = "\(foo) baz"
}
This is odd, as Something.Type certainly does have a member called foo.
Is there a way around this?

Looks like you're trying to initialise the variable baz, before swift has had a chance to know that foo is a property of Something. Place your initialisation inside the init constructor.
class Something {
let foo: String
let baz: String
init () {
foo = "bar"
baz = "\(foo) baz"
}
}

You can also use lazy initialization but now you have to make it a variable:
class Something {
let foo = "bar"
lazy var baz = { "\(self.foo) baz" }()
}

Related

Why does Swift allow assigning a struct property to itself, but not a class property?

Swift has this awesome error that shows up when you try to do something of the form x = x:
class Foo {
var foo = 1
}
var a = Foo()
a.foo = a.foo // error
This helped me avoid typos where the class had two similarly named properties, and I want to assign one to the other, but mistakenly typed the same one twice.
However, in this particular case:
struct Foo {
var foo = 1 {
didSet {
print("Did set")
}
}
mutating func f() {
foo = foo
}
}
var a = Foo()
a.foo = a.foo
It successfully compiles! There isn't even an error on the foo = foo line! If I change Foo to a class, or if I remove the didSet, then the expected error appears. It's just this struct + didSet combination that makes the compiler think "yeah, a.foo = a.foo makes a lot of sense! Let me allow that!"
I found this related post, which is about how to stop getting this error, rather than how to get it.
I also looked on bugs.swift.org, but there were only 3 results, and none of them are related.
I'm using Swift 5.3.2 and Xcode 12.4.
Is there any reason why assigning a struct property to itself is more "OK" than assigning a class property to itself?

Swift: implement a protocol variable as a lazy var?

It seems that it's not possible to implement a variable that's required by a protocol, with a lazy variable. For example:
protocol Foo {
var foo: String { get }
}
struct Bar: Foo {
lazy var foo: String = "Hello World"
}
Compiler complains that Type 'Bar' does not conform to protocol 'Foo'.
It's also not possible to add the lazy keyword in the protocol declaration, since then you get 'lazy' isn't allowed on a protocol requirement error.
So is this not at all possible?
Citing the Language Guide - Properties - Lazy Stored Properties [emphasis mine]:
A lazy stored property is a property whose initial value is not
calculated until the first time it is used.
I.e., the value is mutated upon first usage. Since foo has been blueprinted in the Foo protocol as get, implicitly nonmutating get, the value type Bar does not fulfil this promise with its lazy property foo, a property with a mutating getter.
Changing Bar to a reference type will allow it to fulfil the Foo blueprint (as mutating a property of a reference type doesn't mutate the type instance itself):
protocol Foo {
var foo: String { get }
}
class Bar: Foo {
lazy var foo: String = "Hello World"
}
Alternative, specify in the blueprint of the foo property of Foo that it has a mutating getter.
protocol Foo {
var foo: String { mutating get }
}
struct Bar: Foo {
lazy var foo: String = "Hello World"
}
See the following Q&A for some additional details of the mutating/nonmutating specifiers for getters and setters:
Swift mutable set in property

readonly mutable fields in Swift

When defining a class in Swift, you can have var properties which are like normal fields in other OOP languages, but also let properties which are both read-only and immutable (like T const * const in C++).
However is there a Swift equivalent of C++'s T * const? (That is, the field itself is immutable, but the object it points to is mutable)?
Here's a representation of my scenario:
class Foo {
let bar: Bar
init(bar: Bar) {
self.bar = bar
}
}
protocol Bar {
var fleem: Int? { get set }
}
class ConcreteBar : Bar {
var fleem: Int? = nil
}
var foo: Foo = Foo( bar: ConcreteBar() )
foo.bar.fleem = 123
(Playground link: https://iswift.org/playground?3jKAiu&v=2 )
Presently this gives me this compiler error:
Swift:: Error: cannot assign to property: 'bar' is a 'let' constant`
foo.bar.fleem = 123
Note that I am not actually setting bar, I'm only setting bar.fleem. I don't know why the compiler is complaining about assigning to bar.
If I change Foo to use this:
class Foo {
var bar: Bar
// ...
...then it compiles okay, but then I lose the guarantee that Foo.bar always has the same instance.
I know I could also change it to private(set):
class Foo {
public private(set) var bar: Bar
// ...
...but Foo itself is still free to overwrite the bar object-reference, and the use of var means that the compiler cannot assume the reference is immutable either, so some optimizations may be skipped.
I'm looking for something like a hypothetical let mutable or var readonly keyword or modifier.
By default, protocol typed objects have value value semantics. As a consequence, they're not mutable if the variable is a let constant.
To introduce reference semantics (and by extension, the mutability of objects referred to be a let constant), you need to make your protocol into a class protocol:
protocol Bar: class {
var fleem: Int? { get set }
}
You need to add the class attribute to the protocol to make it reference type compliant:
protocol Bar : class { ...

Swift - Can I set the value of self when calling a closure?

TLDR: Is there a equivalent for JavaScript call or apply in Swift?
Let's say I have Foo class that have an instance variable bar and a method baz that receives a closure as argument:
class Foo {
var bar: String = ""
func baz(closure: (Void) -> Void) {
closure()
}
}
I want to change the self value inside the closure. So the code is executed by the Foo instance.
Like this:
let foo = Foo()
foo.baz {
// I want to be able to change bar value without calling foo.bar, like this:
bar = "Hello world"
}
// Then foo.bar would be "Hello world"
Is that possible?
You can't access Foo's members in the closure in the way you've described, but what you can do is modify the closure to take an instance of Foo as an argument, and pass in self. The result could look something like this.
class Foo {
var bar: String = ""
func baz(closure: (this: Foo) -> Void) {
closure(this: self)
}
}
let foo = Foo()
foo.baz { this in
this.bar = "Hello world"
}
print(foo.bar) // Hello world
Here's a generic type version, which looks closer to call in javascript.
class Foo {
var bar: String = ""
}
func call<T>( this: T, closure: (T->Void) ){
closure(this)
}
let foo = Foo()
call(foo){ this in
this.bar = "Hello World"
}

Access instance constant in lazy constructor

Consider this example
class Foo {
private let bar = "bar"
lazy var baz : String = {
return "baz \(bar)"
}()
}
Unfortunately this won't compile and give the following error
'Foo.Type' does not have a member named 'bar'
I really do not want to declare bar outside the class (globally). Is there no other way to keep this inside the class and why isn't bar accessible in the first place?
TL;DR: preface with self
Swift can be quite misleading with error messages, but in this case, the answer can be deduced from the message. It is looking for bar on type Foo.Type, whereas you are trying to reference an instance variable. Here is code that works:
class Foo {
private let bar = "bar"
lazy var baz : String = {
return "baz \(self.bar)"
}()
}
In lazy props you need to say self
lazy var baz : String = {
let bar = self.bar
return "baz \(bar)"
}()