Book Image

Swift 3 Object-Oriented Programming - Second Edition

By : Gaston C. Hillar
Book Image

Swift 3 Object-Oriented Programming - Second Edition

By: Gaston C. Hillar

Overview of this book

Swift has quickly become one of the most-liked languages and developers’ de-facto choice when building applications that target iOS and macOS. In the new version, the Swift team wants to take its adoption to the next level by making it available for new platforms and audiences. This book introduces the object-oriented paradigm and its implementation in the Swift 3 programming language to help you understand how real-world objects can become part of fundamental reusable elements in the code. This book is developed with XCode 8.x and covers all the enhancements included in Swift 3.0. In addition, we teach you to run most of the examples with the Swift REPL available on macOS and Linux, and with a Web-based Swift sandbox developed by IBM capable of running on any web browser, including Windows and mobile devices. You will organize data in blueprints that generate instances. You’ll work with examples so you understand how to encapsulate and hide data by working with properties and access control. Then, you’ll get to grips with complex scenarios where you use instances that belong to more than one blueprint. You’ll discover the power of contract programming and parametric polymorphism. You’ll combine generic code with inheritance and multiple inheritance. Later, you’ll see how to combine functional programming with object-oriented programming and find out how to refactor your existing code for easy maintenance.
Table of Contents (17 chapters)
Swift 3 ObjectOriented Programming - Second Edition
Credits
About the Author
Acknowledgement
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface

Working with API objects in the Xcode Playground


Now, let's forget a bit about geometry, shapes, polygons, perimeters, and areas. We will interact with API objects in the Xcode Playground. You still need to learn many things before we can start creating object-oriented code. However, we will write some code in the Playground to interact with an existing API before we move forward with our journey into the object-oriented programming world.

Note

The following example interacts with an iOS API, and therefore, you cannot run it in Ubuntu or in the web-based IBM Swift Sandbox. However, you will be able to run most of the examples that don't interact with iOS APIs in the forthcoming chapters.

Object-oriented programming is extremely useful when you have to interact with API objects. When Apple launched iOS 8, it introduced a Health app that provided iPhone users access to a dashboard of health and fitness data. The HealthKit framework introduced in the iOS SDK 8 allows app developers to request permissions from the users themselves to read and write specific types of health and fitness data. The framework makes it possible to ask for, create, and save health and fitness data that the users will see summarized in the Health app. This app is still a very important app in iOS 10, and the Apple Watch device in its two versions can generate very useful data for this app.

When we store and query health and fitness data, we have to use the framework to work with the units in which the values are expressed, their conversions, and localizations. For example, let's imagine an app that stores body temperature data without considering the units and their conversions. A value of 39 degrees Celsius (which is equivalent to 102.2 degrees Fahrenheit) in an adult would means that the person's body temperature is higher than normal (that is, they may have a fever). However, a value of 39 degrees Fahrenheit (equivalent to 3.88 degrees Celsius) would mean that the person's body is close to its freezing point. If our app just stores values without considering the related units and user preferences, we can have huge mistakes. If the app just saves 39 degrees and thinks that the user will always display Celsius, it will still display 39 degrees to a user whose settings use Fahrenheit as the default temperature unit. Thus, the app will provide wrong information to the user.

The data in HealthKit is always represented by a double value with an associated simple or complex unit. The units are classified into types, and it is possible to check the compatibility between units before performing conversions. We can work with HealthKit quantities and units in the Swift interactive Playground and understand how simple it is to work with an object-oriented framework. It is important to note that the Playground doesn't allow us to interact with the HealthKit data store. However, we will just play with quantities and units in a few object-oriented snippets.

Start Xcode, navigate to File | New | Playground..., enter a name for Playground, select iOS as the desired platform, click on Next, select the desired location for the Playground file, and click on Create. Xcode will display a Playground window with a line that imports UIKit and creates a string variable. You just need to add the following line to be able to work with quantities and units from the HealthKit framework, as shown in the subsequent screenshot:

    import HealthKit 

Note

Xcode allows us to create playgrounds for any of the following platforms: iOS, Mac OS, and tvOS.

