100 Days of SwiftUI Learning — Day 10

Krishna
5 min readMay 10, 2022

--

Agenda

  1. Create Structs
  2. Compute property values dynamically.
  3. Taking action when a property changes.
  4. Create custom initializers

Create Structs

Structs let us create complex data types with their variables and functions.

struct Album {
let title : String
let artist : String
let year : Int

func printSummary() {
print("\(title),\(artist),\(year)")
}
}

Like other data type names such as Int, Double etc., it is a convention that the name of your struct contains the first letter uppercase and the rest lowercase.

Since, Album is a data type, you can create variables or constants using it.

let red = Album(title: "Red", artist: "Taylor Swift", year: 2012)print(red.title)print(red.self)//Will print the values of red.red.printSummary()

The values of the variables can be changed by the functions in a struct. Let us take an example of an employee trying to apply for vacation and see if they can apply or not.

struct Employee {
let name: String
var vacationDays: Int

mutating func takeVacation(days:Int) {
if vacationDays > days {
vacationDays -= days
}
else {
print("Sorry, you don't have enough vacation days")
}
}
}

Note:

  1. Any function that reads data and manipulates data belonging to a struct should use keyword mutating in front of it.
  2. When creating a variable with a struct that has a mutating function and you intend to use the function to change the data, remember to create it as var and not let .

Good to know

  1. Variables and constants that belong to structs are called properties.
  2. Functions that belong to structs are called methods.
  3. When we create a constant or variable out of a struct, we call that an instance.
  4. When we create instances of structs we do so using an initializer.
  5. When a struct is created, swift will create a method inside the struct called init() .

When creating a struct and you assign a value to any property, that value is taken as the default value. Now, when you are creating an instance, you can either provide a value for the property or swift will take the default value.

For example, when creating the struct Employee, if I mention var vacationDays = 14 , 14 is taken as default value.

let kane = Employee(name:"kane")let kane = Employee(name:"kane",vacationDays:14)

Both the statements mentioned above represent the same thing.

Compute property values dynamically

Structs can have two kinds of property.

  1. Stored property: It is a variable or constant that holds a piece of data inside an instance of the struct.
  2. Computed property: Calculates the value of the property dynamically every time it is accessed.

Let us take the same example as above. It can be written as follows:

struct Employee {
let name: String
var vacationRemaining: Int
}

var archer = Employee(name: "Sterling Archer", vacationRemaining: 14)
archer.vacationRemaining -= 5
print(archer.vacationRemaining)
archer.vacationRemaining -= 3
print(archer.vacationRemaining)

This works fine but we are losing the information regarding how many vacation days are allocated initially. This problem is solved as follows.

struct Employee {
let name: String
var vacationAllocated = 14
var vacationTaken = 0

var vacationRemaining: Int {
vacationAllocated - vacationTaken
}
}
var archer = Employee(name: "Sterling Archer", vacationAllocated: 14)
archer.vacationTaken += 4
print(archer.vacationRemaining)
archer.vacationTaken += 4
print(archer.vacationRemaining)

Now, rather than making vacationRemaining something we can assign directly, we are calculating every time we access it.

Even though we are able to change the value of vacationRemaining by the use of other two properties, we cannot write to it. In order to do this swift uses get and set .

var vacationRemaining: Int {
get {
vacationAllocated - vacationTaken
}

set {
vacationAllocated = vacationTaken + newValue
}
}
var archer = Employee(name: "Sterling Archer", vacationAllocated: 14)
archer.vacationTaken += 4
archer.vacationRemaining = 5
print(archer.vacationAllocated)

By changing this property, we are now able to get the value of vacationRemaining and also change the value of other property. After running the code, you will notice that the value of vacationAllocated is changed to 9.

Taking action when a property changes

Swift lets us create property observers, which are special pieces of code that run when properties change.

They take two forms:

  1. didSet: Observer to run when the property just changed.
  2. willSet: Observer to run before the property changed.

Example to demonstrate the need for property observers.

struct Game {
var score = 0
}

var game = Game()
game.score += 10
print("Score is now \(game.score)")
game.score -= 3
print("Score is now \(game.score)")
game.score += 1

In this block of code, we have created a struct and updating the score a few times and every time the property value is changed, we are printing its new value but not the third time. Which is considered a bug. To fix this, we use didSet .

struct Game {
var score = 0 {
didSet {
print("Score is now \(score)")
}
}
}

var game = Game()
game.score += 10
game.score -= 3
game.score += 1

Now every time the property score is changed, the piece of code inside didSet is executed. Swift also provides us a constant oldValue inside didSet, in case you need to have custom functionality based on what you are changing from.

Similarly, there is another block of code that can be used when property is changed that is willSet . The piece of code inside this block will be executed before changing the value of the property.

struct App {
var contacts = [String]() {
willSet {
print("Current value is: \(contacts)")
print("New value will be: \(newValue)")
}

didSet {
print("There are now \(contacts.count) contacts.")
print("Old value was \(oldValue)")
}
}
}

var app = App()
app.contacts.append("Adrian E")
app.contacts.append("Allen W")
app.contacts.append("Ish S")

The following lines are printed when the code is build and executed.

Current value is: []New value will be: ["Adrian E"]There are now 1 contacts.Old value was []Current value is: ["Adrian E"]New value will be: ["Adrian E", "Allen W"]There are now 2 contacts.Old value was ["Adrian E"]Current value is: ["Adrian E", "Allen W"]New value will be: ["Adrian E", "Allen W", "Ish S"]There are now 3 contacts.Old value was ["Adrian E", "Allen W"]

Create custom initializers

Default initializer for structs

struct Player {
let name: String
let number: Int
}

let player = Player(name: "Megan R", number: 15)

This block of code creates a new Player instance by providing values for its two properties. This is called as memberwise initializer. Which means it accepts each property in the order it was defined.

Custom initializer

Note that when we are writing a custom initializer, all properties must be assigned a value by the end.

struct Player {
let name: String
let number: Int

init(name: String, number: Int) {
self.name = name
self.number = number
}
}

It is same as the previous block of code except, we have written a custom initializer and we can add extra functionality if needed. By the use of self we mean to say that the value of the parameter to the property.

Since this is a custom initializer, we can be flexible about how to set the values of the properties. For example, we can only mention the name of the player and the number can be picked randomly.

struct Player {
let name: String
let number: Int

init(name: String) {
self.name = name
number = Int.random(in: 1...99)
}
}

let player = Player(name: "Megan R")
print(player.number)

That is everything for today’s learning. See you tomorrow.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Krishna
Krishna

No responses yet

Write a response