Access instance constant in lazy constructor - swift

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)"
}()

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?

Pass anonymous array as paramter in Swift

I have a simple class in my application:
class MyClass{
var Name: String
var Foo1: Bar
var Foo2: Bar?
//var Foos: [Bar]
}
Foo1 is never nil, and Foo2 is optional, so it might be nil.
Instead of having an init like the one below, I'd like to rather have a list property Foos: [Bar], that might contain 1 or 2 elements.
init(_ Name: String, _ Foo1: Bar, _ Foo2: Bar?){
self.Name = Name
self.Foo1 = Foo1
self.Foo2 = Foo2
}
In C# I'd write something like MyClass m = new MyClass("m", New List<Bar>{Bar1, Bar2}) or MyClass m = new MyClass("m", New List<Bar>{Bar1, null}). I'd prefer to rather have one property in MyClass as a List, instead of two separate fields where one might be nil.
I've tried this initializer:
init(_ Name: String, _ Foos: [Bar]) {
self.Name = Name
self.Foos = Foos
}
But when I try to pass an anonymous List to the initializer, I get this warning:
let m = MyClass("m", [Bar]().append(B1))
Cannot use mutating member on immutable value: function call returns immutable value
How can I pass a populated anonymous list to the initializer in Swift, like I would've done in C#?
Try this code
struct Bar { }
class MyClass {
var name: String
var foos: [Bar?]
init(name: String, foos: [Bar?]) {
self.name = name
self.foos = foos
}
}
let bar0 = Bar()
let bar1: Bar? = nil
let object = MyClass(name: "String", foos: [bar0, bar1])
Hope you got the idea. And read more about how it works in Swift
The most challenging requirement, which is also not met by the accepted answer:
Instead of having an init like the one below, I'd like to rather have a list property Foos: [Bar], that might contain 1 or 2 elements.
One way of dealing with this could be a custom runtime error.
enum BarWrapperError: Error {
case emptyList
}
To pass the list and check if it at least contains one element, you can introduce another type
struct BarWrapper {
init(bars: [Bar]) throws {
guard bars.count > 0 else {
throw BarWrapperError.emptyList
}
self.bars = bars
}
let bars: [Bar]
var firstBar: Bar {
return bars[0]
}
var otherBars:[Bar] {
return Array(bars[1 ..< bars.count])
}
}
BarWrapper is initialised with a list of bars. If this list is empty, it will throw an error.
your MyClass would now look like:
class MyClass {
let name: String
let firstBar: Bar
let otherBars: [Bar]
init(name: String, barWrapper: BarWrapper) {
self.name = name
self.firstBar = barWrapper.firstBar
self.otherBars = barWrapper.otherBars
}
}
If you care for the error and want to continue the execution, you can use this like
do {
let bar0 = Bar()
let bar1 = Bar()
let barWrapper = try BarWrapper(bars: [bar0, bar1])
let object = MyClass(name: "String", barWrapper: barWrapper)
print(object.otherBars)
} catch BarWrapperError.emptyList {
print("empty bar list")
}
If you rather want to crash the app if the list is empty, you can shorten it to
let bar0 = Bar()
let bar1 = Bar()
let barWrapper = try! BarWrapper(bars: [bar0, bar1])
let object = MyClass(name: "String", barWrapper: barWrapper)
print(object.otherBars)
by using try!
You can also do
let bar0 = Bar()
let bar1 = Bar()
if let barWrapper = try? BarWrapper(bars: [bar0, bar1]) {
let object = MyClass(name: "String", barWrapper: barWrapper)
print(object.otherBars)
}
if you don't need error handling and your app would be still operational if the MyClass instance isn't created.

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 { ...

Referencing another variable in a global variable in swift

I don't understand why the following doesn't work in Swift:
class SomeClass {
var foo = 1
var bar = self.foo + 1
}
and what's the way around it?
It doesn't work because you cannot use self in that scope to define default values for properties. I believe it is due to the fact that you cannot use self before the object is properly initialized. You could use an explicit initializer instead.
class SomeClass {
var foo: Int
var bar: Int
init() {
self.foo = 1
self.bar = self.foo + 1
}
}
You can, however, access static members.
class SomeClass {
static let initialValue = 1
var foo = initialValue
var bar = initialValue + 1
}

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

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" }()
}