All HealthKit types start with the HK prefix. HKUnit represents a particular unit that can be either simple or complex. Simple units for temperature are degrees Celsius and degrees Fahrenheit. A complex unit for mass/volume is ounces per liter (oz/L). HKUnit supports many standard SI units (Système Internationale d'Unités in French, International System of Units in English) and non-SI units.

Add the following two lines to the Swift Playground and check the results on the right-hand side of the window; you will notice that they generate instances of HKTemperatureUnit. Thus, you created two objects that represent temperature units, as follows. The code file for the sample is included in the swift_3_oop_chapter_01_01 folder:

    let degCUnit = HKUnit.degreeCelsius() 
    let degFUnit = HKUnit.degreeFahrenheit() 

Note

In Swift 2.x, in order to work with the APIs, it was necessary to repeat information many times. Swift 3 reduced the need to repeat information that was obvious, and therefore, we have to write less code to achieve the same goal compared with Swift 2.x. For example, in Swift 2.x, it was necessary to write HKUnit.degreeCelsiusUnit() and HKUnit.degreeFahrenheitUnit(). The HKUnit prefix makes it clear that we are talking about a unit, and therefore, Swift 3 removed the Unit word as a suffix of both HKUnit.degreeCelsiusUnit() and HKUnit.degreeFahrenheitUnit(). As a result, we can write the previously shown code that uses HKUnit.degreeCelsius() and HKUnit.degreeFahrenheit().

However, there are other ways to create objects that represent temperature units. It is also possible to use the HKUnit initializer, which returns the appropriate unit instance from its string representation. For example, the following lines also generate instances of HKTemperatureUnit for degrees in Celsius and Fahrenheit. The code file for the sample is included in the swift_3_oop_chapter_01_01 folder:

    let degCUnitFromStr = HKUnit(from: "degC") 
    let degFUnitFromStr = HKUnit(from: "degF") 

Note

In Swift 2.x, it was necessary to use fromString instead of from to achieve the same goal shown in the previous lines. Swift 3 reduced the code that it is necessary to write to make API calls.

The following lines generate two instances of HKEnergyUnit-one for kilocalories and the other for kilojoules. The code file for the sample is included in the swift_3_oop_chapter_01_01 folder:

    let kiloCaloriesUnit = HKUnit(from: "kcal") 
    let joulesUnit = HKUnit(from: "kJ") 

The next two lines generate two instances of HKMassUnit-one for kilograms and the other for pounds. The code file for the sample is included in the swift_3_oop_chapter_01_01 folder:

    let kiloGramsUnit = HKUnit.gramUnit(with: 
    HKMetricPrefix.kilo) 
    let poundsUnit = HKUnit.pound() 

The next line generates an instance of _HKCompoundUnit because the string specifies a complex unit for mass/volume: ounces per liter (oz/L). The code file for the sample is included in the swift_3_oop_chapter_01_01 folder. The subsequent screenshot shows the results displayed in the Playground:

    let ouncesPerLiter = HKUnit(from: "oz/L") 

HKQuantity encapsulates a quantity value (Double) and the unit of measurement (HKUnit). This class doesn't provide all the operations you might expect to work with quantities and their units of measure, but it allows you to perform some useful compatibility checks and conversions.

The following lines create two HKQuantity instances with temperature units; we name the instances bodyTemperature1 and bodyTemperature2. The former uses degrees Celsius (degCUnit) and the latter degrees Fahrenheit (degFUnit). Then, the code calls the is method with the compatibleWith argument to make sure that each HKQuantity instance can be converted to degrees Fahrenheit (degFUnit). If is returns true, it means that you can convert to HKUnit, which is specified as the compatibleWith argument. We always have to call this method before calling the doubleValue method. This way, we will avoid errors when the units aren't compatible.

The doubleValue method returns the quantity value converted to the unit specified as the for argument. In this case, the two calls make sure that the value is expressed in degrees Fahrenheit, no matter what the temperature unit specified in each HKQuantity instance is. The code file for the sample is included in the swift_3_oop_chapter_01_01 folder. The screenshot that follows the given code shows the results displayed in the Playground:

    let bodyTemperature1 = HKQuantity(unit: degCUnit, 
    doubleValue: 35.2) 
    let bodyTemperature2 = HKQuantity(unit: degFUnit, 
    doubleValue: 95) 
    print(bodyTemperature1.description) 
    print(bodyTemperature2.description) 
 
    if bodyTemperature1.is(compatibleWith: degFUnit) { 
      print("Temperature #1 in Fahrenheit degrees: \
      (bodyTemperature1.doubleValue(for: degFUnit))") 
    } 
 
    if bodyTemperature2.is(compatibleWith: degFUnit) { 
      print("Temperature #2 in Fahrenheit degrees: \
      (bodyTemperature2.doubleValue(for: degFUnit))") 
    } 

The following line shows an example of the code that creates a new HKQuantity instance with a quantity and temperature unit converted from degrees Fahrenheit to degrees Celsius. There is no convert method that acts as a shortcut, so we have to call doubleValue and use it in the HKQuantity initializer, as follows. The code file for the sample is included in the swift_3_oop_chapter_01_01 folder:

    let bodyTemperature2InDegC = HKQuantity(unit: 
      degCUnit, doubleValue: 
    bodyTemperature2.doubleValue(for: degCUnit)) 

The compare method returns a ComparisonResult value that indicates whether the receiver is greater than, equal to, or less than the compatible HKQuantity value specified as an argument. For example, the following lines compare bodyTemperature1 with bodyTemperature2 and print the results of the comparison. Note that it isn't necessary to convert both the HKQuantity instances to the same unit; they just need to be compatible, and the compare method will be able to perform the comparison by making the necessary conversions under the hood. In this case, one of the temperatures is in degrees Celsius and the other is in degrees Fahrenheit. The screenshot that follows the given code shows the results displayed in the Playground:

    let bodyTemperature2InDegC = HKQuantity(unit: 
      degCUnit, doubleValue: 
    bodyTemperature2.doubleValue(for: degCUnit)) 
 
    let comparisonResult = 
    bodyTemperature1.compare(bodyTemperature2) 
    switch comparisonResult { 
      case ComparisonResult.orderedDescending: 
      print("Temperature #1 is greater than #2") 
      case ComparisonResult.orderedAscending: 
      print("Temperature #2 is greater than #1") 
      case ComparisonResult.orderedSame: 
      print("Temperature #1 is equal to Temperature #2") 
    } 
 

Note

In many cases, the APIs removed the NS prefix in Swift 3. In Swift 2.3, the compare method returned an NSComparisonResult value. In Swift 3, the compare method returns a ComparisonResult value. In addition, the APIs in Swift 3 use lowerCamelCase for enumeration values. Therefore, the NSComparisonResult.OrderedDescending value in Swift 2.3 is ComparisonResult.orderedDescending in Swift 3.