Chapter    2

Getting Started with Swift

Ahmed Bakir

As it is one of the newest programming languages on the market, one would be remiss to start a Swift book without offering the reader a brief introduction to the language. Readers who already feel comfortable with Swift can skip this chapter entirely or just reference the sections they are curious about.

Immediately upon its announcement at the 2014 Apple Worldwide Developers Conference (WWDC), Swift became one of the fastest adopted programming languages in recent history. To help aid this rapid acceptance, Apple designed the language to be fully compatible with existing Objective-C code and all of Cocoa/Cocoa Touch (the frameworks you use to build OS X and iOS apps).

In the past year, however, the sudden appearance of Swift has presented two problems: it has been hard to find good information on how the language works and it has been a grueling process to keep up with Apple’s updates to the language. In addition to being one of the fastest adopted languages, Swift has also been one of the most rapidly changing. Since June 2014, the Apple developer community has seen four major revisions of the language, Swift 1.0, 1.1, 1.2, and 2.0. With each of the language updates comes a revision to Cocoa Touch, meaning you need to modify your code to resolve compilation errors. Imagine waking up one day and seeing that your once functional program no longer compiles.

To help you hit the ground running with this book, I will briefly review Swift syntax, how the language implements core programming concepts like object-oriented programming, and workflow tasks like integrating Objective-C into your projects. In this chapter, I will pull in knowledge from Swift projects I have consulted on, focusing on language features that have proved particularly troublesome for my clients, including optionals and try-catch blocks.

For a more complete description of the language, I highly recommend Beginning iPhone Development with Swift: Exploring the iOS SDK by Mark,Topley, Nutting, Olsson and LeMarche (Apress, 2016). The authors intend their book to be a complete guide to programming Swift via Cocoa Touch tutorials. This book assumes you have mastered those concepts and are ready to dive headfirst into Internet of Things concepts using Cocoa Touch. You can also download Apple’s own guide to the language, The Swift Programming Language from the iBooks Store.

Note  This book is written for and tested against Swift 2.1 on XCode 7.1/OS X 10.11 “El Capitan.”

Why Use Swift?

Following “Should I learn to program in iOS or Android?,” the most common question I receive these days is “Should I program in Objective-C or Swift?” The short answer to both is to pick the platform or language you are more comfortable with and stick with that.

Created with the goal of being “Objective-C without the C,” Swift was designed to allow a wider audience of developers, eschewing the message-passing syntax of Objective-C (those annoying square brackets) and the heavy dependence on understanding advanced concepts from C, such as pointers. If you are coming from Java and Python, many features of Swift will seem very familiar to you, such as dot-syntax for calling methods and try-catch blocks. This was an intentional design decision. I am currently teaching an Objective-C course and the number one complaint from my students is Objective-C’s square brackets; they are a derivative of Smalltalk, a ground-breaking language that never received wide adoption outside advanced programming courses but remains in use today. I tell people it takes a couple of weeks to “get over them,” but they remain daunting. For many people, the dot-syntax alone is a compelling reason to use Swift.

Since OS X and iOS are the only modern platforms that use Objective-C, it is often hard for people to differentiate concepts that come from Objective-C and concepts that come from Cocoa [Touch]. To help with that, at the 2015 WWDC, Apple announced that it is open-sourcing Swift, in hopes of encouraging its wider adoption in education and non-Apple platforms, such as Linux. The extra data points should prove extremely valuable when trying to understand core programming concepts.

One of the most compelling reasons to develop for an Apple platform is the extremely prolific developer community, which has produced a wealth of open source libraries and a massive history of solutions to common bugs. For this reason, Apple was eager to announce that Swift was fully compatible with all of Cocoa Touch and existing Objective-C code when Swift first came. As mentioned in the introduction, I can validate that this claim is true, but you may end up updating your code to keep up with changes in Swift. This may seem discouraging, but remember, you are programming for a closed platform; this is always the case for major updates. Before anyone knew about Swift, many of us were shuffling to make our old apps work in the drastically updated iOS7.

