Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

Encapsulation of Data with Properties

Save for later
  • 7 min read
  • 07 Feb 2017

article-image

In this article by Gastón C. Hillar, the author of the book Swift 3 Object Oriented Programming - Second Edition, you will learn about all the elements that might compose a class. We will start organizing data in blueprints that generate instances. We will work with examples to understand how to encapsulate and hide data by working with properties combined with access control. In addition, you will learn about properties, methods, and mutable versus immutable classes.

(For more resources related to this topic, see here.)

Understanding the elements that compose a class

So far, we worked with a very simple class and many instances of this class in the Playground, the Swift REPL and the web-based Swift Sandbox. Now, it is time to dive deep into the different members of a class.

The following list enumerates the most common element types that you can include in a class definition in Swift and their equivalents in other programming languages. We have already worked with a few of these elements:

  • Initializers: This is equivalent to constructors in other programming languages
  • Deinitializer: This is equivalent to destructors in other programming languages
  • Type properties: This is equivalent to class fields or class attributes in other programming languages
  • Type methods: This is equivalent to class methods in other programming languages
  • Subscripts: This is also known as shortcuts
  • Instance properties: This is equivalent to instance fields or instance attributes in other programming languages
  • Instance methods: This is equivalent to instance functions in other programming languages
  • Nested types: These are types that only exist within the class in which we define them

We could access the instance property without any kind of restrictions as a variable within an instance.

However, as it happens sometimes in real-world situations, restrictions are necessary to avoid serious problems. Sometimes, we want to restrict access or transform specific instance properties into read-only attributes. We can combine the restrictions with computed properties that can define getters and/or setters.

Computed properties can define get and or set methods, also known as getters and setters. Setters allow us to control how values are set, that is, these methods are used to change the values of related properties. Getters allow us to control the values that we return when computed properties are accessed. Getters don't change the values of related properties.

Sometimes, all the members of a class share the same attribute, and we don't need to have a specific value for each instance. For example, the superhero types have some profile values, such as the average strength, average running speed, attack power, and defense power. We can define the following type properties to store the values that are shared by all the instances: averageStrength, averageRunningSpeed, attackPower, and defensePower. All the instances have access to the same type properties and their values. However, it is also possible to apply restrictions to their access.

It is also possible to define methods that don't require an instance of a specific class to be called; therefore, you can invoke them by specifying both the class and method names. These methods are known as type methods, operate on a class as a whole, and have access to type properties, but they don't have access to any instance members, such as instance properties or methods, because there is no instance at all. Type methods are useful when you want to include methods related to a class and don't want to generate an instance to call them. Type methods are also known as static or class methods. However, we have to pay attention to the keyword we use to declare type methods in Swift because a type method declared with the static keyword has a different behavior from a type method declared with the class keyword.

Declaring stored properties

When we design classes, we want to make sure that all the necessary data is available to the methods that will operate on this data; therefore, we encapsulate data. However, we just want relevant information to be visible to the users of our classes that will create instances, change values of accessible properties, and call the available methods. Thus, we want to hide or protect some data that is just needed for internal use. We don't want to make accidental changes to sensitive data.

For example, when we create a new instance of any superhero, we can use both its name and birth year as two parameters for the initializer. The initializer sets the values of two properties: name and birthYear. The following lines show a sample code that declares the SuperHero class.

class SuperHero { 
      var name: String 
      var birthYear: Int 
     
      init(name: String, birthYear: Int) { 
        self.name = name 
        self.birthYear = birthYear 
      } 
    }

The next lines create two instances that initialize the values of the two properties and then use the print function to display their values in the Playground:

var antMan = SuperHero(name: "Ant-Man", birthYear: 
    1975) 
    print(antMan.name) 
    print(antMan.birthYear) 
    var ironMan = SuperHero(name: "Iron-Man", birthYear: 
    1982) 
    print(ironMan.name) 
    print(ironMan.birthYear)

The following screenshot shows the results of the declaration of the class and the execution of the lines in the Playground:

encapsulation-data-properties-img-0

The following screenshot shows the results of running the code in the Swift REPL. The REPL displays details about the two instances we just created: antMan and ironMan. The details include the values of the name and birthYear properties:

Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at £15.99/month. Cancel anytime

encapsulation-data-properties-img-1

The following lines show the output that the Swift REPL displays after we create the two SuperHero instances:

antMan: SuperHero = {
      name = "Ant-Man"
      birthYear = 1975
    }
    ironMan: SuperHero = {
      name = "Iron-Man"
      birthYear = 1982
    }

We can read the two lines as follows: the antMan variable holds an instance of SuperHero with its name set to "Ant-Man" and its birthYear set to 1975. The ironMan variable holds an instance of SuperHero with its name set to "Iron-Man" and its birthYear set to 1982.

The following screenshot shows the results of running the code in the web-based IBM Swift Sandbox:

encapsulation-data-properties-img-2

We don't want a user of our SuperHero class to be able to change a superhero's name after an instance is initialized because the name is not supposed to change. There is a simple way to achieve this goal in our previously declared class. We can use the let keyword to define an immutable name stored property of type string instead of using the var keyword. We can also replace the var keyword with let when we define the birthYear stored property because the birth year will never change after we initialize a superhero instance.

The following lines show the new code that declares the SuperHero class with two stored immutable properties: name and birthYear. Note that the initializer code hasn't changed, and it is possible to initialize both the immutable stored properties with the same code:

class SuperHero { 
      let name: String 
      let birthYear: Int 
     
      init(name: String, birthYear: Int) { 
          self.name = name 
          self.birthYear = birthYear 
      } 
    }

Stored immutable properties are also known as stored nonmutating properties.

The next lines create an instance that initializes the values of the two immutable stored properties and then use the print function to display their values in the Playground. Then, the two highlighted lines of code try to assign a new value to both properties and fail to do so because they are immutable properties:

var antMan = SuperHero(name: "Ant-Man", birthYear: 
    1975) 
    print(antMan.name) 
    print(antMan.birthYear) 
 
    antMan.name = "Batman" 
    antMan.birthYear = 1976

The Playground displays the following two error messages for the last two lines, as shown in the next screenshot. We will see similar error messages in the Swift REPL and in the Swift Sandbox:

  • Cannot assign to property: 'name' is a 'let' constant
  • Cannot assign to property: 'birthYear' is a 'let' constant

encapsulation-data-properties-img-3

When we use the let keyword to declare a stored property, we can initialize the property, but it becomes immutable-that is, a constant-after its initialization.

Summary

In this article, you learned about the different members of a class or blueprint. We worked with instance properties, type properties, instance methods, and type methods. We worked with stored properties, getters, setters.

Resources for Article:


Further resources on this subject: