Swift can lazy and didSet set together - swift

I have a property
public lazy var points: [(CGFloat,CGFloat,CGFloat)] = {
var pointsT = [(CGFloat,CGFloat,CGFloat)]()
let height = 100.0
for _ in 1...10 {
pointsT.append((someValue,someValue,100.0))
}
return pointsT
}()
And i want to add a didSet method, is it possible?

Short answer: no.
Try out this simple example in some class or method of yours:
lazy var myLazyVar: Int = {
return 1
} () {
willSet {
print("About to set lazy var!")
}
}
This gives you the following compile time error:
Lazy properties may not have observers.
With regard to the let statement in the other answer: lazy variable are not necessary just "let constants with delayed initialisation". Consider the following example:
struct MyStruct {
var myInt = 1
mutating func increaseMyInt() {
myInt += 1
}
lazy var myLazyVar: Int = {
return self.myInt
} ()
}
var a = MyStruct()
print(a.myLazyVar) // 1
a.increaseMyInt()
print(a.myLazyVar) // 1: "initialiser" only called once, OK
a.myLazyVar += 1
print(a.myLazyVar) // 2: however we can still mutate the value
// directly if we so wishes

The short answer is as other have said is "no" but there is a way to get the effect using a private lazy var and computed var.
private lazy var _username: String? = {
return loadUsername()
}()
var username: String? {
set {
// Do willSet stuff in here
if newValue != _username {
saveUsername(newValue)
}
// Don't forget to set the internal variable
_username = newValue
// Do didSet stuff here
// ...
}
get {
return _username
}
}

No
points is a constant, you cannot set anything to it. The only difference to a let constant is that it is (potentially) initialized later.
This answer provides a little more information as to why you use var instead of let in the lazy case.
Edit: to make the answer not look to empty
Take a look at this blog post where the author raises a few valid points regarding why observing lazy vars might not yet be supported. What should oldValue be in the observer? nil? That would not be a good idea in your non-optional case.

Yes, Started by version Swift 5.3 Property observers can now be attached to lazy properties.
class C {
lazy var property: Int = 0 {
willSet { print("willSet called!") } // Okay
didSet { print("didSet called!") } // Okay
}
}
or
class C {
lazy var property: Int = { return 0 }() {
willSet { print("willSet called!") } // Okay
didSet { print("didSet called!") } // Okay
}
}
Please take a look at the links below for more:
https://www.swiftbysundell.com/tips/lazy-property-observers/
https://github.com/apple/swift/blob/main/CHANGELOG.md

Related

Is it possible to point class instance variables to `inout` parameters in Swift?

Is it possible for multiple class methods to access and modify a single inout parameter that is set in the class constructor? For example something like this:
class X {
var mySwitch: Bool
init(mySwitch: inout Bool) {
self.mySwitch = mySwitch
}
func updateSwitch() {
self.mySwitch.toggle() // this should toggle the external Boolean value that was originally passed into the init
}
}
// usage
var myBool: Bool = false
let x = X(mySwitch: &myBool)
x.updateSwitch()
print(myBool) // this should read 'true'
Short Answer
No.
Long Answer
There are other approaches that can satisfy this.
Binding Variables
In SwiftUI we use Binding Variables to do stuff like this. When the Binding variable updates, it also updates the bound variable. I'm not sure if it will work in Sprite Kit.
class X {
var mySwitch: Binding<Bool>
init(_ someSwitch: Binding<Bool>) {
self.mySwitch = someSwitch
}
func toggle() { mySwitch.wrappedValue.toggle() }
}
struct Y {
#State var mySwitch: Bool = false
lazy var switchHandler = X($mySwitch)
}
Callbacks
We can add a callback to X and call it on didSet of the boolean.
class X {
var mySwitch: Bool {
didSet { self.callback(mySwitch) } // hands the new value back to the call site in Y
}
let callback: (Bool) -> Void
init(_ someSwitch: Bool, _ callback: #escaping (Bool) -> Void) {
self.mySwitch = someSwitch
self.callback = callback
}
func toggle() { mySwitch = !mySwitch } // explicitly set to trigger didSet
}
class Y {
var mySwitch: Bool = false
lazy var switchHandler = X(mySwitch) {
self.mySwitch = $0 // this is where we update the local value
}
}
Short answer: NO.
Your question does not make sense. An inout parameter allows you read/write access to the parameter during the call to function. It has no meaning once the function returns. You sem to think it creates some persistent link between the parameter that is passed as inout and some other variable. It DOES NOT.
You can't "link" a variable that way because inout only works in the scope it's declared.
If what you want is to have some kind of global state, you can wrap it in a class.
class Wrapper {
var value = false
init() {}
}
class Modifier {
let wrapper: Wrapper
init(wrapper: Wrapper) {
self.wrapper = wrapper
}
func updateSwitch() {
wrapper.value.toggle()
}
}
let wrapper = Wrapper()
let modifier = Modifier(wrapper: wrapper)
modifier.updateSwitch()
print(wrapper.value) // this will read 'true'

How do I verify incoming data for properties?

In my model, I have a singleton class which will contain some global properties and methods. I think I've set up the class correctly but I need a way to verify incoming data for the properties. I'm trying to use get and set but these seem to need to return void. I can't use init because it's a singleton.
Am I missing something?
final class Globals {
private init(){}
static let sharedInstance = Globals()
//MARK: Properties
private var _peopleCount: Int!
var peopleCount: Int! {
get {
return _peopleCount
}
set(newPeopleCount) {
guard newPeopleCount > 0 else {
return nil // can't return nil here
}
}
}
}
You shouldn't define your variables as implicitly unwrapped optionals unless you have a very good reason to do so.
Your immediate error is that you cannot return a value in a setter, you need to assign the value to the variable there. If you want to mark an invalid value by peopleCount being nil, define peopleCount as Int? and assign to it nil when the check fails.
final class Globals {
private init(){}
static let sharedInstance = Globals()
//MARK: Properties
private var _peopleCount: Int?
var peopleCount: Int? {
get {
return _peopleCount
}
set(newPeopleCount) {
if let newValue = newPeopleCount, newValue > 0 {
_peopleCount = newValue
}
}
}
}
For most use cases, there is no need for the private backing variable, you can just use didSet to check the value before assigning it. Thanks for #LeoDabus for the idea in comments.
var peopleCount: Int? {
didSet {
if let newValue = peopleCount, newValue > 0 {
peopleCount = newValue
} else {
peopleCount = oldValue
}
}
}

How can I update an assignment in Swift?

I want to know how I can do the following:
var x = Observed<String>("hello")
label.text = x
Somewhere else x is updated:
x.value = "World"
Then label.text is automatically updated. Here is what I have for Observed so far
class Observed<T> {
typealias Observer = (T)->()
var observer: Observer?
var value: T {
didSet {
if let observer = observer {
observer(value)
}
}
}
init(_ v: T) {
value = v
}
}
I believe this is sort of what RxSwift, etc. do, but that's overkill for what I'm doing
You should probably just use a didSet in this scenario.
class YourClass {
var x: String = "" { didSet { someVC.someLabel.text = x } }
}
There is no "native" data binding in iOS as of now.
EDIT: of course there is KVO, thanks #matt for the heads-up :)

Is there a way to tell if a lazy var has been initialized?

I don't want to initialize a view controller until I need to display its view., so I have it in a lazy var like:
lazy var foo: NSViewController! = {
let foo = NSViewController()
foo.representedObject = self.representedObject
return foo
}()
// ...
override var representedObject: Any? {
didSet {
if foo != nil {
foo.representedObject = representedObject
}
}
}
self.representedObject is set before foo is ever referenced, but every time I call if foo != nil, it initializes foo :c
Is there any way I can test if foo has already been set?
A shorter version that uses Swift's built-in lazy semantics:
struct Foo {
lazy var bar: Int = {
hasBar = true
return 123
}()
private(set) var hasBar = false
}
Just check for hasBar instead.
lazy is just a convenience wrapper around one specific lazy-instantiation pattern (and one that is only moderately useful). If you want your own pattern, don't use lazy; just build it yourself.
private var _foo: NSViewController? = nil
var foo: NSViewController {
if let foo = _foo {
return foo
}
let foo = NSViewController()
foo.representedObject = self.representedObject
_foo = foo
return foo
}
// This can be private or public, as you like (or you don't technically need it)
var isFooLoaded: Bool {
return _foo != nil
}
override var representedObject: Any? {
didSet {
if !isFooLoaded {
foo.representedObject = representedObject
}
}
}
This is designed to follow the isViewLoaded pattern, which addresses the same basic problem.
The actual solution I've gone with in my projects is to use the Lazy Containers package that I created, in which I included an isInitialized field:
import LazyContainers
#Lazy
var foo: NSViewController = {
let foo = NSViewController()
foo.representedObject = self.representedObject
return foo
}()
// ...
override var representedObject: Any? {
didSet {
if _foo.isInitialized {
foo.representedObject = representedObject
}
}
}

When should property observers run? What are the exceptions?

The behavior of property observers surprises me in the following situation. I understand that observers do not recursively call themselves, but this behavior seems to carry over to different instances of the same class, and even to instances of different subclasses.
As I understand it, property observers run any time a property is set, even if the value doesn't change, except in initialization. What exactly are the exceptions to this rule? When exactly will property observers be disregarded as seen below?
var observersCalled = 0
class ClassOne {
var relatedOne: ClassOne?
var relatedTwo: ClassTwo?
var property: String = "Initial" {
didSet {
observersCalled += 1
relatedOne?.property = property
relatedTwo?.property = property
}
}
}
class ClassTwo {
var property: String = "Initial" {
didSet {
observersCalled += 1
}
}
}
class Subclass: ClassOne {
override var property: String {
didSet {
observersCalled += 1
}
}
}
let thing = ClassOne()
thing.relatedOne = ClassOne()
thing.property = "New Value"
print(observersCalled) //1 (really?)
observersCalled = 0
thing.relatedOne = nil
thing.relatedTwo = ClassTwo()
thing.property = "Another Value"
print(observersCalled) //2 (makes sense)
observersCalled = 0
thing.relatedOne = Subclass()
thing.relatedTwo = nil
thing.property = "Yet Another Value"
print(observersCalled) //1 (really!?)
There are no exceptions. The behavior I asked about is a bug. I have submitted a bug report for those tracking its progress.