100 Days of SwiftUI Learning — Day 11

Krishna
5 min readMay 11, 2022

Agenda

  1. Limit access to internal data using access control.
  2. Static properties and methods.
  3. Summary: Structs
  4. Checkpoint 6

Limit access to internal data using access control

The properties of a struct can be accessed externally which creates loopholes in various applications. Let us consider the below example.

struct BankAccount {
var funds = 0

mutating func deposit(amount: Int) {
funds += amount
}

mutating func withdraw(amount: Int) -> Bool {
if funds > amount {
funds -= amount
return true
} else {
return false
}
}
}
var account = BankAccount()
account.deposit(amount: 100)
let success = account.withdraw(amount: 200)

if success {
print("Withdrew money successfully")
} else {
print("Failed to get the money")
}

In a normal scenario where this properties mean nothing your code does no harm. But, if you are using this say in a banking environment, you could easily access the property funds and get the money directly without the use of methods deposit or withdraw.

Like this account.funds -=1000 . The property funds can be controlled if you know the name of the instance. To solve this problem, we have to make sure that such properties can only be accessible by the methods inside the struct and cannot be accessed directly. This is where private comes into picture private var funds = 0 . This is called as access control.

Let us look at some access control techniques:

  • Use private for “don’t let anything outside the struct use this.”
  • Use fileprivate for “don’t let anything outside the current file use this.”
  • Use public for “let anyone, anywhere use this.”
  • private(set) used in cases where anyone can read the property but only the methods inside the struct can write to it.

If you are declaring multiple properties as private, you may need to create your own initializer.

Static properties and methods

We came across properties and methods that can be used by instances of a struct. But, you can also add properties and methods directly on a struct itself. We do this by using a keyword static.

struct School {
static var studentCount = 0

static func add(student: String) {
print("\(student) joined the school.")
studentCount += 1
}
}
School.add(student: "Taylor Swift")
print(School.studentCount)

This means the property studentCount and method add() don’t exist uniquely on instances of the struct. Also note that even though we writing to the property studentCount using add(), we don’t need to add the keyword mutating.

If you want to mix and match static and non-static properties and methods, there are two rules:

  1. To access non-static code from static code… you’re out of luck: static properties and methods can’t refer to non-static properties and methods because it just doesn’t make sense — which instance of School would you be referring to?
  2. To access static code from non-static code, always use your type’s name such as School.studentCount. You can also use Self to refer to the current type.

Now we have self and Self, and they mean different things: self refers to the current value of the struct, and Self refers to the current type.

struct Employee {
let username: String
let password: String

static let example = Employee(username: "cfederighi", password: "hairforceone")
}

Summary: Structs

  • You can create your own structs by writing struct, giving it a name, then placing the struct’s code inside braces.
  • Structs can have variable and constants (known as properties) and functions (known as methods)
  • If a method tries to modify properties of its struct, you must mark it as mutating.
  • You can store properties in memory, or create computed properties that calculate a value every time they are accessed.
  • We can attach didSet and willSet property observers to properties inside a struct, which is helpful when we need to be sure that some code is always executed when the property changes.
  • Initializers are a bit like specialized functions, and Swift generates one for all structs using their property names.
  • You can create your own custom initializers if you want, but you must always make sure all properties in your struct have a value by the time the initializer finishes, and before you call any other methods.
  • We can use access to mark any properties and methods as being available or unavailable externally, as needed.
  • It’s possible to attach a property or methods directly to a struct, so you can use them without creating an instance of the struct.

Checkpoint 6

Challenge

Create a struct to store information about a car, including its model, number of seats, and current gear, then add a method to change gears up or down. Have a think about variables and access control: what data should be a variable rather than a constant, and what data should be exposed publicly? Should the gear-changing method validate its input somehow?

Solution #1

I have chosen the properties to be public. I cannot think of a case where it needs to be public.

struct Car {
let model : String
let numOfSeats : Int
var currentGear : Int

mutating func gearUp() {
if currentGear < 6 {
currentGear += 1
}
else {
print("Maximum gear reached")
}
}

mutating func gearDown() {
if currentGear < 2 {
print("Minimum gear reached")
}
else{
currentGear -= 1
}
}
}
var mazda = Car(model: "Mazda 3", numOfSeats: 4, currentGear: 2)
print(mazda.currentGear) // Prints 2
mazda.gearUp()
print(mazda.currentGear) //Prints 3
mazda.gearUp()
print(mazda.currentGear) // Prints 4
mazda.gearDown()
print(mazda.currentGear)//Prints 3

Solution #2

For this solution, I have used private for the property currentGear and also created a custom initializer. Additionally, I have also added didSet{} for currentGear property so that we can print out the updated value every time it’s changed.

struct Car {let model : Stringlet numOfSeats : Intprivate var currentGear : Int {didSet {print("The gear is changed to \(currentGear)")}}mutating func gearUp() {if currentGear < 6 {currentGear += 1}else {print("Maximum gear reached")}}mutating func gearDown() {if currentGear < 2 {print("Minimum gear reached")}else{currentGear -= 1}}init(model: String, numOfSeats: Int) {self.model = modelself.numOfSeats = numOfSeatscurrentGear = 1}}var mazda = Car(model: "Mazda 3", numOfSeats: 4)mazda.gearUp()mazda.gearUp()mazda.gearDown()

It is printed as follows:

The gear is changed to 2The gear is changed to 3The gear is changed to 2

That is everything for today. 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