In Swift, how do I make a class's array of objects only accessible as an entire array? - swift

I have a pattern that I want developers to be able to access my classes, due to using property observers. In the examples below, I have property observer on B.a and A.str, below, so if developers try modify B.a.str directly, only the str property observer is fired. So I want to limit access to B.a to only setting and getting the entire array, making modifications have to happen outside of access to B directly (i.e., you'll see below). I have been reading a lot of Apple docs on Swift 4+, and I can't figure it out. Example below:
class A
{
let str: String? = "Hello!"
...
}
class B
{
var a: [A]()
...
}
I don't want users to use this set of classes like this:
let b: B = B()
B.a.str? = "NewValue"
How do I make "a" accessible only by this pattern:
let a_ref = b.a
a_ref.str? = "NewValue"
b.a = a_ref

I am not sure why you want to have this pattern and I am not sure if it's a good idea to use it. Anyway, you can achieve what you want by simply returning a copy of a when you use the getter that B provides.
class A {
var str: String?
init(str: String? = "Hello") {
self.str = str
}
func copy() -> A {
return A(str: str)
}
}
class B {
private var _a = A()
var a: A {
get { return _a.copy() }
set { _a = newValue }
}
init() { }
}
Now you can test it with the following code:
// First Attempt (Direct)
let b: B = B()
b.a.str = "NewValue"
print(b.a.str) // Prints "Hello"
// Second Attempt (via Reference)
let a_ref = b.a
a_ref.str = "NewValue"
b.a = a_ref
print(b.a.str) // Prints "NewValue

Related

Getters and Setters in Swift - Does it make sense to use WillSet and DidSet instead?

I was doing some research about the reasons we should use Get and Set for our properties.
I've noticed 3 main reasons for it
When you want to do/check something before you actually set the
property
When you want to have a property that you can only Get from it
(maybe for security purposes I guess? ), or give it different access
levels.
Hiding the internal representation of the property while exposing a
property using an alternative representation. (which for me doesn't
make a lot of sense since i can access it on the wrong place using
the Set function anyways)
The code below is a example of how you would implement Get and Set for properties in Swift, taking advantage of those 3 points I mentioned:
class Test
{
private var _testSet:String!
private var _testGetOnly:String
var testSet:String{
get{
return _testSet
}
set{
_testSet = newValue + "you forgot this string"
}
}
var testGetOnly:String!{
get{
return _testGetOnly
}
}
init(testSet:String, testGetOnly:String)
{
_testSet = testSet
_testGetOnly = testGetOnly
}
}
But this other example below also take advantage of those points mentioned but instead of using another computed property to return the private property value I just use the willSet and didSet observers
class Test
{
var testGet:String {
willSet{
fatalError("Operation not allowed")
}
}
var testWillSet:String!{
didSet{
self.testWillSet = self.testWillSet + "you forgot this string"
}
}
init(testGet:String, testWillSet:String)
{
self.testGet = testGet
self.testWillSet = testWillSet
}
}
So I'm curious to know what are the ADVANTAGES and DISADVANTAGES of each implementation.
Thanks in advance
Your question boils down to compile time vs. run time error. To address your 3 questions:
Yes, willCheck is your only option here
Readonly properties fall into 2 types: (a) those whose value derive from other properties, for example, their sum; and (b) those that you want to be able to change by yourself, but not by the users. The first type truly have no setter; the second type has a public getter and a private setter. The compiler can help you check for that and the program will not compile. If you throw a fatalError in didSet you get a runtime error and your application will crash.
There can be state objects that you don't want the user to freely mess with, and yes, you can completely hide those from the users.
Your code first example was too verbose in defining the backing variables - you don't need to do that. To illustrate these points:
class Test
{
// 1. Validate the new value
var mustBeginWithA: String = "A word" {
willSet {
if !newValue.hasPrefix("A") {
fatalError("This property must begin with the letter A")
}
}
}
// 2. A readonly property
var x: Int = 1
var y: Int = 2
var total: Int {
get { return x + y }
}
private(set) var greeting: String = "Hello world"
func changeGreeting() {
self.greeting = "Goodbye world" // Even for private property, you may still
// want to set it, just not allowing the user
// to do so
}
// 3. Hide implementation detail
private var person = ["firstName": "", "lastName": ""]
var firstName: String {
get { return person["firstName"]! }
set { person["firstName"] = newValue }
}
var lastName: String {
get { return person["lastName"]! }
set { person["lastName"] = newValue }
}
var fullName: String {
get { return self.firstName + " " + self.lastName }
set {
let components = newValue.componentsSeparatedByString(" ")
self.firstName = components[0]
self.lastName = components[1]
}
}
}
Usage:
let t = Test()
t.mustBeginWithA = "Bee" // runtime error
t.total = 30 // Won't compile
t.greeting = "Goodbye world" // Won't compile. The compiler does the check for you
// instead of a crash at run time
t.changeGreeting() // OK, greeting now changed to "Goodbye world"
t.firstName = "John" // Users have no idea that they are actually changing
t.lastName = "Smith" // a key in the dictionary and there's no way for them
// to access that dictionary
t.fullName = "Bart Simpsons" // You do not want the user to change the full name
// without making a corresponding change in the
// firstName and lastName. With a custome setter, you
// can update both firstName and lastName to maintain
// consistency
A note about private in Swift 2 vs. Swift 3: if you try this in a Swift 2 playground, you will find t.greeting = "Goodbye world" works just fine. This is because Swift 2 has a strange access level specifier: private means "only accessible within the current file". Separate the class definition and the sample code into different files and Xcode will complain. In Swift 3, that was changed to fileprivate which is both clearer and save the private keyword for something more similar to to Java and .NET

Swift append String var to an List by reference

Hi i want to append string by reference to a List.
for example :
var str = "old"
func foo(){
list.append(str)
}
func changeStr(){
str = "new"
}
my problem is that the value of str ("old") is being appended and not the reference, so when i change str to "new" it doesn't take any effect on the list.
my question is how i can append reference to a string var?
thanks.
You have to wrap the string in a class. Have the array hold members of your class, and update the class object with a new string value.
Just like if you had an array of ints in whatever language you are used to.
Best solution is to get used to standard Swift/functional idioms.
-- EXAMPLE --
class StringObject {
init(str: String) {
self.str = str
}
var str: String = ""
}
let so = StringObject(str: "old")
func foo() {
list.append(so)
}
func changeStr() {
so.str = "new"
}

Protocols and composition on Swift

I am trying to learn more about protocols and got stuck without understanding what is going on on the following piece of code. Mostly seeking some light and direction for good articles and pieces of explanation basically.
In one of the examples from Apple's Library protocols are doing a bit more than making sure the classes conform to it.
They are allowing objects from one class to access methods within other classes without using the traditional class inheritance definition.
This line of code let generator: RandomNumberGenerator on the Dice class is allowing the var d6 that is of type Dice to access a function func random() -> Double that is outside of Dice scope and inside LinearCongruentialGenerator scope and is using the RandomNumberGenerator to make this bridge.
Also allowing to do the following call d6.generator.random() when again .ramdom() is not on Dices scope.
protocol RandomNumberGenerator {
func random() -> Double
}
class LinearCongruentialGenerator: RandomNumberGenerator {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom * a + c) % m)
return lastRandom/m
}
}
class Dice {
let sides: Int
let generator: RandomNumberGenerator
init(sides: Int, generator: RandomNumberGenerator) {
println(generator.random())
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
}
var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
Update to question
Thanks for the answers! By doing some research I think I just touched composition. So I wrote the code bellow to exemplify composition a bit better, without using protocols or delegates. Just pure composition. Please let me know if I got it right as it may help other people trying to understand composition.
class A {
var a1: String
init (valueToA: String){
self.a1 = valueToA
}
func aFunc1() -> A {
return self
}
}
class B {
var b1: A
init (valueToB: A ) {
self.b1 = valueToB
}
func bFunc1(){
println("I am bFunc and I am calling aFunc \(b1.aFunc1())")
}
}
var myA = A(valueToA: "Initiated myA with this string")
//myA.aFunc1()
var myB = B(valueToB: myA)
myB.b1 = A(valueToA: "a value to B")
myB.b1.aFunc1()
The same code but now with protocols
protocol myProtocol {
func protocolFunc(value: String) -> String
}
class A: myProtocol {
var a1: String
init (valueToA: String){
self.a1 = valueToA
}
func aFunc1() -> A {
return self
}
func protocolFunc(value: String) -> String {
return value
}
}
class B {
var b1: A
var b2: myProtocol
init (valueToB1: A, valueToB2: myProtocol ) {
self.b1 = valueToB1
self.b2 = valueToB2
}
func bFunc1(){
println("I am bFunc and I am calling aFunc \(b1.aFunc1())")
}
func callProtocolFuncOnA (value: String) {
b1.protocolFunc(value)
}
}
var myA1 = A(valueToA: "my A 1 created")
var myA2 = A(valueToA: "my A 2 created")
var myB = B(valueToB1: myA1, valueToB2: A(valueToA: "my A 3 created"))
myB.callProtocolFuncOnA("calling other function")
As #DevAndArtist says in his comment it allows encapsulation (abstraction) when a type of RandomNumberGenerator is passed in the initializer of the Dice class and so only that part of your implementation is visible to you.
In my humble opinion it could be better if the constant generator was not visible outside the Dice class scope as you say in your question, for example making his access modifier private, but remember that in your example all is set in the same swift file and this implies that the private access modifier isn't like the other programming languages like C#, Java, etc.
Even doing private you can access d6.generator.random() in your call because it exist in the same Swift file, the only way you can hide the property if you create a new Swift file for the Dice class and then this call when the property is private of course :
var d6 = Dice(sides: 6, generator: LinearCongruentialGenerator())
println(d6.generator.random())
gave you the following error:
'Dice' does not have a member named 'generator'
And you can hide the property outside of its scope. It's only a point of view.
I hope this help you.

Using a Type Variable in a Generic

I have this question except for Swift. How do I use a Type variable in a generic?
I tried this:
func intType() -> Int.Type {
return Int.self
}
func test() {
var t = self.intType()
var arr = Array<t>() // Error: "'t' is not a type". Uh... yeah, it is.
}
This didn't work either:
var arr = Array<t.Type>() // Error: "'t' is not a type"
var arr = Array<t.self>() // Swift doesn't seem to even understand this syntax at all.
Is there a way to do this? I get the feeling that Swift just doesn't support it and is giving me somewhat ambiguous error messages.
Edit: Here's a more complex example where the problem can't be circumvented using a generic function header. Of course it doesn't make sense, but I have a sensible use for this kind of functionality somewhere in my code and would rather post a clean example instead of my actual code:
func someTypes() -> [Any.Type] {
var ret = [Any.Type]()
for (var i = 0; i<rand()%10; i++) {
if (rand()%2 == 0){ ret.append(Int.self) }
else {ret.append(String.self) }
}
return ret
}
func test() {
var ts = self.someTypes()
for t in ts {
var arr = Array<t>()
}
}
Swift's static typing means the type of a variable must be known at compile time.
In the context of a generic function func foo<T>() { ... }, T looks like a variable, but its type is actually known at compile time based on where the function is called from. The behavior of Array<T>() depends on T, but this information is known at compile time.
When using protocols, Swift employs dynamic dispatch, so you can write Array<MyProtocol>(), and the array simply stores references to things which implement MyProtocol — so when you get something out of the array, you have access to all functions/variables/typealiases required by MyProtocol.
But if t is actually a variable of kind Any.Type, Array<t>() is meaningless since its type is actually not known at compile time. (Since Array is a generic struct, the compiler needs know which type to use as the generic parameter, but this is not possible.)
I would recommend watching some videos from WWDC this year:
Protocol-Oriented Programming in Swift
Building Better Apps with Value Types in Swift
I found this slide particularly helpful for understanding protocols and dynamic dispatch:
There is a way and it's called generics. You could do something like that.
class func foo() {
test(Int.self)
}
class func test<T>(t: T.Type) {
var arr = Array<T>()
}
You will need to hint the compiler at the type you want to specialize the function with, one way or another. Another way is with return param (discarded in that case):
class func foo() {
let _:Int = test()
}
class func test<T>() -> T {
var arr = Array<T>()
}
And using generics on a class (or struct) you don't need the extra param:
class Whatever<T> {
var array = [T]() // another way to init the array.
}
let we = Whatever<Int>()
jtbandes' answer - that you can't use your current approach because Swift is statically typed - is correct.
However, if you're willing to create a whitelist of allowable types in your array, for example in an enum, you can dynamically initialize different types at runtime.
First, create an enum of allowable types:
enum Types {
case Int
case String
}
Create an Example class. Implement your someTypes() function to use these enum values. (You could easily transform a JSON array of strings into an array of this enum.)
class Example {
func someTypes() -> [Types] {
var ret = [Types]()
for _ in 1...rand()%10 {
if (rand()%2 == 0){ ret.append(.Int) }
else {ret.append(.String) }
}
return ret
}
Now implement your test function, using switch to scope arr for each allowable type:
func test() {
let types = self.someTypes()
for type in types {
switch type {
case .Int:
var arr = [Int]()
arr += [4]
case .String:
var arr = [String]()
arr += ["hi"]
}
}
}
}
As you may know, you could alternatively declare arr as [Any] to mix types (the "heterogenous" case in jtbandes' answer):
var arr = [Any]()
for type in types {
switch type {
case .Int:
arr += [4]
case .String:
arr += ["hi"]
}
}
print(arr)
I would break it down with the things you already learned from the first answer. I took the liberty to refactor some code. Here it is:
func someTypes<T>(t: T.Type) -> [Any.Type] {
var ret = [Any.Type]()
for _ in 0..<rand()%10 {
if (rand()%2 == 0){ ret.append(T.self) }
else {
ret.append(String.self)
}
}
return ret
}
func makeArray<T>(t: T) -> [T] {
return [T]()
}
func test() {
let ts = someTypes(Int.self)
for t in ts {
print(t)
}
}
This is somewhat working but I believe the way of doing this is very unorthodox. Could you use reflection (mirroring) instead?
Its possible so long as you can provide "a hint" to the compiler about the type of... T. So in the example below one must use : String?.
func cast<T>(_ value: Any) -> T? {
return value as? T
}
let inputValue: Any = "this is a test"
let casted: String? = cast(inputValue)
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
Why Swift doesn't just allow us to let casted = cast<String>(inputValue) I'll never know.
One annoying scenerio is when your func has no return value. Then its not always straightford to provide the necessary "hint". Lets look at this example...
func asyncCast<T>(_ value: Any, completion: (T?) -> Void) {
completion(value as? T)
}
The following client code DOES NOT COMPILE. It gives a "Generic parameter 'T' could not be inferred" error.
let inputValue: Any = "this is a test"
asyncCast(inputValue) { casted in
print(casted)
print(type(of: casted))
}
But you can solve this by providing a "hint" to compiler as follows:
asyncCast(inputValue) { (casted: String?) in
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>
}

Re-initialize a lazy initialized variable in Swift

I have a variable that initialized as:
lazy var aClient:Clinet = {
var _aClient = Clinet(ClinetSession.shared())
_aClient.delegate = self
return _aClient
}()
The problem is, at some point, I need to reset this aClient variable so it can initialize again when the ClinetSession.shared() changed. But if I set the class to optional Clinet?, LLVM will give me an error when I try to set it to nil. If I just reset it somewhere in the code using aClient = Clinet(ClinetSession.shared()), it will end up with EXEC_BAD_ACCESS.
Is there a way that can use lazy and being allowed to reset itself?
lazy is explicitly for one-time only initialization. The model you want to adopt is probably just an initialize-on-demand model:
var aClient:Client {
if(_aClient == nil) {
_aClient = Client(ClientSession.shared())
}
return _aClient!
}
var _aClient:Client?
Now whenever _aClient is nil, it will be initialized and returned. It can be reinitialized by setting _aClient = nil
Because the behavior of lazy changed in Swift 4, I wrote a few structs that give very specific behavior, which should never change between language versions. I put these on GitHub, under the BH-1-PD license: https://github.com/RougeWare/Swift-Lazy-Patterns
ResettableLazy
Here is the one relevant to this question, which gives you a way to lazily-initialize a value, cache that value, and destroy it so it can be lazily-reinitialized later.
Note that this requires Swift 5.1! For the Swift 4 version, see version 1.1.1 of that repo.
The simple usage of this is very straightforward:
#ResettableLazy
var myLazyString = "Hello, lazy!"
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
_myLazyString.clear()
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
myLazyString = "Overwritten"
print(myLazyString) // Just returns the value "Overwritten"
_myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
This will print:
Hello, lazy!
Hello, lazy!
Hello, lazy!
Hello, lazy!
Overwritten
Hello, lazy!
If you have complex initializer logic, you can pass that to the property wrapper:
func makeLazyString() -> String {
print("Initializer side-effect")
return "Hello, lazy!"
}
#ResettableLazy(initializer: makeLazyString)
var myLazyString: String
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
_myLazyString.clear()
print(myLazyString) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString) // Just returns the value "Hello, lazy!"
myLazyString = "Overwritten"
print(myLazyString) // Just returns the value "Overwritten"
_myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
You can also use it directly (instaed of as a property wrapper):
var myLazyString = ResettableLazy<String>() {
print("Initializer side-effect")
return "Hello, lazy!"
}
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString.wrappedValue) // Just returns the value "Hello, lazy!"
myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
print(myLazyString.wrappedValue) // Just returns the value "Hello, lazy!"
myLazyString.wrappedValue = "Overwritten"
print(myLazyString.wrappedValue) // Just returns the value "Overwritten"
_myLazyString.clear()
print(myLazyString.wrappedValue) // Initializes, caches, and returns the value "Hello, lazy!"
These will both print:
Initializer side-effect
Hello, lazy!
Hello, lazy!
Initializer side-effect
Hello, lazy!
Hello, lazy!
Overwritten
Initializer side-effect
Hello, lazy!
This answer has been updated; its original solution no longer works in Swift 4 and newer.
Instead, I recommend you use one of the solutions listed above, or #PBosman's solution
Previously, this answer hinged on behavior which was a bug. Both that old version of this answer, its behavior, and why it's a bug are described in the text and comments of Swift bug SR-5172 (which has been resolved as of 2017-07-14 with PR #10,911), and it's clear that this behavior was never intentional.
That solution is in that Swift bug's text, and also in the history of this answer, but because it's a bug exploit that doesn't work in Swift 3.2+ I recommend you do not do that.
EDIT: As per Ben Leggiero's answer, lazy vars can be nilable in Swift 3.
EDIT 2: Seems like nilable lazy vars are no more.
Very late to the party, and not even sure if this will be relevant in Swift 3, but here goes. David's answer is good, but if you want to create many lazy nil-able vars, you will have to write a pretty hefty block of code. I'm trying to create an ADT that encapsulates this behaviour. Here's what I've got so far:
struct ClearableLazy<T> {
private var t: T!
private var constructor: () -> T
init(_ constructor: #escaping () -> T) {
self.constructor = constructor
}
mutating func get() -> T {
if t == nil {
t = constructor()
}
return t
}
mutating func clear() { t = nil }
}
You would then declare and use properties like this:
var aClient = ClearableLazy(Client.init)
aClient.get().delegate = self
aClient.clear()
There are things I don't like about this yet, but don't know how to improve:
You have to pass a constructor to the initializer, which looks ugly. It has the advantage, though, that you can specify exactly how new objects are to be created.
Calling get() on a property every time you want to use it is terrible. It would be slightly better if this was a computed property, not a function, but computed properties cannot be mutating.
To eliminate the need to call get(), you have to extend every type you want to use this for with initializers for ClearableLazy.
If someone feels like picking it up from here, that would be awesome.
This allows setting the property to nil to force reinitialization:
private var _recordedFileURL: NSURL!
/// Location of the recorded file
private var recordedFileURL: NSURL! {
if _recordedFileURL == nil {
let file = "recording\(arc4random()).caf"
let url = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(file)
NSLog("FDSoundActivatedRecorder opened recording file: %#", url)
_recordedFileURL = url
}
return _recordedFileURL
}
Swift 5.1:
class Game {
private var _scores: [Double]? = nil
var scores: [Double] {
if _scores == nil {
print("Computing scores...")
_scores = [Double](repeating: 0, count: 3)
}
return _scores!
}
func resetScores() {
_scores = nil
}
}
Here is how to use:
var game = Game()
print(game.scores)
print(game.scores)
game.resetScores()
print(game.scores)
print(game.scores)
This produces the following output:
Computing scores...
[0.0, 0.0, 0.0]
[0.0, 0.0, 0.0]
Computing scores...
[0.0, 0.0, 0.0]
[0.0, 0.0, 0.0]
Swift 5.1 and Property Wrapper
#propertyWrapper
class Cached<Value: Codable> : Codable {
var cachedValue: Value?
var setter: (() -> Value)?
// Remove if you don't need your Value to be Codable
enum CodingKeys: String, CodingKey {
case cachedValue
}
init(setter: #escaping () -> Value) {
self.setter = setter
}
var wrappedValue: Value {
get {
if cachedValue == nil {
cachedValue = setter!()
}
return cachedValue!
}
set { cachedValue = nil }
}
}
class Game {
#Cached(setter: {
print("Computing scores...")
return [Double](repeating: 0, count: 3)
})
var scores: [Double]
}
We reset the cache by setting it to any value:
var game = Game()
print(game.scores)
print(game.scores)
game.scores = []
print(game.scores)
print(game.scores)
There are some good answers here. Resetting a lazy var is indeed, desirable in a lot of cases.
I think, you can also define a closure to create client and reset lazy var with this closure. Something like this:
class ClientSession {
class func shared() -> ClientSession {
return ClientSession()
}
}
class Client {
let session:ClientSession
init(_ session:ClientSession) {
self.session = session
}
}
class Test {
private let createClient = {()->(Client) in
var _aClient = Client(ClientSession.shared())
print("creating client")
return _aClient
}
lazy var aClient:Client = createClient()
func resetClient() {
self.aClient = createClient()
}
}
let test = Test()
test.aClient // creating client
test.aClient
// reset client
test.resetClient() // creating client
test.aClient
If the objective is to re-initialize a lazy property but not necessarily set it to nil, Building from Phlippie Bosman and Ben Leggiero, here is something that avoids conditional checks every time the value is read:
public struct RLazy<T> {
public var value: T
private var block: () -> T
public init(_ block: #escaping () -> T) {
self.block = block
self.value = block()
}
public mutating func reset() {
value = block()
}
}
To test:
var prefix = "a"
var test = RLazy { () -> String in
return "\(prefix)b"
}
test.value // "ab"
test.value = "c" // Changing value
test.value // "c"
prefix = "d"
test.reset() // Resetting value by executing block again
test.value // "db"
I made #David Berry's answer into a property wrapper. Works great with UI-components that you want to reload if you need to apply size changes but otherwise want to hold in their configured state.
#propertyWrapper class Reloadable<T: AnyObject> {
private let initializer: (() -> T)
private var _wrappedValue: T?
var wrappedValue: T {
if _wrappedValue == nil {
_wrappedValue = initializer()
}
return _wrappedValue!
}
init(initializer: #escaping (() -> T)) {
self.initializer = initializer
}
func nuke() {
_wrappedValue = nil
}
}
Here's an example with a CAShapeLayer. Set you variable like so:
#Reloadable<CAShapeLayer>(initializer: {
Factory.ShapeLayer.make(fromType: .circle(radius: Definitions.radius, borderWidth: Definitions.borderWidth)) // this factory call is just what I use personally to build my components
}) private var circleLayer
and when you want to reload your view just call:
_circleLayer.nuke()
Then you can use the var circleLayer as you normally would in your re-layout routine, upon which it will get re-initialized.
PS: I made a gist for the file I use in my own project: https://gist.github.com/erikmartens/b34a130d11b62400ab13a59a6c3dbd91
This seems like a pretty bad code smell. Something strange is going on with:
var _aClient = Clinet(ClinetSession.shared())
What is ClinetSession.shared()?
This looks like a static function that returns a new instance of ClinetSession on every call.
So no wonder you are not seeing changes to this object. It seems like a broken singleton to me.
class ClinetSession {
static func shared() -> Self {
ClinetSession()
}
}
Try doing this instead:
class ClinetSession {
static let shared = ClinetSession()
var value: Int = 0
}
Now if you change ClinetSession value you will see it.
There's no need for a lazy property that can be reset here as far as I can tell.
Here's a fully working example. BTW if you don't own ClinetSession then write a wrapper to control this.
class Clinet {
let clinetSession: ClinetSession
init(_ clinetSession: ClinetSession) {
self.clinetSession = clinetSession
}
var delegate: P?
}
class ClinetSession {
static let shared = ClinetSession()
var value = 0
}
protocol P { }
struct Test: P {
lazy var aClient:Clinet = {
var _aClient = Clinet(ClinetSession.shared)
_aClient.delegate = self
return _aClient
}()
mutating func updateSession() {
aClient.clinetSession.value = 10
}
}
var test = Test()
test.updateSession()
print(test.aClient.clinetSession.value)
// prints 10
Note: If you don't want to use a singleton then don't use shared() as the constructor as this is a convention. But then it's up to you to make sure you pass in the same reference as the one you want to mutate. That's your job to manage. The singleton just makes sure there is only 1 instance so this becomes simpler but has its trade offs.