Advantage of computed properties (gettable ones only) vs. stored properties - swift

I would like to clarify if I understand the following concept correctly.
Assume that my goal is to store the String "Good morning, Mike" into the variable var sayGoodMorningToUser.
The String is composed of two variables, namely
var greeting = "Good morning, "
var username = "Mike"
What difference does it make if I use stored properties vs. computed properties, in other words:
var sayGoodMorningToUserStored = greeting + username
vs.
var sayGoodMorningToUserComputed:String {
return greeting + username
}
The only difference I see between these two approaches is that anyone could change the value of sayGoodMorningToUserStored easily and directly, e.g. by writing
var sayGoodMorningToUserStored = "myNewChangedValue"
whereas the variable sayGoodMorningToUserComputed cannot be modified directly, because it cannot simply be set it to a new String value:
var sayGoodMorningToUserComputed = "Hallo" //this would cause an error
Otherwise I cannot understand why people compute the variable instead of simply writing
var sayGoodMorningToUserStored = greeting + username.
Can anyone explain if I understood it correctly? Or are there also other advantages of computed variables vs. stored ones?
I would like to limit my question to gettable variables only, because discussing settable variables here would go beyond the scope.

The difference in your example is this:
var sayGoodMorningToUserStored = "myNewChangedValue"
and
var sayGoodMorningToUserStored = greeting + username
are set when your class is initialized, whereas this:
var sayGoodMorningToUserComputed:String {
return greeting + username
}
is evaluated every time the property is accessed.
The simple example is a class that has a firstName and lastName, but also want fullName. Using a normal property, every time you update firstName or lastName you will also have to update fullName so it will match. Using a computed property, every time you access fullName you will get the up to date information.

Computed Properties
var sayGoodMorningToUserComputed: String {
return greeting + username
}
sayGoodMorningToUserComputed acts just like a function. If a change has been made to greeting or username, then sayGoodMorningToUserComputed will return an up-to-date result that will be the concatenation of the current values.
You would want to use this if you want to ensure your returned value is computed off the latest values of its dependencies (greeting and username).
In the case that both dependencies are final, then it's very likely that the compiler would optimise this computed property into a stored property, because it knows the dependencies can't change
Stored properties
var sayGoodMorningToUserStored = greeting + username
sayGoodMorningToUserStored is just a variable, with nothing special going on. However, it's only set once, whenever the containing scope is initialized. It's computed once, stored and remains constant until it is overwritten by an external source. As such, if greeting or username changes, there will be no effect on sayGoodMorningToUserStored, because it's been computed from the old values, and stored.
You would want to use this if you want to improve performance by caching the result of a computation whose dependencies are constant.

Related

Trying to assign variable using + swiftUI

I'm creating a math app and am trying to assign the correct answer to a variable. I've tried these two options below
#State private var correctAnswer1: Int {
number1L1 + number2L1
}
With this, I receive the error "Property wrapper cannot be applied to a computed property".
if currentLevel == 1
{
Text(" \(number1L1)")
.font(.system(size:70))
Text("+ \(number2L1)")
.font(.system(size:70))
Text("____")
.font(.system(size:70))
correctAnswer = number1L1 + number2L1
}
With this I receive the error "Type () cannot conform to 'View'"
How do I assign this variable without running into an error?
You really have two questions here. In the first case you have a property that is a computed property.
private var correctAnswer1: Int {
number1L1 + number2L1
}
Says that every time you ask for yourObject.correctAnswer1 the system should compute its value by running the code in the block.
Something a property that is attributed with #State, in contrast, is a box that holds a single value. You don't have a single value, you have a mechanism for computing a value. The compiler complains.
I the second case, you're laying out a View.
When you are working inside the body block of a SwiftUI View you are in a special environment called a ViewBuilder. Within that environment, each expression is expected to return some kind of View. The compiler collects them in a special way and will combine them together to make a user interface. (The environment also allows you some control-flow structures like if statements that are treated specially. If you are really curious you can search the web for information about Swift Result Builders)
In your case, you've included an expression that doesn't return a View in a place where Swift expects each expression to return one and Swift complains.

How to avoid typing incorrect serialization name when updating a property from a database?

