Simplifying nested if in swift - swift

My app has a class called MyDevice that I use to communicate with hardware. This hardware is optionnal, so is the instance variable:
var theDevice:MyDevice = nil
Then, in the app, I have to initialize the device (for communication) then perform a self test to check its availability and readiness to perform. If this fails, the device is either not available / reachable / is malfunctionning.
Here is my overly complicated code. I am looking how to simplify it.
if let device = self.theDevice
{
device.initDevice()
if (!device.selfTest())
{
self.theDevice = nil;
}
}
I tried to combine all tests with && in the if statement, but it fails.
The issue is that I have 12 of them for various devices at the beginning of a function. It takes a lot of space and is dirty. How can I combine these statements in Swift ?

You can use ? optional chaining, and combine if case let pattern matching and where statement
theDevice?.initDevice()
if case let selfTest? = theDevice?.selfTest() where !selfTest{
theDevice = nil
}
But I would prefer if let optional unwrapping for readability.

You could improve this by making initDevice return self, then you could do the following:
if case let selfTest? = theDevice?.initDevice().selfTest() where !selfTest {
theDevice = nil
}
Another option may be to make initDevice perform the selfTest and return it.
All of this assumes that the initDevice is your code, or you can edit in one way or another.
If it is not editable, you can always make an extension and make a new method in that:
extension TheDeviceClass {
func initAndTest() -> Bool {
initDevice()
return selfTest()
}
}
and then call it using something like this:
if case let selfTest? = theDevice?.initAndTest() where !selfTest {
theDevice = nil
}
But this might make it more complicated than you would want. If you use this code a lot, consider putting it in another method.

I think you should change your MyDevice implementation to make your code less redundant, by defining a failable initializer and do all the checks inside it. Thus if the initialization fails you will get a nil and can just assign it to the relevant property of the other class, and don't even need the method that checks the 12 devices.
class MyDevice {
func selfTest() -> Bool {
// test
return testresult
}
func initDevice() {
// initialization
}
init?() {
self.initDevice()
if !self.selfTest() {
return nil
}
}
}
If you don't like to change your code too much, you may just define a closure to reduce these redundant code, like this (it still looks ugly so I DO NOT recommend you to do this.):
class SomeClassThatHasTwelveDevices {
var theDeviceOne: MyDevice?
var theDeviceTwo: MyDevice?
var theDeviceThree: MyDevice?
var theDeviceFour: MyDevice?
var theDeviceFive: MyDevice?
var theDeviceSix: MyDevice?
var theDeviceSeven: MyDevice?
var theDeviceEight: MyDevice?
var theDeviceNine: MyDevice?
var theDeviceTen: MyDevice?
var theDeviceEleven: MyDevice?
var theDeviceTwelve: MyDevice?
func someMethodThatChecksAllDevices() {
let check: (inout MyDevice?) -> Void = { deviceRef in
if let device = deviceRef {
device.initDevice()
if !device.selfTest() {
deviceRef = nil
}
}
}
check(&theDeviceOne)
check(&theDeviceTwo)
check(&theDeviceThree)
check(&theDeviceFour)
check(&theDeviceFive)
check(&theDeviceSix)
check(&theDeviceSeven)
check(&theDeviceEight)
check(&theDeviceNine)
check(&theDeviceTen)
check(&theDeviceEleven)
check(&theDeviceTwelve)
}
}

Related

Swift Generics and Protocols

