Difference between variables - swift

Currently I'm trying to learn Swift. But I don't understand the difference between
var a : String {return "some text"}
and
var b : String = "some text"
What's the first code example for and when do I use it?

The second variable var b is declared as a stored property:
In its simplest form, a stored property is a constant or variable that
is stored as part of an instance of a particular class or structure.
Stored properties can be either variable stored properties (introduced
by the var keyword) or constant stored properties (introduced by the
let keyword).
You could consider it as the default way for declaring properties.
The first variable var a is declared as a computed property:
In addition to stored properties, classes, structures, and
enumerations can define computed properties, which do not actually
store a value. Instead, they provide a getter and an optional setter
to retrieve and set other properties and values indirectly.
You should declare a computed property when you need to edit the value of a stored property or even getting a new type based on another stored property.
Example:
struct MyStruct {
// stored properties
var var1: Int
var var2: Int
// comupted properties
var multiplication: Int {
return var1 * var2
}
var result: String {
return "result is: \(multiplication)"
}
}
Keep in mind that computed properties do not store the value, instead it just acts like a regular function that returns a value of a type.
Also, you could treat the computed property as a getter-setter for your -private- stored properties, example:
struct AccessControlStruct {
private var stored: String
var computed: String {
get {
return stored
}
set {
stored = newValue.trimmingCharacters(in: .whitespaces)
}
}
}
Since stored declared as private, the only way is to access from out of the structure scope is by setting/getting its value by talking to computed. Obviously, you could any desired edit to the value before setting/getting it to/from stored, as an example, I am letting the newValue string of the computed to be trimmed before setting it to stored, it is also possible to edit the value before getting it.
Reference:
For more information, I would suggest to review:
The Swift Programming Language - Properties.

The first declaration is what is being called a computed property. You can use it if the valuable’s value is the result of a calculation. In your example, however, it doesn’t make sense to use one. Here’s an example that should give you the idea:
var fullName: String { return firstName + " " + lastName }

Related

Swift 5 storing and passing KeyPaths

Let's say I have the following class:
class User: NSObject {
var name = "Fred"
var age = 24
var email = "fred#freddy.com"
var married = false
}
I want to be able to write a generic function that takes in a list of KeyPaths for a known class type, read the values and print to screen. The problem is, the I can't get the following code to compile as the type of the KeyPath's Value is not known, and will be different for each time. What do I have to do to make this work generically?
Consider the following:
struct KeyPathProperties<T> {
var name: String
var relatedKeyPaths: [KeyPath<T, Any>]
}
extension KeyPath where Root == User {
var properties: KeyPathProperties<Root> {
switch self {
case \Root.name:
return KeyPathProperties(name: "name", relatedKeyPaths: [\Root.age, \Root.email])
default:
fatalError("Unknown key path")
}
}
}
This line fails to compile:
return KeyPathProperties(name: "name", relatedKeyPaths: [\Root.age, \Root.email])
with this error:
Cannot convert value of type 'KeyPath<User, Int>' to expected element type 'KeyPath<User, Any>'
This is what I wish to be able to do, for instance:
let myUser = User()
var keyPathProps = KeyPathProperties(name: "name", relatedKeyPaths: [\User.age, \User.email])
for keyPath in props.relatedKeyPaths {
print("Value: \(myUser[keyPath: keyPath])")
}
The above won't compile of course. Essentially I want to store keyPaths in an array at runtime, so I can generically at some point in time get values out of the User. I need to know if I can re-write the above in some way where the compiler can safely and correctly determine the type of the keyPath's value at runtime.
This is a conceptual use case for a much more complex architectural issue I'm trying to solve with hopefully less code.
MORE INFORMATION:
At runtime I wish to keep track of the properties that get modified - these properties are held in a modifiedProps array in each object / instance. At some point at runtime, I wish to be able to enumerate over this array of KeyPaths and print their values like so:
for modifiedKeyPath in self.modifiedProps {
print ("\(self[keyPath: modifiedKeyPath])"
}
In short - I need to be able to capture the generic type of the KeyPath within KeyPathProperties. How do I achieve this?
SIDE NOTE: I can already easily achieve this by using Swift 3 style string based KeyPaths (by adding #objc to the class properties). I can store an array of keyPaths as strings and later do:
let someKeyPath = #keyPath(User.email)
...
myUser.value(forKeyPath: someKeyPath)
I just cannot do this with Swift 4 KeyPaths generically.
The error tells you what your misconception is:
Cannot convert value of type 'KeyPath<User, Int>'
to expected element type 'KeyPath<User, Any>'
You seem to think that you can use a KeyPath<User, Int> where a KeyPath<User, Any> is expected, ostensibly on the grounds that an Int is an Any. But that's not true. These are generic types, and generic types are not covariant — that is, there is no substitution principle for generics based on their parameterized types. The two types are effectively unrelated.
If you need an array of key paths regardless of their parameterized types, you would need an array of PartialKeyPath or AnyKeyPath. It seems that in your use case the root object is the same throughout, so presumably you want PartialKeyPath.

Conditional Defining a property in model object

I have a model object and there are some properties in that object. Based on some conditions, I want a property to be defined there or not to be defined. For example, this property is my app version.
class Person {
var name: String
var address: String
var age: String
// I want some condition here like if myAppVersion > 1.0 then add isChild
// property to my model object other wise don't add that
var isChild: Bool
// Normal property again
var gender: String
}
I want this behaviour because the properties are coming from the backend and all these properties are required, so if, for some reason, the BE doesn't send the a required property which the client is expecting, then I will crash. These properties have to be mandatory and not optional.
Don't do this.
Declare your parameter as an optional and set it to nil if you don't want it to have a value. You should create two separate classes if you want to have different implementations, but that would be pretty superfluous for just one little change.
If your application crashes just because a property has a nil value, you should really take a look at optional handling in Swift and nullability in Objective-C.

What's the point of READ-only variables when you have LET?

For example:
var dogName : String {
return "Buster"
}
VS..
let dogName = "Buster"
Let's say we're declaring each of these at the top level of a class as instance properties. Are these just two ways of doing the same thing? If not, what's the point of having a read-only variable?
Thanks
Let me try to sum up what the other answers are saying while also adding missing information that I think is critical in order to understand this.
Properties
Properties are simply values that are associated with an object and may be queried in a trivial amount of time without the need (or ability) for parameters like methods have.
Stored Properties
When you create a stored property, whether with let or var, the value assigned at any given point in time will be stored in memory, which is why it is called a stored property.
var name = "Matt"
For variables using var, the value is stored in memory in a way that makes it mutable (editable). You can reassign the value at will and it will replace the previous value stored in memory.
let name = "Matt"
For constants using let, the value is also stored in memory, but in such a way that it may not be changed after the first time assigning to it.
Computed Properties
Computed properties are not stored in memory. As ganzogo says in the comments, computed properties act similarly to methods, but do not take parameters. When deciding when to use a computed property or a function with no parameters, the Swift API Design Guidelines recommend using a computed property when it will simply create or fetch, and then return the value, provided that this takes a trivial amount of time.
var fullName: String {
return firstName + lastName
}
Here, we assume that firstName and lastName are already properties on the object. There is no sense of initialization with this property because it is not stored anywhere. It is fetched on demand every time. That is why there is no sense to doing anything like the following:
var dogName : String {
return "Buster"
}
This has no benefit over a stored property except that no memory will be used in storing the String "Buster".
In fact, this is a simplified version of computed properties. You will notice that the Swift Language Guide describes the use of both get and set in a computed property. set allows you to update the state of other variables when one sets a computed variable. For example:
var stored: Int
var computed: Int {
get {
return stored + 5
}
set {
stored = newValue - 5
}
}
Some useful applications of this were pointed out by Rajan's answer, for example getting and setting volume from width, height, and depth.
A read-only computed var is just a computed var which specifies only a getter, in which case the get keyword and brackets are not required.
Read-Only for Access Control
When developing modules such as frameworks, it is often useful to have a variable only be modifiable from within that object or framework and have it be read-only to the public.
private var modifiableItem: String
public var item: String {
return modifiableItem
}
The idea here is that modifiableItem should only be mutable from within the object that defined it. The private keyword ensures that it is only accessible within the scope of the object that created it and making it a var ensures that it may be modified. The public var item, then, is a computed variable that is exposed to the public that enables anyone to read, but not mutate the variable.
As Hamish notes in the comments, this is more concisely expressible by using private(set):
public private(set) var item: String
This is probably the best way to go about it, but the previous code (using a private stored property and public computed one) demonstrates the effect.
let dogName = "Buster"
means that the dogName variable can't be changed later on once assigned "Buster" and it becomes constant
var dogName : String {
return "Buster"
}
It is a computed read only property where you can have some calculation which can be changed as it is a var but in a way defined below:
The computed property can be changed like
var dogName : String {
return "Stress"+"Buster"
}
Consider this example from Apple Docs
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
It will print
// Prints "the volume of fourByFiveByTwo is 40.0"
Here the volume is calculated when you initialize the object of struct Cuboid and is computed at run time. If it was let, then you have to initialize it before using by some constant.
If you want to read more about it, read the Computed Properties section here
In your example, they are 2 ways of doing the same thing. However, you can do a lot more with a computed property. For example:
var dogName: String {
return firstName + " " + lastName
}
Here, firstName and lastName might not be known at initialization time. This is not possible to do with a simple let property.
It might help you to think of a computed property as a method with no parameters.
A read-only property in a class/struct means that you can't change the value of the property for that instance of the class/struct. It prevents me from doing:
someObject.dogName = "Buddy" // This fails - read-only property
However, I can still do this:
var someVariable = someObject.dogName // someVariable now is "Buster"
someVariable = "Buddy" // This is OK, it's now "Buddy"
A let constant means you won't be changing the value of that specific constant in that block of code.
let someName = "Buster"
someName = "Buddy" // This fails - someName is a constant
There are two different cases:
1) Value type:
struct DogValueType {
var name: String
}
let dog1 = DogValueType(name: "Buster")
var dog2: DogValueType {
return DogValueType(name: "Buster")
}
let dog3: DogValueType = {
return DogValueType(name: "Buster")
}()
dog1 - dog3 can't be changed or mutated
dog1 & dog3 stores value
dog3 computes value each time you accessing it
2) Reference type:
class DogReferenceType {
var name: String
init(name: String) {
self.name = name
}
}
let dog4 = DogReferenceType(name: "Buster")
var dog5: DogReferenceType {
return DogReferenceType(name: "Buster")
}
let dog6: DogReferenceType = {
return DogReferenceType(name: "Buster")
}()
dog4 - dog6 can't be changed, but can be mutated
dog4 & dog6 stores reference to an object.
dog5 creates object each time you accessing it

