This entire series is really nicely done and very informative. One correction to note, computed properties are not read only (Apple docs). Thanks again.
I need help. I’ve been going through your class and struct videos and need clarification.
Here’s my CLASS, with properties, example that maybe you can help with the details.
class List { var firstStringArray:[String] = [ ]
var aStringArray = [“A”,“B”,“C”] var anotherStringArray = [“X”,“Y”,“Z”]
}
If I make an instance of the class: var myList = List()
And I assign: myList.firstListArray = myLIst.aStringArray
Now, I want to reuse and re-assign firstListArray.
I want it to stop pointing or referencing aStringArray and now point at
ONLY anotherStringArray.
How do I accomplish this?
In other words how do I change it’s “reference”?
Thanks,
Gary
PS
Am I better off using a Struct when my properties are initialized
to literal values or use a Class like my example?
In fact, Array are implemented as Struct, so when you assign an array to another one, you create a copy of it.
This can be simply verified with the following code :
var myList = List()
myList.firstStringArray = myList.aStringArray
print(myList.aStringArray) // PRINTS ["A", "B", "C"]
myList.firstStringArray.removeLast(1)
print(myList.aStringArray) // PRINTS ["A", "B", "C"]
The last element of firstStringArray was removed, but since it’s a copy of aStringArray and not a reference, aStringArray isn’t changed.
If you want to use references, you have to encapsulate your array in a “dummy” class, such as :
class arrayReference : CustomStringConvertible
{
var array:[String] = []
convenience init() {
self.init(array: [])
}
init(array:[String])
{
self.array = array
}
var description: String
{
return "\(array)"
}
}
‘You must declare computed properties—including read-only computed properties—as variable properties with the var keyword, because their value is not fixed. The let keyword is only used for constant properties, to indicate that their values cannot be changed once they are set as part of instance initialization.’
I understood the need for declaring read-write Computed Properties as variables.But can you please explain me why we need to declare the read-only computed properties as variables?
If you use a read only computed property, the value returned can change too (even if it cannot explicitly be set), thus you need to use the var keyword.
Simple example :
class CounterClass {
private var count = 0
var numberOfTimesThisVariableWasUsed : Int{
get {
count += 1
return count
}
}
}
let counter = CounterClass()
let value1 = counter.numberOfTimesThisVariableWasUsed
// value1 = 1
let value2 = counter.numberOfTimesThisVariableWasUsed
// value2 = 2
The numberOfTimesThisVariableWasUsed is a read-only computed property, but this value is changed each time you get it. A var keyword is needed since the value can change at runtime.
I was doing the challenge and instead of using if statements I wanted to use a switch statement, but I got errors: “Expression pattern of type ‘Bool’ cannot match values of type ‘Double’”. My code looked like this:
struct FuelTank {
var lowFuel: Bool
var level: Double {
didSet {
let min = 0.0
let max = 1.0
let isLowFuel = 0.1
switch level {
case level > max :
level = max
case level < min :
level = min
case level <= isLowFuel :
lowFuel = true
default :
lowFuel = false
}
}
}
}
A switch statement considers a value and compares it against several possible matching patterns.
The matching patterns must be of the same type of your value. So in your switch, you should use Double values for each case. Instead, you are using Bool :
For example level > max results in a boolean, it’s true or false.
You can use boolean in a if…else control flow :
if level > max {
level = max
} else if level < min {
level = min
} else if level <= isLowFuel {
lowFuel = true
} else {
lowFuel = false
}
There is a switch -way to do so, but I consider it a little ugly :
switch level {
case let x where x > max :
level = max
case let x where x < min :
level = min
case let x where x <= isLowFuel :
lowFuel = true
default:
lowFuel = false
}
Totally unrelated to your question, but you should split your tests in two part, because if you initialise a FuelTank with a value lower than min, then you will not pass it the test which will set lowFuel to true.
Also, didSet is not called during the initialization, so lowFuel will not be be set to true if you pass a value lower than isLowFuel as parameter.
Here is an example of functional FuelTank class :
struct FuelTank {
private static let min = 0.0
private static let max = 1.0
private static let isLowFuel = 0.1
var lowFuel: Bool = false
var level: Double {
didSet {
testLevel()
}
}
private mutating func testLevel() {
if level < FuelTank.min {
level = FuelTank.min
} else if level > FuelTank.max {
level = FuelTank.max
}
lowFuel = (level <= FuelTank.isLowFuel)
}
public init (level : Double){
self.level = level
testLevel()
}
}
Don’t hesitate to ask if you have further questions
Just a comment on the Ub3r H4ck3r Challenge solution: I believe having the students use a stored property for the lowFuel Boolean is a design mistake. Here is my solution:
The solution file requires you to answer the “lowFuel” question when creating a tank, which makes no sense.
var theTank = FuelTank(lowFuel: false, level: 1.0)
Where as with my solution:
var theTank = FuelTank(level: 1.0)
To me a property like 'lowFuel" screams to be a computed property. Is there some design element that I am missing that should have lowFuel be a stored Property?
EDIT: I was thinking you might want to have the lowFuel actively set because you want to “turn-on” a low fuel indicator when this situation happens. However, you could still check the lowFuel computed property with the level didSet and the react if required.
You are right, a computed property could be used here since the value of ‘lowFuel’ is always dependent of the value of ‘level’, and thus cannot be set explicitly.
i’ve been thinking about the solution of the challenge and still can’t figure how to do it. When I checked the solution, i tried initializing a tank var but it seems to not solve the issue.
i believe @sedawk 's answer and @micazeve 's explanation is much more sensible …
Hope this is changed for future version of this tutorial though. (Swift4 and up)
You need to use a didSet property observer which runs just after the level variable’s value is set and change the tank’s fuel level value right after you initialize the tank1 object with the FuelTank structure’s default initialiser like this:
We are in the process of updating the whole video course for Swift 4 and Xcode 9 after all so this thread and topic is closed for further questions and issues regarding it for now until then.