What I have noticed is that for most people, neither Swift nor Objective-C is the major hurdle to learning iOS development. Nine times out of ten, it is learning Cocoa Touch. Apple’s APIs (application programming interfaces) have a very “descriptive” nature, where they generally describe every parameter in the method name. For many beginners, this makes it hard to guess where/how to get started. The good news is, Apple has backported its documentation and provides complete API documentation for every method in Cocoa Touch in both Swift and Objective-C. Most of the problems you will run into will not be about the syntax of a “for” loop; instead they will be about what set of Cocoa Touch methods you call to perform the behaviors you need, and how you need to configure them. Swift and Objective-C are just tools for accomplishing that; pick the one you like using more.

Basic Swift Syntax

In this section, I briefly introduce several basic Swift syntax concepts to help you become familiar with the language and the following examples. As many of you are coming from Objective-C, I will prefix each Swift example with the corresponding Objective-C code. These examples are not meant to be part of any project, so take them as short, working examples. As mentioned in the introduction, all of these examples are written against the standard for Swift 2.0.

Calling Methods (Hello World)

In Objective-C, to print “Hello World” on the console, you would use the NSLog() method: NSLog(@"Hello World");

In Swift, the equivalent is as follows:

print("Hello World")

You should notice a couple of big differences:

No semicolons: Swift uses the newline character to separate lines.

No @ symbol: Swift has its own String class, which is implemented as a simple character array. In Objective-C, the @ symbol served as a literal, a shortcut for creating NSString objects without calling its constructor.

print: Swift uses this familiar method name from C and Java to allow you to print a line directly to the console.

Calling a method with multiple parameters in Objective-C looks as follows:

NSInteger finalSum = [self calculateSumOf:5 andValue:6];

Self represents the receiver (the object that is “receiving” the message”)

CalculateSumOf: represents the method name

5 represents the first parameter that is being passed

andValue represents the second parameter’s label

6 represents the second parameter that is being passed.

In Swift, you call methods with multiple parameters by adding them to a comma-delimited list within the parentheses (as you would in C or Java):

var finalSum = calculateSum(5, 6)

If the method defines labels for its parameters, add the labels in front of the values:

var finalSum = calculateSum(firstValue: 5, secondValue: 6)

Note  Unless specifically required by a method, you can omit the first parameter’s label.

Defining Variables

After “Hello World,” every programming lesson must continue with a discussion of variables. In Objective-C, you created variables by declaring the type, variable, and value:

NSInteger count = 5;

In Swift, you declare variables by specifying their name and mutability (var or let), and optionally:

the data type and initialization value:

var count : Int = 5

All variables defined with var in Swift are mutable, meaning you can change their values at runtime without re-initializing the variable—for example, NSMutableString or NSMutableArray.

The let keyword is similar to const in Objective-C and C; this is a constant value that is not meant to change.

Swift infers data types, so if you prefer, you can omit the type from your declaration:

var count = 5

Swift also allows you to store float (decimal) and Boolean values in variables directly.

To store a float in Objective-C:

float average = 2.4;

To store a float in Swift:

var average : float = 2.4

You can use Double or Long to specify the size of the variable, just as in Objective-C

To store a Boolean in Objective-C:

BOOL isInitialized = YES;  // or NO

To store a Boolean in Swift:

var  isInitialized : Bool = true  // or false

Custom types follow the same rules in Objective-C and Swift as other types: insert the type’s name before the variable name:

In Objective-C:

MyType myVariable;
myVariable.name = @"Bob"; //name is a property of the type

In Swift:

var myVariable : MyType
MyVariable.name = "Bob"

Objects require a slightly different process for initialization, which I will cover in the section “Object-Oriented Programming in Swift.”

Compound Data Types

In most programming languages, like Java and Objective-C, the only data types provided out of the box are primitives for storing values like integers, Booleans, and decimal values directly in memory. For more complicated data, you need to define your own types using structs or classes. While Swift provides this functionality, it does differentiate types that are provided by the language or the program.

Instead, it separates types into named types, which are named by the language or program, and compound types, which allow you to return multiple, distinct values under the guise of “one variable.” These are commonly called tuples in Swift and other languages.

You define a tuple in Swift by replacing the type with a series of types in parentheses:

var myStatistics : (Int, Int, Float)

To set values, embed those in parentheses as well:

var myStatistics : (Int, Int, Float) = (2, 2, 5.2)

Tuples operate similarly to structs in Objective-C, which are custom data types you define (unlike an object, they do not have methods). For reference, remember that a struct in Objective-C is defined thus:

typedef struct {
        NSString *name;
        NSString *description;
        NSInteger displayOrder;
} MyType;

In this example, name, description, and displayOrder are the properties of the struct, and MyType is the name.

To replicate this behavior in Swift, by treating a tuple like a named type, use the _typealias_ keyword:

typealias MyType = (String, String, Int)

You can then instantiate a tuple using your type name as you would any other type:

var myVariable: MyType = ("Bob", "Bob is cool", 1)

Conditional Logic

In Objective-C, you implemented the most basic kind of conditional logic by placing a comparison in parentheses, as part of an if statement:

if (currentValue < maximumValue) {
}

In Swift, this syntax is largely unchanged, except the requirement for parentheses is gone:

if currentValue < maximumValue

An if-else statement retains this rule as well:

if currentValue < maximumValue {
        //Do something
} else if currentValue == 3 {
        //Do something else
}

Note  All the comparison operators you are familiar with from Objective-C are still valid in Swift.

In Objective-C, you could use a ternary operator to combine an if-else and an assignment

NSInteger value = (currentValue < maximumValue) ? currentValue : maximumValue  ;

In this block of statement, you check if the currentValue is less than the maximumValue. If the statement is true, set value to currentValue; else set value to maximumValue.

This syntax is still available in Swift, and mostly unchanged:

var value = currentValue < maximumValue ? currentValue : maximumValue

When you wanted to check against multiple values in Objective, you used a switch statement, with a case block for each value:

switch(currentValue) {
        case 1: NSLog("value 1");
                break;
        case 2: NSLog("value 2");
                break;
        case 3: NSLog("value 3");
                break;
}

The good news is the switch statement is also available in Swift, with a couple of changes:

  1. switch statements in Swift allow you to compare objects. The Objective-C requirement for comparing values has been eliminated in Swift.
  2. switch statements in Swift no longer fall through by default. This means you do not have to add a break; statement at the end of each case.
  3. switch statements in Swift need to be exhaustive (meaning they cover all values, or include a default case). This requirement is a best practice for code security and prevents unexpected comparisons.

In Swift, the switch statement described previously would look as follows:

