Combining setters, getters, and a related property

Sometimes, we want to have more control over the values that are set to properties and retrieved from them, and we can take advantage of getters and setters to do so. In fact, we can combine a getter and a setter, which generate a computed property and a related property that stores the computed value, and access protection mechanisms to prevent the user from making changes to the related property and force him to always use the computed property.

The superhero's sneakers might change over time. However, we always have to make sure that the sneakers' name is an uppercase string. We can define a sneakers property with a getter method that always converts the string value to an uppercase string and stores it in a private sneakersField property.

Whenever we assign a value to the sneakers property, the setter method is called under the hood with the value to be assigned as an argument. Whenever we specify the sneakers property in any expression, the getter method is called under the hood to retrieve the actual value. The following lines show a new version of the SuperHero class that adds a sneakers calculated property.

  The code file for the sample is included in the swift_3_oop_chapter_03_08 folder:

    import Foundation 
 
    public class SuperHero { 
      public let name: String 
      public var birthYear: Int 
     
      private var sneakersField = "NOT SPECIFIED" 
     
      public var sneakers: String { 
        get { 
          return sneakersField 
        } 
        set { 
          sneakersField = newValue.localizedUppercase 
        } 
      } 
     
      public var age: Int { 
        get { 
          return currentYear - birthYear 
        } 
        set { 
          birthYear = currentYear - newValue 
        } 
      } 
      public var currentYear: Int { 
        let date = Date() 
        let calendar = Calendar.current 
        let year = calendar.component(.year, from: date) 
             
        return year 
      } 
     
      init(name: String, birthYear: Int, sneakers: String) 
      { 
        self.name = name 
        self.birthYear = birthYear 
        self.sneakers = sneakers 
      } 
    } 

The new version of the class is declared as public class; therefore, we declared name, birthYear, and sneakers as public properties. We also declared both the age and currentYear properties as public. This way, when someone creates instances of the SuperHero class outside the source file that declares it, he will be able to access the public members, that is, the public properties we have declared. However, the code declares the sneakersField property as a private property; therefore, only the code included in the SuperHero class will be able to access this property. This way, the sneakersField property will be hidden for those who create instances of the SuperHero class.

Tip

Swift 3 made changes to the meaning of both the private and public access modifiers when compared to previous Swift versions, and introduced new access modifiers: fileprivate and open.

In Swift 3, a class declared with the public access modifier is accessible outside the defining module. However, a class declared as public can only be subclassed in the same module where it is defined. If we want to be able to access and subclass a class outside a module, we must use the new open access modifier. A class declared with the open access modifier in Swift 3 is equivalent to a class declared with the public access modifier in the earlier Swift versions.

When we declare a member of a class with the public access level, the member will be accessible but not overridable outside the defining module. If we want the member to be both accessible and overridable outside of the defining module, we must use the new open access modifier. A member declared with the open access modifier in Swift 3 is equivalent to a member declared with the public access modifier in the earlier Swift versions.

When we declare a member of a class with the private access level, the member will be accessible only within the enclosing declaration, that is, within the class. If we want the member to be accessible only in the defining module, we must use the new fileprivate access modifier. A member declared with the fileprivate access modifier in Swift 3 is equivalent to a member declared with the private access modifier in the previous Swift versions. The private access modifier in Swift 3 is more private than in the previous versions and allows us to declare members that we only want to use within the code of the class that declares them.

When we declare the sneakersField private property, we will specify its initial value as "NOT SPECIFIED" and not declare its type because the type-inference mechanism determines that it is of type String, based on the initial value. The following line of code is equivalent to the second line of code. We used the first line for the declaration to simplify our code and avoid redundancy whenever possible:

    private var sneakersField = "NOT SPECIFIED" 
    private var sneakersField: String = "NOT SPECIFIED" 

Tip

We should take advantage of the type inference mechanism included in Swift as much as possible to reduce unnecessary boilerplate code.

The initializer for the class added a new argument that provides an initial value for the new sneakers property. The next lines create two instances of the SuperHero class, assign a value to the sneakers computed property, and then use the print function to display the value of the property in the Playground. In both cases, we will initialize sneakers with a string that the setter method converts to an uppercase string. Thus, when we print the values returned by the getter method, the Playground will print the uppercase string that is stored in the sneakerField private property. Enter the lines after the code that creates the new version of the SuperHero class. The code file for the sample is included in the swift_3_oop_chapter_03_08 folder:

    var superBoy = SuperHero(name: "Super-Boy", birthYear: 
    2008, sneakers: "Running with Swift 3") 
    print(superBoy.sneakers) 
    var superGirl = SuperHero(name: "Super-Girl", 
    birthYear: 2009, sneakers: "Jumping Super Girl") 
    print(superGirl.sneakers) 

Note the number of times each property's getter and setter methods are executed in the Playground. In this case, the sneakers getter method is executed two times, as shown in the following screenshot:

Combining setters, getters, and a related property

Note

We can combine a property with the getter and setter methods, along with access protection mechanisms and a related property that acts as an underlying field, to have absolute control over how values are set to and retrieved from the underlying field.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.57.16