Given the below, this will throw a compile time error about the protocol not being able to adhere to itself and only struct/enum can adhere to the protocol. This seems to defeat the purpose of being able to use protocols in generics. I'm trying to understand why this doesn't work, but if I remove the generic and just put the protocol where 'Z' is everything is fine. It seems antithetical to what protocols and generics should be allowed for.
**Edit for question clarity: I need to take a type of Any that can be cast to a dictionary of [String:MyProtocol] and pass it into the method printEm. printEm must use the generic as it will be instantiating instances of the class Z.
protocol MyProtocol {
init()
var whoAmI:String { get }
}
func genericPassing(unknownThing:Any) {
let knownThing = unknownThing as? [String:MyProtocol]
if(knownThing != nil){
self.printEm(knownThing)
}
}
func printEm<Z:MyProtocol>(theThings:[String:Z]) {
let zCollection:[Z] = []
for thing in theThings {
print(thing.whoAmI)
zCollection.append(Z())
}
}
**Edited printEm to illustrate why generic is needed.
** Edit for more complex code. The two primary requirements are the use of a generic to call Z() and the ability to take an Any and somehow type check it and/or cast it so that it can be used in a genericized method.
private func mergeUpdates<Z:RemoteDataSyncable>(source:inout Z, updates:[WritableKeyPath<Z, Any>:Any]) throws {
for key in updates.keys {
let value = updates[key]!
let valueDict = value as? [String:[WritableKeyPath<RemoteDataSyncable, Any>:Any]]
if(valueDict != nil) {
var currentValueArray = source[keyPath: key] as? [RemoteDataSyncable]
if(currentValueArray != nil) {
self.mergeUpdates(source: &currentValueArray!, updates: valueDict!)
}
else {
throw SyncError.TypeError
}
}
else {
source[keyPath: key] = value
}
}
}
private func mergeUpdates<Z:RemoteDataSyncable>(source:inout [Z], updates:[String:[WritableKeyPath<Z,Any>:Any]]) {
for key in updates.keys {
var currentObject = source.first { syncable -> Bool in
return syncable.identifier == key
}
if(currentObject != nil) {
try! self.mergeUpdates(source: &currentObject!, updates: updates[key]!)
}
else {
var newSyncable = Z()
try! self.mergeUpdates(source: &newSyncable, updates: updates[key]!)
source.append(newSyncable)
}
}
}
This is a perfect example of why protocols do not conform to themselves. In your code Z is MyProtocol, so Z() is MyProtocol(). How would that work? What type is it? How big is it? You then can't put them into a [Z], since they might be different types.
You mean to pass arbitrary MyProtocols and call init on the type of each element:
func printEm(theThings:[String: MyProtocol]) {
var zCollection:[MyProtocol] = []
for thing in theThings.values {
print(thing.whoAmI)
zCollection.append(type(of: thing).init())
}
}
When I suggested using closures, this is the kind of thing I mean. This Updater can accept arbitrary ReferenceWritableKeyPaths, and when you pass it Any update value, it'll assign it to every key path that can accept it. That's kind of useless, but shows the technique. (Keep in mind that the updater is retaining the object, so that may be a problem that you need to address.)
class Updater {
private(set) var updaters: [(Any) -> ()] = []
func add<Root, Value>(keyPath: ReferenceWritableKeyPath<Root, Value>, on root: Root) {
updaters.append { value in
if let value = value as? Value {
root[keyPath: keyPath] = value
}
}
}
func updateAll(with value: Any) {
for updater in updaters {
updater(value)
}
}
}
class Client {
var updateMe: Int = 0
}
let client = Client()
let updater = Updater()
updater.add(keyPath: \.updateMe, on: client)
updater.updateAll(with: 3)
client.updateMe // 3
The key lesson in this code is that the generic types on the add are erased (hidden) by the (Any) -> () closure at compile-time. And the runtime as? check is done inside that closure, where the types are all known.

How Can I Match Swift 4 KVO on Non-Objective-C Type?

I have a Result type that I use in asynchronous processes:
internal enum Result<T> {
case success(T)
case failure(Error)
}
I also have a APIDataResultContext that I use to pass data between Operation subclasses:
internal final class APIDataResultContext: NSObject {
// MARK: Properties
private let lock = NSLock()
private var _result: Result<Data>!
internal var result: Result<Data>! {
get {
lock.lock()
let temp = _result
lock.unlock()
return temp
}
set {
lock.lock()
_result = newValue
lock.unlock()
}
}
}
In my unit tests, I need to determine when result has been set in an APIDataResultContext instance. I can't use KVO because my Result<T> type cannot be marked as dynamic since it can't be represented in Objective-C.
I don't know of another way that will allow me to monitor when result is changed other than using a closure property or a Notification, which I would prefer not to do. I will resort to one of the two if necessary, though.
What other way(s) can I monitor for a change of result?
I ended up adding a closure property to APIDataResultContext:
internal final class APIDataResultContext {
// MARK: Properties
internal var resultChanged: (()->())?
private let lock = NSLock()
private var _result: Result<Data>!
internal var result: Result<Data>! {
get {
lock.lock()
let temp = _result
lock.unlock()
return temp
}
set {
lock.lock()
_result = newValue
lock.unlock()
resultChanged?()
}
}
}
I use the closure in my tests to determine when result has been changed:
internal func testNeoWsFeedOperationWithDatesPassesDataToResultContext() {
let operationExpectation = expectation(description: #function)
let testData = DataUtility().data(from: "Hello, world!")
let mockSession = MockURLSession()
let testContext = APIDataResultContext()
testContext.resultChanged = {
operationExpectation.fulfill()
guard let result = testContext.result else {
XCTFail("Expected result")
return
}
switch result {
case .failure(_):
XCTFail("Expected data")
case .success(let data):
XCTAssertEqual(data, testData, "Expected '\(testData)'")
}
}
NeoWsFeedOperation(context: testContext, sessionType: mockSession, apiKey: testAPIKey, startDate: testDate, endDate: testDate).start()
mockSession.completionHandler?(testData, nil, nil)
wait(for: [operationExpectation], timeout: 2)
}
You've already solved this issue (and what you did is probably what I'd do), but there's probably still value in providing a literal answer for the title question: How can you use KVO on a non-Objective-C type?
As it turns out, it's not too difficult to do, although it is somewhat ugly. Basically, you need to create an Objective-C property that is typed Any with the same Objective-C name as the Swift name of the real property. Then, you put willSet and didSet handlers on the real property that call the appropriate KVO methods for the Objective-C property. So, something like:
#objc(result) private var _resultKVO: Any { return self.result }
internal var result: Result<Data>! {
willSet { self.willChangeValue(for: \._resultKVO) }
didSet { self.didChangeValue(for: \._resultKVO) }
}
(For the sake of simplicity, I'm assuming that result is your stored property, and removing the lock and the private property from the equation)
The caveat is that you will have to use _resultKVO instead of result when constructing key paths to observe, which means that if this needs to be observable from outside the object, you can't make _resultKVO private, and you'll have to clutter up your class's interface with it. But so it goes.
Again, I probably wouldn't do this for your particular use case (and if you did, you could obviously fire the notifications in result's set rather than having to bother with willSet and didSet), but in some cases this can be useful, and it's good to have an answer describing how to do it as a reference.

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>
}

Lazy Var vs Let

I want to use Lazy initialization for some of my properties in Swift.
My current code looks like this:
lazy var fontSize : CGFloat = {
if (someCase) {
return CGFloat(30)
} else {
return CGFloat(17)
}
}()
The thing is that once the fontSize is set it will NEVER change.
So I wanted to do something like this:
lazy let fontSize : CGFloat = {
if (someCase) {
return CGFloat(30)
} else {
return CGFloat(17)
}
}()
Which is impossible.
Only this works:
let fontSize : CGFloat = {
if (someCase) {
return CGFloat(30)
} else {
return CGFloat(17)
}
}()
So - I want a property that will be lazy loaded but will never change.
What is the correct way to do that? using let and forget about the lazy init? Or should I use lazy var and forget about the constant nature of the property?
This is the latest scripture from the Xcode 6.3 Beta / Swift 1.2 release notes:
let constants have been generalized to no longer require immediate
initialization. The new rule is that a let constant must be
initialized before use (like a var), and that it may only be
initialized: not reassigned or mutated after initialization.
This enables patterns like:
let x: SomeThing
if condition {
x = foo()
} else {
x = bar()
}
use(x)
which formerly required the use of a var, even though there is no
mutation taking place. (16181314)
Evidently you were not the only person frustrated by this.
Swift book has the following note:
You must always declare a lazy property as a variable (with the var keyword), because its initial value might not be retrieved until after instance initialization completes. Constant properties must always have a value before initialization completes, and therefore cannot be declared as lazy.
This makes sense in the context of implementing the language, because all constant stored properties are computed before initialization of an object has finished. It does not mean that the semantic of let could have been changed when it is used together with lazy, but it has not been done, so var remains the only option with lazy at this point.
As far as the two choice that you presented go, I would decide between them based on efficiency:
If accessing the value of a property is done rarely, and it is expensive to compute upfront, I would use var lazy
If the value is accessed in more than 20..30% of cases or it is relatively inexpensive to compute, I would use let
Note: I would further optimize your code to push the conditional into CGFloat initializer:
let fontSize : CGFloat = CGFloat(someCase ? 30 : 17)
As dasblinkenlight points out lazy properties should always be declared as variables in Swift. However it is possible make the property read-only so it can only be mutated from within the source file that the Entity was defined in. This is the closest I can get to defining a "lazy let".
private(set) lazy var fontSize: CGFloat = {
if someCase {
return 30
} else {
return 17
}
}()
You can use Burritos for lazy constant properties. This library provides different property wrappers for Swift 5.1. Install it with CocoaPods by adding the following line to your Podfile:
pod 'Burritos'
With this library you can replace
lazy var fontSize : CGFloat = {
if (someCase) {
return CGFloat(30)
} else {
return CGFloat(17)
}
}()
with
#LazyConstant var fontSize : CGFloat = {
if (someCase) {
return CGFloat(30)
} else {
return CGFloat(17)
}
}()
And then self.fontSize = 20 leads to compilation error.

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.