switch currentValue {
        case 1: println("value 1")
        case 2: NSLog("value 2")
        case 3: NSLog("value 3")
        default: NSLog("other value)
}

Enumerated Types

In Objective-C, you use an enumerated type to group related values and to reduce errors from “magic numbers,” values that are hard-coded into your code.

In Objective-C, enumerated types (enums) are defined as such:

typedef enum {
        Failed = -1,
        Loading,
        Success
} PlaybackStates;

Where PlaybackStates is the name of the enum, Failed, Loading, and Success are the three possible values, and -1 is the initial value of the first item in the enum. In Objective-C, if not explicitly defined, enums start at 0 and increment by 1. Objective-C enums can only store discrete, increasing values, such as integers or characters.

In Swift, the syntax for defining an enumerated type is similar, except you indicate the value type and use the case keyword to indicate each named value:

enum PlaybackStates : Int {
        case Failed = -1,
        case Loading,
        case Success
}

Swift does not try to automatically increment values like in Objective-C, so you can store any type of value in an enum, including String.

To use a value from an enum in Objective-C, you replace your magic number with the value’s name. You can store the value in an integer or a variable with the enum’s name as its type:

NSInteger myPlaybackState = Failed;
PlaybackState myPlaybackState = Failed;

For conditional statements:

if (myPlaybackState == Failed)

In Swift, the easiest way to use an enum is by appending the value name to the type:

var myPlaybackState = PlaybackStates.Failed

This applies for conditionals as well:

if myPlaybackState == PlaybackState.Failed

If you are trying to retrieve the value that is stored in an enum member, use the rawValue property. For instance, if you are trying to translate PlaybackState to an Int value:

let playbackStateValue : Int = PlaybackState.Failed.rawValue

Loops

The syntax for all of the major types of loops (for, for-each, do, do-while) are largely unchanged in Swift. Two major changes are that you do not need to declare your type and parentheses are once again optional:

for name in nameArray {
        print ("name = (name)")
}

Also, there is a major addition in Swift that can improve your loops: ranges. Ranges in Swift allow you to specify a set of values that you can use to iterate a loop or as part of comparison.

There are two major types of ranges in Swift:

  • Closed Ranges, expressed as x...y, create a range that starts at x and includes all values including y
  • Half-Open Ranges, expressed as x..y, create a range that starts at x and includes all values up to y

You could use a range in a for-each loop as follows:

for i in 1..5  {
        print (i)
}

(This would print the numbers 1-4.)

Object-Oriented Programming in Swift

Now that you have a little bit of understanding of the basic syntax of Swift, I will start to cover Swift syntax as it applies to object-oriented programming.

Building a Class

Unlike Objective-C, Swift does not have a concept of interface files (.h) and implementation files (.m). In Swift, your entire class is declared within a .swift file.

In Objective-C, a class declaration consisted of:

  • Class name
  • Parent class
  • Property declarations (in interface file)
  • Method definitions (in implementation file)

In Objective-C, you declared a class in your header file:

@interface LocationViewController: UIViewController {
        @property NSString *locationName;
        @property Double latitude;
        @property Double longitude;
        -(NSString)generatePrettyLocationString;
}
@end

@Interface specified that you were declaring a class. UIViewController was the name of the parent class you were subclassing. @property indicated a variable as an instance variable, or property of the class. Finally, you also included method signatures in this block, so that they could be accessible by other classes.

In your .m file, you would define (or implement) the methods of your class, in the @implementation block:

@implementation LocationViewController {
        -(NSString)generatePrettyLocationString {
                //Do something here
        }
}

Swift does not split classes into .h and .m files; instead, all declarations and definitions take place in your .swift file.

You define classes in Swift with the class keyword:

class LocationViewController: UIViewController {

        var locationName : String?
        var latitude: Double = 1.0
        var longitude: Double = -1.0

        func generatePrettyLocationString -> String {
                //Do something
        }
}

Properties are included in a class by placing them inside the class block. Swift enforces property initialization. There are three ways to resolve compilation errors that occur as a result of not including an initial value for a property:

  • Specify a value when you declare the variable
  • Specify that the variable is an optional (explained further in this section)
  • Create a constructor (init) method that sets initial values for every property in the class.

Method implementations are also included directly inside the class block. There is no need to forward-declare signatures as in C or Objective-C.

Protocols

Protocols are a concept from Objective-C that allows you to define a limited interface between two classes, via a delegate property. For instance, when you use the iPhone’s camera, the class that presents the camera declares itself as implementing the UIImagePickerControllerDelegate protocol and defines the two methods that the protocol specifies for passing back information from the camera (without creating and managing a camera object in your code).

In Objective-C, to indicate that you will be implementing a protocol, you add the protocol’s name to your class declaration, after your parent class, in carrots:

@interface LocationViewController: UIViewController <UIImagePickerControllerDelegate>

In Swift, you simply add the protocol’s name after the parent class, separated by a comma:

class LocationViewController : UIViewController, UIImagePickerControllerDelegate {
        ...
}

You can do this for an infinite number of protocols by appending the protocol names, separated by commas:

class LocationViewController : UIViewController, UIImagePickerControllerDelegate, UITextFieldDelegate {
        ...
}

Method Signatures

Before defining a method in Swift, let’s investigate a method signature in Objective-C:

-(BOOL)compareValue1:(NSInteger)value1 toValue2:(NSInteger)value2;

In this line of code, you can see that the return type comes before the method name and that each of the parameters is provided after a colon. You add labels to every parameter after the first one, to increase readability.

In Swift, you declare a method by placing the func keyword in front of the method name and then including the input parameters and output parameters in parentheses:

func compareValues(value1: Int, value2: Int) -> (result: Bool)

Swift uses -> to separate the input parameters and return parameters.

As with variables in Swift, you indicate the type of each parameter by appending the type name with a colon.

If your method does not return anything, you can omit the return parameters and ->:

func isValidName(name: String) {
}

Since tuples are everywhere in Swift, you can also return a tuple from a method:

func compareValues(value1: Int, value2: Int) -> (result: Bool, average: Int) {
}

Accessing Properties and Methods

The concept of accessing a method or property on an object is referred to as message-passing. In Objective-C, the primary way of passing messages was through Smalltalk syntax:

[receiver message];

where receiver represents the object that is being acted upon, and message represents the property or method you are trying to access.

In Swift, you can access a property or method of an object by using dot-syntax, as you would in C or Java. This includes classes that were defined in Objective-C.

receiver.message()

In Objective-C, you always have to use Smalltalk syntax to access a method. To call the reloadSubviews method on a UIView object in Objective-C, you would make a call like the following:

[myView reloadSubviews];

In Swift, the same line of code would look like this:

myView.reloadSubviews()

If you were passing multiple parameters to a method in Objective-C, you would append the labels and values:

[myNumberManager calculateSumOfValueA:-1 andValueB:2];

In Swift, simply include the extra parameters in the parentheses:

myNumberManager.calculateSum(-1, valueB:2)

In Objective-C you had two ways of reading the value of a property. Through Smalltalk syntax:

CGSize viewSize = [myView size];

or through dot-syntax:

CGSize viewSize = myView.size;

In Swift, you always use dot-syntax to access properties.

var viewSize: CGSize = myView.size

To set a value for a property in Objective-C, you could use dot-syntax to set the value of a property:

myView.size = CGSizeMake(0,0, viewWidth, viewHeight);

But, you could also use an autogenerated setter method:

[myView setSize:CGSizeMake(0,0, viewWidth, viewHeight)];

In Swift, you always use dot syntax to set the value of a property:

myView.size = CGSizeMake(0,0, viewWidth, viewHeight)

Instantiating Objects

In Objective-C, you would instantiate an object by allocating it in memory and then calling its constructor method:

NSMutableArray *fileArray = [[NSMutableArray alloc] init];

Some classes have constructors that allow you to pass in parameters.

NSMutableArray *fileArray = [[NSMutableArray alloc] initWithArray:otherArray];

Some classes also have convenience constructors, which serve as shortcuts for the process of allocating and initializing an object.

NSMutableArray *fileArray = [NSMutableArray arrayWithArray:otherArray];

Things are a bit easier in Swift. Swift automatically allocates memory, removing the alloc step. Additionally, the default constructor for a class in Swift is the class name with an empty set of parentheses appended to the end:

var fileArray = Array()

If the class you are initializing takes parameters in its constructor, call the constructor as you would any other method:

var myView = UIView(frame: myFrame)

If you are instantiating an object from an Objective-C class, you need to call its constructor as a method:

var mutableArray = NSMutableArray()

Note  Swift has its own classes to represent Strings, Arrays, Dictionaries, and Sets, but you are free to use the Objective-C classes to maintain compatibility with older APIs or libraries.

Strings

In Objective-C, to represent a string, you would use the NSString class. To initialize a string, you could use a constructor method:

NSString *myString = [[NSString alloc] initWithString: @"Hello World"];

You could also use the @ symbol to load the value directly, using Objective-C’s literal syntax:

NSString *myString = @"Hello World";

In Swift, the class for representing strings is String. You load values directly:

Let myString = "Hello"

In Objective-C, NSString objects are immutable, meaning you cannot append or remove characters at runtime, unless you assigned a new value to the string. To resolve this issue, you could use the NSMutableString class in Objective-C, which allows you to mutate its objects at runtime:

NSMutableString *myString = [NSMutableString stringWIthString@"Hello"];
[myString appendString:@" world"]; //result is "Hello world"

In Swift, String objects defined with var are mutable. You can append values using the append function:

var myString = "Hello"
myString.append(" world") //result is "Hello world"

Note  You can continue to use the NSString and NSMutableString classes in Swift, but it is only recommended for interfacing with Objective-C classes.

Formatting Strings

In Objective-C, when you wanted to insert a value from an object into a string, you had to use a string formatter to build a custom string:

NSString *summaryString = [NSString
    stringWithFormat:@"int value 1: %d,  float value 2: %f, string value 3: %@", -1, 2.015, @"Hello"];

For each value you wanted to insert into the string, you needed to use a combination of characters, referred to as a format specifier, to substitute the variable you wanted to insert into the string. Format specifiersgenerally consist of the % character and a letter indicating the value type (e.g., @ for string, d for integer value, and f for float value).

Swift makes it easier for you to insert a value into a string by placing the variable’s name in parentheses, prepended by a forward slash:

let value1 = -1
let value2 = 2.015
let value3 = "Hello"
var summaryString = "int value 1: (value1), float value 2: (value2), string value 3: (value3)"

In Objective-C, to limit the number of decimal places that appeared in a float value, you would add a period and the number of spaces in front of the format specifier for the float:

NSString *summaryString = [NSString
    stringWithFormat:@"float value: %0.2f", 2.015];

To replicate this in Swift, use the String constructor method which specifies format as its input.

var summaryString = String(format:  "float value: %0.2f", 2.015)

The syntax is the same as that for Objective-C, where you pass in the format string and then a comma-separated list of values to replace the format specifiers.

Collections

In both Objective-C and Swift, the following are three classes referred to as collections, which “collect” similar objects together into one object:

Arrays: An ordered collection of objects. These preserve the order you use to load items. You retrieve items by indicating their position in the collection.

Set: An unordered collection of objects. These are used to test “membership” of objects–for instance, if an object exists in a set. You retrieve objects by specifying a subset.

Dictionary: An unordered collection of objects, which is identified by “keys.” These are also called key-value pairs. You retrieve objects by specifying their keys.

In Objective-C, you used the NSArray class to represent arrays. Arrays in Objective-C contain objects only, and can be initialized with several convenience constructor methods, including [NSArray arrayWithObjects:].

NSArray *stringArray = [NSArray arrayWithObjects:@"string 1",
    @"string 2", @"string 3"]

In Swift, you can declare an array by providing its values in square brackets when you define the variable.

var stringArray = ["string 1", "string 2", "string 3"]

Swift does not place the same restriction on the contents of an array, so you can initialize it with scalar values (such as Ints).

var intArray = [1, 3, 5]

If you do not want to initialize your array with values, declare a variable type for the input by placing the type name in square brackets:

var intArray : [Int]

You can also use subscript notation to read or change values in a Swift array.

var sampleString = stringArray[3]

In Objective-C, NSArray objects are immutable. To make an array mutable, you defined it using the NSMutableArray class:

NSMutableArray *stringArray = [NSMutableArray arrayWithObjects:@"string 1",
  @"string 2", @"string 3"]

Remember, by defining your array with the var keyword, you make it a mutable variable, allowing you to “mutate” existing values or add new ones at runtime. For instance, you can use the plus (+) operator to append values to the array.

stringArray += "string4"

You can also change a value by specifying a value at an index

stringArray[2] = "This is now the coolest string".

In Objective-C, you create a set by initializing it with a comma-separated list of objects:

NSSet *mySet = [NSSet setWithObjects: @"string 1", @"string 2", @"string 3"];

You could also create a set with an array.

NSSet *mySet = [NSSet setWithArray: stringArray];

To membership, you use the containsObject method:

if ([mySet containsObject:"string 1"]) {
        print("success!")
}

In Swift, you create a set by using the Set class and specifying the type of its members:

var stringSet: Set<String>

You can initialize a set with an array:

var stringSet: Set<String> = ["string 1", "string 2", "string 3"]

Similarly, you can mutate the set if it is defined as a mutable variable (with the var keyword).

StringSet += "hello world"

To create a dictionary in Objective-C, you pass in arrays of keys and values. The two are matched according to the order you pass them in.

NSDictionary *myDict = [NSDictionary dictionaryWithObjects:@"string 1", @"string 2", @"string 3", nil forKeys:@"key1", @"key2", @"key3"];

You could also use literal shortcut to initialize a dictionary in Objective-C.

NSDictionary *myDict = @{@"key1" : @"string 1", @"key2" : @"string 2", @"key3" : @"string 3" };

To access a value from a dictionary in Objective-C, you specified its key.

NSString *string = [myDict objectForKey:@"key1"];

In Swift, you define a dictionary by specifying the types of its keys and values.

var myDict = [String, String]()

The empty brackets specify that you are creating an empty dictionary.

To initialize a dictionary with key-value pairs, pass them in using literal syntax. For Swift dictionaries, this is a comma-separated list of key-value pairs (connected with colons).

var myDict  : [String, String] = ["key1" : "string 1", "key2": "string 2", "key3" : "string 3"]

To retrieve an object from a dictionary, use the following key:

let myString = myDict["key1"]

If the dictionary is a mutable variable, you can mutate values or append new key-value pairs at runtime.

myDict["key3"] = "this is the coolest string"

Casting

In Objective-C, to cast an object from one class to another, you would prepend the class name and optionally an asterisk, identifying your pointer.

UINavigationController *navigatonController = (UINavigationController *)segue.destinationController;

In Swift, casting is as easy as using the as keyword.

let navigationController = segue.destinationController as UINavigatonController

Simply add the as keyword and the class name to the result, and the compiler will do the rest for you. You can even insert this keyword inline, wherever you would normally use your object:

for (file as String in fileArray) {}

Swift-Specific Language Features

In this section, I will review that which my clients found particularly difficult when adopting Swift—particularly, optionals and try-catch blocks. These do not translate directly to language features in Objective-C and need extra care to use correctly.

Optionals

In Objective-C, the nil keyword is used to represent an object that is null. Null is the “initial” state of values, before they are initialized or after they have been “reset.” For an object, a pointer points to an empty area in memory. For scalar variables, it is the value 0. The idea is, an object that has been initialized correctly will have a non-null value; a non-null value is considered “valid.” It is also common for methods to return nil values in error conditions.

In Objective-C, a common way to check if an object is valid is by checking for nil. For instance, to check if a value exists for a key in a dictionary

if ( myDict["coolestKey"} != nil) {
        //success
}

If the value for that key does not exist in the dictionary, it will return nil.

In Objective-C, this applies for properties of a class as well. If a property has not been initialized, attempting to retrieve it will return nil.

if (myLocationManager.locationString != nil) {
        //success
}

In Objective-C, if you try to perform an operation on a nil pointer, you will crash your application.

Swift attempts to resolve this issue through the concept of optionals. An optional is a type that can represent a nil, or an uninitialized value. To define a variable as an optional, append the ? operator to the type name:

var myString : String?

You will commonly see this syntax in class properties. In Swift, all properties need to be valid when they are initialized. There are three ways to resolve this issue.

  1. Specify a value for the property when declaring it.
    class LocationManager: NSObject {
            var locationString : String = "Empty string"
    }
  2. Create a constructor which initializes the property.
    class LocationManager: NSObject {
            var locationString : String

            init(locationString: String) {
                    self.locationString = locationString
            }

    }
  3. Define the property as an optional.
    class LocationManager: NSObject {
            var locationString  : String?
    }

When trying to access the property, check if it is nil before trying to use it.

if myLocationManager.locationString != nil {
        //success
}

If the property you are trying to access has properties you want to access (for instance, a UIView object), you need to “unwrap” your class’s property before using it. This operation is referred to as “optional-chaining.” The general idea is, tell the compiler that your property is an optional and then try to check if the derived property is non-nil; if it is, create a variable to contain the property. In the case of checking if a subview exists on a UIViewController:

if  let mapViewSize = self.mapView?.size {
        //success
        print("size = (mapViewSize)")
}

The ? operator lets the compiler know that the property is an optional. If the optional returns nil, it will not attempt to access the derived property and will not execute the success block.

If you have confirmed that an optional property is non-nil, you can access its derived properties by force-unwrapping it. Force-unwrapping is indicated by the ! operator.

if (self.mapView != nil) {

        let mapViewSize = self.mapView!.size
        print("size = (mapViewSize)")
}

If you do not check that the property is non-nil before force-unwrapping, your application will crash.

Try-Catch Blocks

The major difficulty of error passing in Swift is that it does not directly translate over from Objective-C. In Objective-C, you would catch an error by passing an NSError object by reference. This object would be mutated by the method you called; an error would be represented by a non-nil value.

In Swift, methods throw errors using exceptions. You can catch these using a try-catch block.

In Objective-C, to return an error, you define a method that takes a pointer to an NSError object.

-(void)translateString:(NSString *)inputString error:(NSError **)error {
}

To modify the object, “dereference” it using the * operator. This allows you to perform operations on the object that is represented by the pointer.

-(void)translateString:(NSString *)inputString error:(NSError **)error {

        *error = [NSError errorWithDomain:NSCocoaErrorDomain code:400 userInfo:userInfoDict];
}

In this example, we create a new NSError object using a domain (an enum representing the general “kind” of error), an error code represented by an integer value, and a dictionary containing any other information we want to pass back about the error.

To call the method from Objective-C, create an error object and pass a “reference” to it using the & operator.

-(void)myMethod {
        NSError *error;
        [self translateString:@"hello" error:&error];
}

To define a method that returns an error in Swift, add the “throws” keyword after the parameter list, and before the return values list:

func translateString(inputString: String) throws -> Void {

}

Swift defines a protocol called ErrorType, which allows you to create exceptions that translate to NSError properties when called from Objective-C. To define your error, create an enum that uses this protocol. Specify any codes and domains for your error types.

enum TranslationError : Int, ErrorType {
        case EmptyString = -100
        case UnrecognizedLanguage = 1000
        case InvalidString = 1001
}

To throw an exception, use the throw keyword, specifying the enum and error type.

func translateString(inputString: String) throws -> Void {
        if (inputString  == nil) {
                throw TranslationError.EmptyString
        }
}

To catch an error, use a try-catch block. Place the error-prone code in a do block; prepared the try keyword to the line that is going to throw the exception. Catch the exception in the catch block.

do {
        let myString : String = nil
        try translateString(myString)
} catch TranslationError.InvalidString  {
        print("this is an invalid string")
}

If you want to try to catch multiple exceptions of the same type, add additional catch blocks.

do {
        let myString : String = nil
        try translateString(myString)
} catch TranslationError.InvalidString  {
        print("this is an invalid string")
} catch TranslationError.EmptyString  {
        print("this is an empty string")
}

Note  You need a try-catch block for each method you try to call that returns an exception. Methods should only return one type of exception.

Mixing Objective-C and Swift in Projects

Swift lets you import classes written in Objective-C and call Objective-C methods. This import capability is extremely valuable, because you can import any of the existing Cocoa Touch APIs or your existing Objective-C code.

If your project is going to be primarily in Swift, make sure that you specify Swift as the primary language when creating it from Xcode > New > Project, as shown in Figure 2-1.

9781484211953_Fig02-01.jpg

Figure 2-1. Creating a Swift project

To add an Objective-C class to your Swift project, follow the same process you would use for an Objective-C project: drag and drop the files onto the Project Navigator, or select Add Files to <Project Name> from the File menu, as shown in Figure 2-2.

9781484211953_Fig02-02.jpg

Figure 2-2. Importing files into your project

To access an Objective-C class from a Swift file, simply create an object using the type and constructor specified by the class, and call methods via dot-syntax.

var myObjcString : NSMutableString = NSMutableString(string: "Hello")
myObjcString.appendString(" world")

The compiler takes care of converting constructors and other methods to Swift-style syntax for you, take advantage of autocompletion to help you, as shown in Figure 2-3.

9781484211953_Fig02-03.jpg

Figure 2-3. Autocompletion results for NSMutableString

Calling Swift from Objective-C

You can also call Swift classes and methods from Objective-C, as long as you are aware of a few rules.

  • If you are subclassing a Foundation class (e.g., NSString) or a Cocoa Touch class (e.g., UIViewController), it will be available when importing your Swift file into an Objective-C class.
  • Within a Swift-compatible class, if a method uses a language feature not available in Objective-C, such as an optional or tuple, it will not be available to Objective-C. Attempt to resolve these issues by writing methods that can translate directly to Objective-C.
  • Objective-C mutates NSError objects for passing back errors, while Swift 2.0 uses exceptions. To make a method that is compatible between the two languages, remember take advantage of the ErrorType protocol, as described in the section “Try-Catch Block.”
  • You can make an enum available to Objective-C by prepending the @objc keyword. Similarly, make sure the types in your enum are compatible with Objective-C (increasing, discrete values like integers or characters)
    @Objc enum PlaybackStates : Int {
            case Failed = -1,
            case Loading,
            case Success
    }

Note  The @objc keyword is used globally throughout Swift as a way of explicitly defining a type, method, or class as Objective-C compatible.

Summary

In this chapter, I introduced Swift by comparing how Objective-C and Swift implement basic syntax and object-oriented programming concepts. You noticed that while Swift brings significant syntax changes to iOS development, its methods are designed to work like Objective-C. For the most part, by using Swift method syntax, you can call any Objective-C Cocoa Touch method from Swift. In instances where this is not possible, there are ways to use protocols like ErrorType to produce output that is compatible with Objective-C. To make the chapter particularly useful, I paid special attention to optionals and try-catch blocks, two language features that are not implemented in Objective-C.

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

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