Swift: get the compile time name of variable (referencing to a class)

Is there a way to get the compile time name of a variable in Swift 2?
I mean the first variable name, which references to a new class instance, if any.
Here is a simple example:
public class Parameter : FloatLiteralConvertible {
var name:String?
var value:Double
// init from float literal
public required init (floatLiteral value: FloatLiteralType) {
self.value = Double(value)
self.name = getLiteralName()
}
func getLiteralName () -> String {
var literalName:String = ""
// do some magic to return the name
return literalName
}
}
let x:Parameter = 2.0
print(x.value) // this returns "2.0"
print(x.name!) // I want this to return "x"
I've already checked similar questions on that topic handling mirroring or objective-c reflections. But in all those cases, one can get only the property names in a class - in the example above name and value.
The same question has been asked in 2014 - Swift: Get Variable Actual Name as String
- and I hope, that since then there is a solution in swift 2.
No, there is no way to do that.
You have to understand that in the compiled state that variable usually does not exist. It can be optimized out or it is represented only as an item on the execution stack.
Even in languages with much better reflection that Swift has, usually you cannot inspect local variables.
To be honest, getting the name of a local variable dynamically has no practical use case.

Return from initializer without initializing all stored properties error

import UIKit
enum DeviceType {
case Phone, Tablet, Watch
}
enum OperatingSystemType {
case iOS, Android, Windows
}
struct OperatingSystemVersion {
var Major: Int
var Minor: Int
var Patch: Int
}
struct OperatingSystem{
var type: OperatingSystemType
var version: OperatingSystemVersion
}
class Device {
var DeviceID: Int
var Type: DeviceType
var Operating_System: OperatingSystem
var UserID: Int
var Description: String
var InventoryNR: String
init () {
DeviceID = 1233
UserID = 2
Description = "took"
InventoryNR = "no17"
}
}
// I can't seem to get past this. I just want to create 2 enums, 2 structs and 2 classes
Device - Class
Device Id - Integer
Type - DeviceType
Operating System - OperatingSystem
User Id - Int
Description - String
Inventory Number - String
The error says you returned from init without initializing all stored properties. That's what the problem is. You need to initialize Type and OperatingSystem in init:
init () {
DeviceID = 1233
Type = .Phone
Operating_System = OperatingSystem(type: .iOS, version: OperatingSystemVersion(Major: 9, Minor: 0, Patch: 2))
UserID = 2
Description = "took"
InventoryNR = "no17"
}
In the future, please read the error messages before posting.
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html
Initialization
Initialization is the process of preparing an instance
of a class, structure, or enumeration for use. This process involves
setting an initial value for each stored property on that instance and
performing any other setup or initialization that is required before
the new instance is ready for use.
You implement this initialization process by defining initializers,
which are like special methods that can be called to create a new
instance of a particular type. Unlike Objective-C initializers, Swift
initializers do not return a value. Their primary role is to ensure
that new instances of a type are correctly initialized before they are
used for the first time.
Instances of class types can also implement a deinitializer, which
performs any custom cleanup just before an instance of that class is
deallocated. For more information about deinitializers, see
Deinitialization.
Setting Initial Values for Stored Properties
Classes and structures must set all of their stored properties to an
appropriate initial value by the time an instance of that class or
structure is created. Stored properties cannot be left in an
indeterminate state.
You can set an initial value for a stored property within an
initializer, or by assigning a default property value as part of the
property’s definition. These actions are described in the following
sections.
NOTE
When you assign a default value to a stored property, or set its
initial value within an initializer, the value of that property is set
directly, without calling any property observers.
Initializers
Initializers are called to create a new instance of a particular type.
In its simplest form, an initializer is like an instance method with
no parameters, written using the init keyword:
init() {
// perform some initialization here
}
The example below defines a new structure called Fahrenheit to store temperatures expressed in the
Fahrenheit scale. The Fahrenheit structure has one stored property,
temperature, which is of type Double:
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit") // prints "The default temperature is 32.0° Fahrenheit"
The structure defines a single initializer, init, with no parameters, which initializes the stored temperature with a
value of 32.0 (the freezing point of water when expressed in the
Fahrenheit scale).
Default Property Values
You can set the initial value of a stored property from within an
initializer, as shown above. Alternatively, specify a default property
value as part of the property’s declaration. You specify a default
property value by assigning an initial value to the property when it
is defined.
NOTE
If a property always takes the same initial value, provide a default
value rather than setting a value within an initializer. The end
result is the same, but the default value ties the property’s
initialization more closely to its declaration. It makes for shorter,
clearer initializers and enables you to infer the type of the property
from its default value. The default value also makes it easier for you
to take advantage of default initializers and initializer inheritance,
as described later in this chapter.
You can write the Fahrenheit structure from above in a simpler form by
providing a default value for its temperature property at the point
that the property is declared:
struct Fahrenheit {
var temperature = 32.0
}