I'm using Firestore database and i want to update some fields from a document.
var updateFields = {
"completedByUid": authRepo.getUID(),
"completed": currentTime,
};
return await docRef.update(updateFields);
Instead of typing the properyName manually how can i get the name directly from an object Object, and by this avoiding typing it incorrectly or outdated.
Let's say i have an object like:
#JsonSerializable()
class OrderModel {
String? completedByUid;
String? completed;
}
and i want to get the serialization name something like:
var updateFields = {
OrderModel.completedByUid: authRepo.getUID(),
or
var updateFields = {
order.completedByUid.properyName: authRepo.getUID(),
Normally this would be a use for reflection but that might be overkill.
I think the design pattern you could use is a constant that you use to both set and read the values, like so:
const String ORDER_MODEL_COMPLETED_BY_UID_PROPERTY = "completedByUid";
This doesn't exactly guarantee a match if, for example, some existing data is present and may have a name mismatch.
For an answer based on reflection, see this answer.

Returning variables from Model to other ViewControllers

I am making a weather application. I basically created a class where I will only get data from API and return them as needed. I have variables like cityName, currentWeather etc.
Problem is sometimes API doesn't provide them all so I need to check if they are nil or not before return them. What comes to my mind is set variables like this first:
private var cityNamePrivate: String!
and then
var cityNamePublic: String {
if cityNamePrivate == nil {
//
}
else { return cityNamePrivate }
But as you can as imagine, its very bad because I have a lots of variables. Is there any better logic for this? Should I just return them and check later in wherever I send them?
The problem here is that you will have many variables to deal with. It's not just returning them from the API, it's also dealing with them in your app and perhaps in some processing code.
One solution is to have a big class or struct with many properties. This will work very well, is straightforward to implement but will require lots of repetitive code. Moreover, it will require to change your API and all your code, whenever some new properties are made available by the remote web service.
Another approach is to have replace the big inflexible class or struct, with a dynamic container, e.g. an array of items [ Item ] or a dictionary that associates names to items [ String : Item ]. If the data is just strings, it's straightforward. If it's several types, you may have to have to implement a small type system for the elements. Example:
var x : [ String: String] = [:]
x["city"]="Strasbourg"
x["temperature"]="34°C"
struct Item {
var name : String
var value : String
}
var y : [ Item ] = [Item(name:"city",value:"Strasbourg"),
Item(name:"temperature", value:"34°C")]
The other advantage of this approach is that you stay loyal to the semantics: an information that is not available in the API (e.g. "unknown") is not the same as a default value. I.e. if the weather API does not return a temperature, you will not display 0 in your app. because 0 is not the same as "unknown". While strings are more robust in this matter, the absence of a name is not the same as an empty name.
This last remark suggests that in your current scheme, of having a big data transfer object, you should consider to keep the properties as optional, and move the responsibility for the processing of unknown data to your app.

Trouble understanding private attributes in classes and the class property method in Python 3

This class example was taken from here.
class Celsius:
def __init__(self, temperature = 0):
self.temperature = temperature
def to_fahrenheit(self):
return (self.temperature * 1.8) + 32
def get_temperature(self):
print("Getting value")
return self._temperature
def set_temperature(self, value):
if value < -273:
raise ValueError("Temperature below -273 is not possible")
print("Setting value")
self._temperature = value
temperature = property(get_temperature, set_temperature)
The idea here is that when we create an instance of Celsius and set the temperature attribute (e.g. foo = Celsus (-1000) ), we want to make sure that the attribute is not less than -273 BEFORE setting the temperature attribute.
I don't understand how it seems to bypass self.temperature = temperature and go straight to the last line. It seems to me like there are three attributes/properties created here: the Class attribute, temperature; the Instance attribute, temperature; and the set_temperature function which sets the attribute _temperature.
What I DO understand is that the last line (the assignment statement) must run the code property(get_temperature, set_temperature) which runs the functions get_temperature and set_temperature and intern sets the private attribute/property _temperature.
Moreover, if I run: foo = Celsius(100) and then foo.temperature, how is the result of foo.temperature coming from temperature = property(get_temperature, set_temperature) and thus _temperature AND NOT self.temperature = temperature? Why even have self.temperature = temperature if temperature = property(get_temperature, set_temperature) gets ran every time the foo.temperature call is made?
More questions...
Why do we have two attributes with the same name (e.g. temperature) and how does the code know to retrieve the value of _temperature when foo.temperature is called?
Why do we need private attributes/properties an not just temperature?
How does set_temperature(self, value) obtain the attribute for parameter value (e.g. the argument that replaces value)?
In short, please explain this to me like a three year old since I have only been programming a few months. Thank you in advance!
When we are first taught about classes/objects/attributes we are often told something like this:
When you look up an attribute like x.foo it first looks to see if
'foo' is an instance variable and returns it, if not it checks if
'foo' is defined in the class of x and returns that, otherwise an
AttributeError is raised.
This describes what happens most of the time but does not leave room for descriptors. So if you currently think the above is all there is about attribute lookup property and other descriptors will seem like an exception to these rules.
A descriptor basically defines what to do when looking up / setting an attribute of some instance, property is an implementation that lets you define your own functions to call when getting / setting / deleting an attribute.
When you do temperature = property(get_temperature, set_temperature) you are specifying that when x.temperature is retrieved it should call x.get_temperature() and the return value of that call will be what x.temperature evaluates to.
by specifying set_temperature as the setter of the property it states that when ever x.temperature is assigned to something it should call set_temperature with the value assigned as an argument.
I'd recommend you try stepping through your code in pythontutor, it will show you exactly when get_temerature and set_temperature are called after which statements.

Dynamic property names in VBA

I have a custom class module in VBA (Access) that is supposed to handle a large amount of external data. Currently I have two functions Read(name) and Write(name, value) that allows to read and set dynamic properties.
Is there a way to define a more syntactic way to read and write those data? I know that some objects in VBA have a special way of accessing data, for example the RecordSet, which allows to read and set data using myRS!property_name. Is there a way to do exactly the same for custom class modules?
The exclamation mark syntax is used to access members of a Scripting.Dictionary instance(you'll need to add a reference to Microsoft Scripting Runtime through Tools > References first). To use this syntaxyou'll need to be storing the information internally in a dictionary.
The quickest way to use it in a class is to give your class an object variable of type Scripting.Dictionary and set it up as follows:
Option Explicit
Dim d As Scripting.Dictionary
Private Sub Class_Initialize()
Set d = New Scripting.Dictionary
End Sub
Private Sub Class_Terminate()
Set d = Nothing
End Sub
Public Property Get IntData() As Scripting.Dictionary
Set IntData = d
End Property
Now you can access properties using myinstance.IntData!MyProperty = 1... but to get to where you want to be you need to use Charlie Pearson's technique for making IntData the default member for your class.
Once that's done, you can use the following syntax:
Dim m As MyClass
Set m = New MyClass
Debug.Print "Age = " & m!Age ' prints: Age =
m!Age = 27
Debug.Print "Age = " & m!Age ' prints: Age = 27
Set m = Nothing
Okay, thanks to Alain and KyleNZ I have now found a working way to do this, without having a collection or enumerable object below.
Basically, thanks to the name of the ! operator, I found out, that access via the bang/pling operator is equivalent to accessing the default member of an object. If the property Value is the default member of my class module, then there are three equivalent statements to access that property:
obj.Value("param")
obj("param")
obj!param
So to make a short syntax working for a custom class module, all one has to do is to define a default member. For example, I now used the following Value property:
Property Get Value(name As String) As String
Value = SomeLookupInMyXMLDocument(name)
End Property
Property Let Value(name As String, val As String) As String
SetSomeNodeValueInMyXMLDocument(name, val)
End Property
Normally, you could now access that like this:
obj.Value("foo") = "New value"
MsgBox obj.Value("foo")
Now to make that property the default member, you have to add a line to the Property definition:
Attribute Value.VB_UserMemId = 0
So, I end up with this:
Property Get Value(name As String) As String
Attribute Value.VB_UserMemId = 0
Value = SomeLookupInMyXMLDocument(name)
End Property
Property Let Value(name As String, val As String) As String
Attribute Value.VB_UserMemId = 0
SetSomeNodeValueInMyXMLDocument(name, val)
End Property
And after that, this works and equivalent to the code shown above:
obj("foo") = "New value"
MsgBox obj("foo")
' As well as
obj!foo = "New value"
MsgBox obj!foo
' Or for more complex `name` entries (i.e. with invalid identifier symbols)
obj![foo] = "New value"
MsgBox obj![foo]
Note that you have to add the Attribute Value.VB_UserMemId = 0 in some other editor than the VBA editor that ships with Microsoft Office, as that one hides Attribute directives for some reason.. You can easily export the module, open it in notepad, add the directives, and import it back in the VBA editor. As long as you don't change too much with the default member, the directive should not be removed (just make sure you check from time to time in an external editor).
See this other question: Bang Notation and Dot Notation in VBA and MS-Access
The bang operator (!) is shorthand for
accessing members of a Collection or
other enumerable object
If you make your class extend the Collection class in VBA then you should be able to take advantage of those operators. In the following question is an example of a user who extended the collection class:
Extend Collections Class VBA