Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
iOS Programming Cookbook
iOS Programming Cookbook

iOS Programming Cookbook: Over 50 exciting and powerful recipes to help you unearth the promise of iOS programming

€28.99 €19.99
Book Mar 2017 520 pages 1st Edition
eBook
€28.99 €19.99
Print
€37.99
Subscription
€14.99 Monthly
eBook
€28.99 €19.99
Print
€37.99
Subscription
€14.99 Monthly

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now

Product Details


Publication date : Mar 31, 2017
Length 520 pages
Edition : 1st Edition
Language : English
ISBN-13 : 9781786460981
Vendor :
Apple
Category :
Table of content icon View table of contents Preview book icon Preview Book

iOS Programming Cookbook

Chapter 1. Swift Programming Language

In this chapter, we will cover the following topics:

  • Using closures to create self-contained code

  • Creating enumerations to write readable code

  • Working with protocols and delegates

  • Using extensions to extend classes functionality

  • Working with memory management and ARC

  • Using error handling

  • Using generics to write generic and reusable code

Introduction


Welcome to our first chapter in iOS Programming Cookbook. We will start our journey in this book with a revision or emphasize on the most important and commonly used topics in Swift programming language. Before talking about these topics, ensure that you have a basic knowledge about Swift programming language and have used it before.

It has been more than 2 years since Apple released the awesome programming language-Swift. Swift is meant to be easy to code, easy to learn, safe, and intuitive. For each version of Swift, Apple introduces some awesome features and enhancements in the language. As we see in Swift 2.0, Swift came with higher performance, and new APIs such as error handling, and some enhancements. Swift is not meant to be available in iOS development only; you may find it in other platforms later in the future, thanks to the announcement of Apple that Swift will become open source.

Our recipes in this chapter will focus on the most important topics in Swift that will be used frequently in iOS development. When you focus on these topics and learn them properly, you will find using them in development will make your life easier and your code will be more organized. There are many people who can write code, but only few can write awesome code. Thus, mastering these topics is very important to be a good developer and to help you and others working on a project.

Note

For the latest features of Swift, ensure that you are using the latest version of Xcode.

Using closures to create self-contained code


Closures are self-contained lines of code to be executed and passed like any other data types. You should be familiar with blocks or at least heard about them in Objective-C or C. This recipe will help you to understand closure syntax and get familiar in using them.

Getting ready

Closures syntax in Swift is pretty easy and is easier than the syntax in C or Objective-C. The general form of closure is as follows:

{ (parameters) ->returnType in 
   // block of code goes here 
} 

As you see, you first put open curly braces, add list of parameters and the return type, then the keyword in, followed by lines of code in your closure. Closures are first-class type, which means it can be nested, passed in parameters, returned from function, and so on.

How to do it...

  1. Go to Xcode and create a new playground file called Closures to test code about closures in it.

  2. To see closures in action, copy and paste this piece of code in the playground file (the output of each statement is printed on the right):

          var names = ["David", "Jones", "Suzan", "Naomi", "Adam"] 
     
          names.sort() // ["Adam", "David", "Jones", "Naomi", "Suzan"] 
          names.sort{ (str1: String, str2: String) ->Bool in 
            return str1 > str2 
          } 
     
          // ["Suzan", "Naomi", "Jones", "David", "Adam"] 
    

How it works...

Swift provides us with a built-in system function called sort. The function can sort any collection of data. The function, by default, will sort the collection in an ascending order. The sort function gives us another flexibility by which you can provide a closure that returns the comparison result between any two items in the list to determine which should come first in the list.

As we saw, the default sort function sorts our data in an ascending order; in order to do any other logic, we can sort with closure that gives you two items as parameters to decide how to compare them. The sort function sorts the collection in place, and that's why the names variable is created as var not let. If the names collection is defined as let, you will not be able to use the sort() function. There is another function called sorted(), which returns a totally new sorted collection without changing the original one. It's available in both versions of the collection with var or let.

There's more...

Even though the closure syntax looks simple, but Swift can make it simpler. Let's see how closure syntax can be optimized.

Inferring type

When closures are passed as argument like what we did in the sort function, Swift can infer the types of closure parameters and return type. In that case, we can omit the parameters and return types, as there is no need to write them. In our previous example, when we infer types, the sort function would be like this:

names.sort{ str1, str2 in 
   return str1 > str2 
} 

As you can see, the String types and the return type have been omitted.

Omitting the return keyword

Swift can make your life easier than that. When closure body consists of only one expression, the return keyword can be omitted. So, the new version of sort function will be like this:

names.sort({ str1, str2 in str1 > str2}) 

Shorthand arguments

To reach the maximum awesomeness of Swift, you can refer to the argument list with names $0, $1, and so on. When you decide to use the shorthand arguments, you can omit the list of parameters. You may ask what about the in keyword, will it be alone? The answer is no, we won't leave it alone; we can omit it as well completely. Here is the final version of our sort function:

names.sort({ $0 > $1}) 

Creating enumerations to write readable code


Using enumerations is one of the best practices that you should follow while writing any software project and not only iOS projects. Once you find that you have a group of related values in your project, create enum to group these values and to define a safe type for these values. With enumerations, your code becomes more readable and easy to understand, as it makes you define new types in your project that map to other value. In Swift, enumerations have been taken care of and have become more flexible than the ones used in other programming languages.

Getting ready

Now, we will dive into enumerations and get our hands dirty with it. To do so, we will create a new playground file in Xcode called Enumerations so that we can practice how to use enumerations and also see how it works.

Writing enumerations is meant to be easy, readable, and straightforward in syntax writing in Swift. Let's see how enum syntax goes:

enum EnumName{ 
} 

You see how it's easy to create enums; your enumeration definition goes inside the curly braces.

How to do it...

Now, let's imagine that we are working on a game, and you have different types of monsters, and based on the type of monster, you will define power or the difficulty of the game. In that case, you have to use enum to create a monster type with different cases, as follows:

  1. Type the following code to create enum with name Monster:

          enum Monster{ 
            case Lion 
            case Tiger 
            case Bear 
            case Crocs 
          } 
     
          enum Monster2{ 
            case Lion, Tiger, Bear, Crocs 
          } 
    
  2. Use the '.' operator to create enums variables from the previously created enum:

          var monster1 = Monster.Lion 
          let monster2 = Monster.Tiger 
          monster1 = .Bear 
    
  3. Use the switch statement to check the value of enum to perform a specific action:

          func monsterPowerFromType(monster:Monster) ->Int { 
        
            var power = 0 
        
            switch monster1 { 
              case .Lion: 
                power = 100 
              case .Tiger: 
                power = 80 
              case .Bear: 
                power  = 90 
              case .Crocs: 
                power = 70 
            } 
            return power 
          } 
     
          let power = monsterPowerFromType(monster1) // 90 
     
          func canMonsterSwim(monster:Monster) ->Bool{ 
        
            switch monster { 
              case .Crocs: 
                return true 
              default: 
                return false 
            } 
          } 
     
          let canSwim = canMonsterSwim(monster1) // false 
    

How it works...

Now, you have a new type in your program called Monster, which takes one value of given four values. The values are defined with the case keyword followed by the value name. You have two options to list your cases; you can list each one of them in a separate line preceded by the case keyword, or you can list them in one line with a comma separation. I prefer using the first method, that is, listing them in separate lines, as we will see later that we can add raw values for cases that will be more clear while using this method.

Note

If you come from a C or Objective-C background, you know that the enums values are mapped to integer values. In Swift, it's totally different, and they aren't explicitly equal to integer values.

The first variable monster1 is created using the enum name followed by '.' and then the type that you want. Once monster1 is initialized, its type is inferred with Monster; so, later you can see that when we changed its value to Bear, we have just used the '.' operator as the compiler already knows the type of monster1. However, this is not the only way that you will use enums. Since enums is a group of related values, so certainly you will use it with control flow to perform specific logic based on its value. The switch statement is your best friend in that case as we saw in the monsterPowerFromType() function.

We've created a function that returns the monster power based on its type. The switch statement checks all values of monster with '.' followed by an enum value. As you already know, the switch statement is exhaustive in Swift and should cover all possible values; of course, you can use default in case it's not possible to cover all, as we saw in the canMonsterSwim() function. The default statement captures all non-addressed cases.

There's more...

Enumerations in Swift have more features, such as using enums with raw values and associated values.

Enum raw values

We saw how enums are defined and used. Enum cases can come with predefined values, which we call raw values. To create enums with raw values, the following rules should be adhered:

  • All raw values should be in the same type.

  • Inside the enum declaration, each raw value should be unique.

  • Only possible values allowed to use are strings, characters, integer, and floating point numbers.

Assigning raw values

When you assign raw values to enum, you have to define the type in your enum syntax and give value for each case:

enum IntEnum: Int{ 
   case case1 = 50 
   case case2 = 60 
   case case3 = 100 
} 

Swift gives you flexibility while dealing with raw values. You don't have to explicitly assign a raw value for each case in enums if the type of enum is Int or String. For Int type enums, the default value of enum is equal to the value of previous one + 1. In case of the first case, by default it's equal to 0. Let's take a look at this example:

enum Gender: Int{ 
   case Male 
   case Female 
   case Other 
} 
var maleValue = Gender.Male.rawValue  // 0 
var femaleValue = Gender.Female.rawValue // 1 

We didn't set any raw value for any case, so the compiler automatically will set the first one to 0, as it's a no set. For any following case, it's value will be equal to previous case value + 1. Another note is that .rawValue returns the explicit value of the enum case. Let's take a look at another complex example that will make it crystal clear:

enum HTTPCode: Int{ 
   case OK = 200 
   case Created  // 201 
   case Accepted // 202 
   case BadRequest = 400 
   case UnAuthorized 
   case PaymentRequired 
   case Forbidden 
    
} 
 
let pageNotFound = HTTPCode.NotFound 
let errorCode = pageNotFound.rawValue  // 404 

We have explicitly set the value of first case to 200; so, the following two cases will be set to 201 and 202, as we didn't set raw values for them. The same will happen for BadRequest case and the following cases. For example, the NotFound case is equal to 404 after incrementing cases.

Now, we see how Swift compiler deals with Int type when you don't give explicit raw values for some cases. In case of String, it's pretty easier. The default value of String enum cases will be the case name itself. Let's take a look at an example:

enum Season: String{ 
   case Winter 
   case Spring 
   case Summer 
   case Autumn 
} 
 
let winter = Season.Winter 
 
let statement = "My preferred season is " + winter.rawValue // "My preferred season is Winter" 

You can see that we could use the string value of rawValue of seasons to append it to another string.

Using Enums with raw values

We saw how easy it is to create enums with raw values. Now, let's take a look at how to get the raw value of enums or create enums back using raw values.

We already saw how to get the raw value from enum by just calling .rawValue to return the raw value of the enum case.

To initialize an enum with a raw value, the enum should be declared with a type; so in that case, the enum will have a default initializer to initialize it with a raw value. An example of an initializer will be like this:

let httpCode = HTTPCode(rawValue: 404)  // NotFound 
let httpCode2 = HTTPCode(rawValue: 1000) // nil 

The rawValue initializer always returns an optional value because there will not be any matching enum for all possible values given in rawValue. For example, in case of 404, we already have an enum whose value is 404. However, for 1000, there is no enum with such value, and the initializer will return nil in that case. So, before using any enum initialized by the rawValue initializer, you have to check first whether the value is not equal to nil; the best way to check for enums after initialization is by this method:

if let httpCode = HTTPCode(rawValue: 404){ 
   print(httpCode) 
} 
if let httpCode2 = HTTPCode(rawValue: 1000){ 
   print(httpCode2) 
} 

The condition will be true only if the initializer succeeds to find an enum with the given rawValue.

Enums with associated values

Last but not least, we will talk about another feature in Swift enums, which is creating enums with associated values. Associated values let you store extra information with enum case value. Let's take a look at the problem and how we can solve it using associated values in enums.

Suppose we are working with an app that works with products, and each product has a code. Some products codes are represented by QR code format, but others by UPC format. Check out the following image to see the differences between two codes at http://www.mokemonster.com/websites/erica/wp-content/uploads/2014/05/upc_qr.png):

The UPC code can be represented by four integers; however, QR code can be represented by a string value. To create an enum to represent these two cases, we would do something like this:

enum ProductCode{ 
   case UPC(Int, Int, Int, Int) 
   case QR(String) 
} 
 
var productCode = ProductCode.UPC(4, 88581, 1497, 3) 
productCode = ProductCode.QR("BlaBlaBla") 

First, UPC is a case, which has four integer values, and the second is a QR, which has a string value. Then, you can create enums the same way we created before in other enums, but here you just have to pass parameters for the enum. When you need to check the value of enum with its associated value, we will use a switch statement as usual, but with some tweaks:

switch productCode{ 
case .UPC(let numberSystem, let manufacturerCode, let productCode, let checkDigit): 
   print("Product UPC code is \(numberSystem) \(manufacturerCode) \(productCode) \(checkDigit)") 
case .QR(let QRCode): 
   print("Product QR code is \(QRCode)") 
} 
 
// "Product QR code is BlaBlaBla" 

Working with protocols and delegates


Protocol is a set of methods and properties for a particular task to which classes, structure, or enumeration can be conformed.

Getting ready

The syntax of protocol goes like this:

protocol ProtocolName{ 
   // List of properties and methods goes here.... 
} 

The keyword protocol followed by the protocol name and curly braces are the building blocks of any protocol you need to write. Classes, structures, or enumeration can then conform to it like this:

class SampleClass: ProtocolName{ 
} 

After class name, you type colon and the super class name that this class extend from if any, followed by a list of protocols that you want to conform to with a comma separation.

How to do it...

  1. Create a new playground file in Xcode called Protocols as usual.

  2. Complete the following example using the following protocol:

          protocol VehicleProtocol{ 
            // properties 
            var name: String {set get} // settable and gettable 
            var canFly: Bool {get} // gettable only (readonly) 
        
            // instance methods 
            func numberOfWheels() ->Int 
            func move() 
            func stop() 
        
            // class method 
            staticfuncpopularBrands() -> [String] 
          } 
     
          class Bicycle: VehicleProtocol{ 
            var name: String 
            var canFly: Bool{ 
              return false 
          } 
        
          init(name: String){ 
          self.name = name 
          } 
        
          func numberOfWheels() -> Int { 
            return 2 
          } 
          func move() { 
           // move logic goes here 
          } 
          func stop() { 
           // stop logic goes here 
          } 
          static func popularBrands() -> [String] { 
            return ["Giant", "GT", "Marin", "Trek", "Merida", "Specialized"] 
          } 
        } 
     
        class Car: VehicleProtocol{ 
          var name: String 
          var canFly: Bool{ 
            return false 
          } 
        
        init(name: String){ 
          self.name = name 
        } 
        
        funcnumberOfWheels() ->Int { 
          return 4 
        } 
        func move() { 
          // move logic goes here 
        } 
        func stop() { 
          // stop logic goes here 
        } 
        static func popularBrands() -> [String] { 
          return ["Audi", "BMW", "Honda", "Dodge", "Lamborghini", "Lexus"] 
        } 
      } 
     
      let bicycle1 = Bicycle(name: "Merida 204") 
      bicycle1.numberOfWheels() // 2 
     
      let car1 = Car(name: "Honda Civic") 
      car1.canFly  // false 
     
      Bicycle.popularBrands() // Class function 
      // ["Giant", "GT", "Marin", "Trek", "Merida", "Specialized"] 
      Car.popularBrands() // ["Audi", "BMW", "Honda", "Dodge", "Lamborghini", "Lexus"] 
    

How it works...

We started by defining VehicleProtocol that has a list of properties and functions that every vehicle should have. In properties, we have two types of properties: name, which is marked as {get set}, and canFly, which is marked as {get}. When you mark a property {get set}, it means it's gettable and settable, whereas {get} means it only gettable, in other words, it's a read-only property. Then, we added four methods, out of which three methods-numberOfWheels(), move(), and stop()-are instance methods. The last one-popularBrands()- marked as static is a type method. Types methods can be called directly with type name, and there is no need to have instance to call it.

Then, we created two new classes, Bicycle and Car, which conform to VehicleProtocol, and each one will have different implementations.

There's more...

We have already covered the most important parts of protocols and how to use it, but still they have more features, and there are many things that can be done with it. We will try here to mention them one by one to see when and how we can use them.

Mutating methods

Swift allows you mark protocol methods as mutating when it's necessary for these methods to mutate (modify) the instance value itself. This is applicable only in structures and enumerations; we call them value types. Consider this example of using mutating:

protocol Togglable{ 
   mutating func toggle() 
} 
 
enum Switch: Togglable{ 
   case ON 
   case OFF 
    
   mutating func toggle() { 
       switch self { 
       case .ON: 
           self = OFF 
       default: 
           self = ON 
       } 
   } 
} 

The Switch enum implements the method toggle, as it's defined in the protocol Togglable. Inside toggle(), we could update self-value as function marked as mutating.

Delegation

Delegation is the most commonly used design pattern in iOS. In delegation, you enable types to delegate some of its responsibilities or functions to another instance of another type. To create this design pattern, we use protocols that will contain the list of responsibilities or functions to be delegated. We usually use delegation when you want to respond to actions or retrieve or get information from other sources without needing to know the type of that sources, except that they conform to that protocol. Let's take a look at an example of how to create use delegate:

@objc protocol DownloadManagerDelegate { 
   func didDownloadFile(fileURL: String, fileData: NSData) 
   func didFailToDownloadFile(fileURL: String, error: NSError) 
} 
class DownloadManager{ 
   weak var delegate: DownloadManagerDelegate! 
   func downloadFileAtURL(url: String){ 
       // send request to download file 
       // check response and success or failure 
       if let delegate = self.delegate { 
           delegate.didDownloadFile(url, fileData: NSData()) 
       } 
   } 
} 
 
class ViewController: UIViewController, DownloadManagerDelegate{ 
   func startDownload(){ 
       letdownloadManager = DownloadManager() 
       downloadManager.delegate = self 
   } 
    
   func didDownloadFile(fileURL: String, fileData: NSData) { 
       // present file here 
   } 
   func didFailToDownloadFile(fileURL: String, error: NSError) { 
        // Show error message 
   } 
} 

The protocol DownloadManagerDelegate contains methods that would be called once the specific actions happen to inform the class that conforms to that protocol. The DownloadManager class performs the download tasks asynchronously and informs the delegate with success or failure after it's completed. DownloadManager doesn't need to know which object will use it or any information about it. The only thing it cares about is that the class should conform to the delegate protocol, and that's it.

Class-only protocols

We mentioned before that classes, structures, and enumerations could adopt protocols. The difference among them is that classes are reference types, whereas structures and enumerations are value types. If you find yourself having some specific actions that will be done only via reference types, mark it as class only. To do so, just mark it as follows:

protocol ClassOnlyProtocol: class{ 
   // class only properties and methods go here 
} 

Add a colon : and the class keyword to mark your protocol as class only.

Checking protocol conformance

It would be very useful to check whether an object conforms to a specific protocol or not. It's very useful when you have a list of objects, and only some of them conform to specific protocol. To check for protocol conformance, do the following:

class Rocket{ 
} 
 
var movingObjects = [Bicycle(name: "B1"), Car(name:"C1"), Rocket()] 
 
for item in movingObjects{ 
   if let vehicle =  item as? VehicleProtocol{ 
       print("Found vehcile with name \(vehicle.name)") 
       vehicle.move() 
   } 
   else{ 
       print("Not a vehcile") 
   } 
} 

We created a list of objects, and some of them conform to VehicleProtocol that we created earlier. Inside the for-loop we casted each item to VehicleProtocol inside if statement; the cast will succeed only if this item already conforms to that protocol.

Optional requirements

You see that when you list your properties and methods in a protocol, the type that conforms to that protocol should adopt to all properties and methods. Skipping one of them will lead to a compiler error. Some protocols may contain methods or properties that are not necessary to implement, especially with delegates. Some delegate methods are meant to notify you something that you don't care about. In that case, you can mark these methods as optional. The keyword optional can be added before properties and methods to mark them as optional. Another thing, the protocol that has optional stuff should be marked with @Objc. Take a look at the following example:

@objc protocol DownloadManagerDelegate { 
   func didDownloadFile(fileURL: String, fileData: NSData) 
   optional func didFailToDownloadFile(fileURL: String, error: NSError) 
} 

It's the new version of DownloadManagerDelegate, which marks didFailToDownloadFile method as optional.

Using extensions to extend classes functionality


For its name, extensions are used to extend an existing functionality. In Swift, you can extend classes, structures, protocols, and enumerations. Extensions are similar to categories in Objective-C except that extensions don't have names. It's very useful to add functionality to any type that you don't have access to its source code, such as native classes String, NSArray, and so on.

Getting ready

In Swift, syntax is pretty easy, and that's why it is awesome. To extend any type, just type the following:

extension TypeToBeExtended{ 
} 

Inside the curly braces, you can add your extensions to the type to be extended. In extension, you can do the following:

  • Adding instance- or class-computed properties

  • Adding instance or class methods

  • Adding new initializers

  • Defining subscripts

  • Adding nested types

  • Conforming to protocols

Once you create an extension to any type, the new functionality will be available for all instances in the whole project.

How to do it...

  1. Create a new playground file in Xcode called Extensions.

  2. Create extension for double value by adding computing properties, as follows:

       extension Double{ 
        
            var absoluteValue: Double{ 
              return abs(self) 
            } 
        
            var intValue: Int{ 
              return Int(self) 
            } 
          } 
     
          extension String{ 
        
            var length: Int{ 
              return self.characters.count 
            } 
          } 
     
          let doubleValue: Double = -19.5 
          doubleValue.absoluteValue // 19.5 
          doubleValue.intValue // 19 
     
          extension Int{ 
        
            func isEven() ->Bool{ 
              return self % 2 == 0 
            } 
        
            func isOdd() ->Bool{ 
              return !isEven() 
            } 
        
            func digits() -> [Int]{ 
              var digits = [Int]() 
              var num = self 
              repeat { 
                
                let digit = num % 10 
                digits.append(digit) 
                num /= 10 
                
              } while num != 0 
            
              return digits 
            } 
          } 
     
          let num = 12345 
          num.digits()  // [5, 4, 3, 2, 1] 
    

How it works...

In Double type, we have added two computed properties. The computed properties are properties that will be calculated every time when it's called. We've added a property called absoluteValue, which returns the absolute value; same for intValue, which returns the integer value of double. Then, for any double value in the whole project, these two properties are accessible and can be used.

In the Int type, we have defined three new instance methods. The isEven() method should return true if this number is even, false otherwise, and the same logic applies for isOdd(). The third method that has some more logic is digits(), which returns array of digits in the number. The algorithm is simple; we get the last digit by getting the remainder of dividing the number by 10, and then skip the last digit by dividing by 10.

There's more...

Extensions are not meant to add new properties and methods only. You extend types by adding new initializers, mutating methods, and by defining subscripts.

Mutating instance methods

When you add instance methods, you can let them mutate (modify) the instance itself. In methods we've added before, we just do some logic and return a new value, and the instance value remains the same. With mutating, the value of instance itself will be changed. To do so, you have to mark your instance method with the mutating keyword. Let's take a look at an example:

extension Int{ 
   mutating func square(){ 
       self = self * self 
   } 
    
   mutating func double(){ 
       self = self * 2 
   } 
} 
 
var value = 8 
value.double() // 16 
value.square() // 256 

When you mark your method as mutating, it lets you to change self and assign new value to it.

Adding new initializer

Extensions allow you to add new initializer to the currently available initializer for any particular type. For example, let's take a look at the CGRect class. CGRect has three initializers: empty init; init with origin and size; and init with x, y, width, and height. We will add new initializer with a center point and a rectangular size. Let's take a look at how to do it:

extension CGRect{ 
   init(center:CGPoint, size:CGSize){ 
       let x = center.x - size.width / 2 
       let y = center.y - size.height / 2 
       self.init(x: x, y: y, width: size.width, height: size.height) 
   } 
} 
 
let rect = CGRect(center: CGPoint(x: 50, y: 50), size: CGSizeMake(100, 80)) // {x 0 y 10 w 100 h 80} 

Define subscripts

One of features that extensions provide to us is the ability to define subscripts to a particular type. Subscripting allows you to get value by calling [n] to get information at index n. Like array, when you access item at that index, you can do the same with any type you want. In the following example, we will add subscripting support to the String type:

extension String{ 
   subscript(charIndex: Int) -> Character{ 
       let index = startIndex.advancedBy(charIndex) 
       return self[index] 
   } 
} 
let str = "Hello" 
str[0] // "H" 

To add subscript to type, just add the keyword subscript followed by index and the return type. In our preceding example, the subscript will return the character at a given index. We advanced the startIndex, which is a property in the String type and points to the first character by the input charIndex. Then, we return the character at that Index.

Working with memory management and ARC


If you are coming from the old school where MRC (Manual Reference Counting) was being used for memory management, you definitely know how much headache developers suffer to manage memory in iOS. With iOS 5, Apple introduced ARC (Automatic Reference Counting), and life became easier in terms of memory management. Though ARC manages your memory automatically, some mistakes may ruin your memory with no mercy if you didn't understand the concept of memory management.

Getting ready

Before checking how to manage memory and avoid some common mistakes, I would like to highlight some notes:

  • Assigning a class instance to variable, constant, or properties will create a strong reference to this instance. The instance will be kept in memory as long as you use it.

  • Setting the reference to nil will reduce its reference counting by one (once it reaches zero, it will be deallocated from memory). When your class deallocated from memory, all class instance properties will be set to nil as well.

How to do it...

  1. Create two classes, Person and Dog, with a relation between them, as shown in the following code snippet (this code snippet has a memory issue called reference cycle):

          class Dog{
            var name: String
            var owner: Person!
            init(name: String){
              self.name = name
            }
          }
          class Person{
            var name: String
            var id: Int
            var dogs = [Dog]()
            init(name: String, id: Int){
              self.name = name
              self.id = id
            }
          }
          let john = Person(name: "John", id: 1)
          let rex = Dog(name: "Rex")
          let rocky = Dog(name: "Rocky")
          john.dogs += [rex, rocky] // append dogs
          rex.owner = john
          rocky.owner = john
    
  2. Update the reference type of owner property in the Dog class to break this cycle:

          class Dog{
            var name: String
            weak var owner: Person!
            init(name: String){
            self.name = name
          }
        }

How it works...

We have started our example by creating two classes, Person and Dog. The Person class has one-to-many relation to the Dog class via the property array dogs. The Dog class has one-to-one relation to class Person via the property owner. Everything looks good, and it works fine if you tested, but unfortunately we did a terrible mistake. We have a retain cycle problem here, which means we have two objects in memory; each one has a strong reference to the other. This leads to a cycle that prevents both of them from being deallocated from memory.

This problem is a common problem in iOS, and not all developers note it while coding. We call it as parent-child relation. Parent (in our case, it's the Person class) should always have a strong reference to child (the Dog class); child should always have a weak reference to the parent. Child doesn't need to have strong reference to parent, as child should never exit when parent is deallocated from memory.

To solve such a problem, you have to break the cycle by marking one of these references as weak. In step 2, we see how we solved the problem by marking the property owner as weak.

There's more...

The reference cycle problem can happen in situations other than relations between classes. When you use closure, there is a case where you may face a retain cycle. It happens when you assign a closure to a property in class instance and then this closure captures the instance. Let's consider the following example:

class HTMLElement { 
 
let name: String 
let text: String? 
 
lazy var asHTML: () -> String = { 
if let text = self.text { 
return "<\(self.name)>\(text)</\(self.name)>" 
        } else { 
return "<\(self.name) />" 
        } 
    } 
 
init(name: String, text: String? = nil) { 
        self.name = name 
self.text = text 
    } 
} 
 
let heading = HTMLElement(name: "h1", text: "h1 title") 
print(heading.asHTML()) // <h1>h1 title</h1> 

We have the HTMLElement class, which has closure property asHTML. Then, we created an instance of that class which is heading, and then we called the closure to return HTML text. The code works fine, but as we said, it has a reference cycle. The instance set closure to one of its property, and the closure captures the instance (happens when we call self.name and self.text inside the closure). The closure in that case will retain self (have a strong reference to the heading instance), and at the same time, heading already has a strong reference to its property asHTML. To solve reference cycle made with closure, add the following line of code as first line in closure:

[unownedself] in 

So, the class will look like this:

class HTMLElement { 
 
let name: String 
let text: String? 
 
lazy var asHTML: () -> String = { 
 
        [unownedself] in 
 
if let text = self.text { 
return "<\(self.name)>\(text)</\(self.name)>" 
        } else { 
return "<\(self.name) />" 
        } 
    } 
 
init(name: String, text: String? = nil) { 
        self.name = name 
self.text = text 
    } 
} 

The unowned keyword informs the closure to use a weak reference to self instead of the strong default reference. In that case, we break the cycle and everything goes fine.

Using error handling


In any iOS project, a lot of operations may fail and you have to respond to these errors in your project. Since Swift 2, a new mechanism has been added to the language for responding and dealing with errors in your project. You can now throw and catch errors when you do any operation that may fail for some reason. Suppose, you do some logic to request some data in a JSON format from a remote server and then you save this data in a local database. Can you imagine how many errors may happen for these operations? Connection may fail between your app and the remote server, failing to parse the JSON response, database connection is closed, database file doesn't exist, or another process is writing in database and you have to wait. Recovering from these errors allows you take the appropriate action based on the error type.

Getting ready

Before starting to learn how to handle errors in Swift, you first have to be familiar with how to represent in errors that are going to happen in your program. Swift provides you with a protocol called ErrorType that your errors types should adopt. Then, to represent errors, here comes the role of enumerations to help you. You create a new enum, which lists all error cases, and this enum should conform to the ErrorType protocol. The syntax of using enum with ErrorType will be something like this:

enum DBConnectionError: ErrorType{ 
  case ConnectionClosed 
  case DBNotExist 
  case DBNotWritable 
} 

As we see it's pretty straightforward. You create enum representing the error that conforms to ErrorType protocol, and then list all errors as cases in the enum.

How to do it...

  1. As usual, let's create a new playground named ErrorHandling.

  2. Let's create now a new error type for a function that will sign up a new user in a system:

          enum SignUpUserError: ErrorType{ 
     
            case InvalidFirstOrLastName 
            case InvalidEmail 
            case WeakPassword 
            case PasswordsDontMatch 
          } 
    
  3. Now, create the sign up function that throws errors we made in the previous step, if any:

          func signUpNewUserWithFirstName(firstName: String, lastName: String, email: String, password: String, confirmPassword: String) throws{ 
     
            guard firstName.characters.count> 0 &&lastName.characters.count> 0 else{ 
     
              throw SignUpUserError.InvalidFirstOrLastName 
            } 
     
            guard isValidEmail(email) else{ 
              throw SignUpUserError.InvalidEmail 
            } 
     
            guard password.characters.count> 8 else{ 
              throw SignUpUserError.WeakPassword 
            } 
     
            guard password == confirmPassword else{ 
              throw SignUpUserError.PasswordsDontMatch 
            } 
     
            // Saving logic goes here 
     
            print("Successfully signup user") 
     
            } 
     
            func isValidEmail(email:String) ->Bool { 
     
              let emailRegex = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}" 
              let predicate = NSPredicate(format:"SELF MATCHES %@", emailRegex) 
              return predicate.evaluateWithObject(email) 
            } 
    
  4. Now, let's see how to use the function and catch errors:

           do{ 
             trysignUpNewUserWithFirstName("John", lastName: "Smith", email: "john@gmail.com", password: "123456789", confirmPassword: "123456789") 
           } 
           catch{ 
             switch error{ 
               case SignUpUserError.InvalidFirstOrLastName: 
                 print("Invalid First name or last name") 
               case SignUpUserError.InvalidEmail: 
                 print("Email is not correct") 
               case SignUpUserError.WeakPassword: 
                 print("Password should be more than 8 characters long") 
               case SignUpUserError.PasswordsDontMatch: 
                 print("Passwords don't match") 
               default: 
                 print(error) 
             } 
           } 
    

How it works...

We started our code example by creating a new error type called SignUpUserError, which conforms to ErrorType protocol. As we see, we listed four errors that may happen while signing up any user in our system, such as invalid first name or last name, invalid e-mail, weak password, and passwords that don't match. So far, so good!

Then, we create a function signUpNewUserWithFirstName, which takes user input values, and as we can see, we have marked it with the throws keyword. The keyword throws says that this function may throw an error anytime during execution, so you be prepared to catch errors thrown by this method.

Inside the implementation of the function, you will see a list of guard statements that checks for user input; if any of these guard statements returned false, the code of else statement will be called. The statement throw is used to stop execution of this method and throw the appropriate error based on the checking made.

Catching errors is pretty easy; to call a function that throws error, you have to call it inside the do-catch block. After the do statement, use the try keyword and call your function. If any error happens while executing your method, the block of code inside the catch statement will be called with a given parameter called error that represents the error. We've created a switch statement that checks the type of error and prints a user-friendly statement based on the error type.

There's more...

The information that we previously presented is enough for you to deal with error handling, but still there are a couple of things considered important to be known.

Multiple catch statements

In the preceding example, you will notice that we've created a catch statement, and inside, we used a switch statement to cover all cases of error. This is a correct way, but for your reference, we have another way to do this. Consider the following:

catch SignUpUserError.InvalidFirstOrLastName{ 
 
} 
catch SignUpUserError.InvalidEmail{ 
 
} 
catch SignUpUserError.WeakPassword{ 
 
} 
catch SignUpUserError.PasswordsDontMatch{ 
 
} 

After the do statement, you can list catch statement with the type of error that this statement will catch. Using this method has a condition that the catch statements should be exhaustive, which means it should cover all types of errors.

Disable error propagation

Functions that usually throw an error, in some cases, don't throw an error. In some cases, you may know that calling a function like these with some kind of parameters will never throw an error. In that case, Swift gives you an option to disable error propagation via calling this method with try! instead of try. Calling throwing functions via try! will disable error propagation, and if an error is thrown in that case, you will get a runtime error. So, it's better to take care while using try!.

Using generics to write generic and reusable code


Generic code is used to write reusable and flexible functionalities that can deal with any type of variables. This helps in writing reusable and clean code regardless of the type of objects your generic code deals with. An example of using generics is when you use Array and Dictionary. You can create an array of Int or String or any type you want. That's because Array is natively created and can deal with any type. Swift gives you the ability to write generic code very easily as you will see in this section.

Getting ready

Before learning how to write generic code, let's see an example of a problem that generics solve. I bet you are familiar with stack data structures and have been using it in one of the computer science courses before. Anyway, it's a kind of collection data structure that follows LIFO (Last in first out). It has very commonly used APIs for these operations, which are pop and push. Push inserts new item to the stack; pop returns the last inserted one. It's just a simple overview, as we will not explain data structures in this book as it's out of topic.

How to do it...

Here, we will create the stack data structure with/without generics:

  1. Create a new playground named Generics.

  2. Let's create the data structure stack with type Int:

          class StackInt{ 
            var elements = [Int]() 
     
          func push(element:Int) 
          { 
            self.elements.append(element) 
          } 
          func pop() ->Int 
          { 
            return self.elements.removeLast() 
          } 
          func isEmpty()->Bool 
          { 
            returnself.elements.isEmpty 
          } 
        } 
     
        var stack1 = StackInt() 
        stack1.push(5)    // [5] 
        stack1.push(10)  //[5,10] 
        stack1.push(20) // [5,10,20] 
        stack1.pop()   // 20 
    
  3. Let's see the same created stack but with a generics fashion:

          class Stack <T>{ 
            var elements = [T]() 
            func push(element:T) 
            { 
              self.elements.append(element) 
            } 
            func pop()->T{ 
              return self.elements.removeLast() 
            } 
          } 
     
          var stackOfStrings = Stack<String>() 
          stackOfStrings.push("str1") 
          stackOfStrings.push("str2") 
          stackOfStrings.pop() 
     
          var stackOfInt = Stack<Int>() 
          stackOfInt.push(4) 
          stackOfInt.push(7) 
          stackOfInt.pop() 
    

How it works...

The first class we created, StackInt, is a stack that can work only with the Int type. It's good if you are going to use it with Int type only. However, a famous data structure like it can be used with different types, such as String or Double. It's not possible to create different stack class for each type, and here comes the magic of generics; instead we created another class called Stack marked with <T> to say it's going to deal with the generic type T, which can be Int, String, Double, and so on. Then, we create two stack instances, stackOfStrings and stackOfInt, and both share the same code as their class is built with generics.

Left arrow icon Right arrow icon
Download code icon Download Code

Key benefits

  • Create high performance iOS apps with a focus on application development APIs and techniques
  • Enrich your UI skills with UIStoryboard, Autolayout, Size classes, and Container view
  • Produce enhanced results with iOS 10 as a result of learning and implementing pro-level practices, techniques, and solutions

Description

Do you want to understand all the facets of iOS programming and build complex iOS apps? Then you have come to the right place. This problem-solution guide will help you to eliminate expensive learning curves and focus on specific issues to make you proficient at tasks and the speed-up time involved. Beginning with some advanced UI components such as Stack Views and UICollectionView, you will gradually move on to building an interface efficiently. You will work through adding gesture recognizer and touch elements on table cells for custom actions. You will work with the Photos framework to access and manipulate photos. You will then prepare your app for multitasking and write responsive and highly efficient apps. Next, you will integrate maps and core location services while making your app more secure through various encryption methods. Finally, you will dive deep into the advanced techniques of implementing notifications while working with memory management and optimizing the performance of your apps. By the end of the book, you will master most of the latest iOS 10 frameworks.

What you will learn

[*] Build your own custom UIViews through code or the interface builder [*] Implement a dynamic and interactive interface in an iOS app [*] Work on various graphics related elements and the process of using them together to make meaningful shapes. [*] Use the side over and split view to interact with multiple apps concurrently [*] Encrypt JSON calls to make the app more secure [*] Work on web markup feature to enhance search optimization

What do you get with eBook?

Product feature icon Instant access to your Digital eBook purchase
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
Buy Now

Product Details


Publication date : Mar 31, 2017
Length 520 pages
Edition : 1st Edition
Language : English
ISBN-13 : 9781786460981
Vendor :
Apple
Category :

Table of Contents

22 Chapters
iOS Programming Cookbook Chevron down icon Chevron up icon
Credits Chevron down icon Chevron up icon
About the Author Chevron down icon Chevron up icon
About the Reviewer Chevron down icon Chevron up icon
www.PacktPub.com Chevron down icon Chevron up icon
Customer Feedback Chevron down icon Chevron up icon
Preface Chevron down icon Chevron up icon
1. Swift Programming Language Chevron down icon Chevron up icon
2. The Essentials Chevron down icon Chevron up icon
3. Integrating with Messages App Chevron down icon Chevron up icon
4. Working with Interface Builder Chevron down icon Chevron up icon
5. Working with UITableView Chevron down icon Chevron up icon
6. Animations and Graphics Chevron down icon Chevron up icon
7. Multimedia Chevron down icon Chevron up icon
8. Concurrency Chevron down icon Chevron up icon
9. Location Services Chevron down icon Chevron up icon
10. Security and Encryption Chevron down icon Chevron up icon
11. Networking Chevron down icon Chevron up icon
12. Persisting Data with Core Data Chevron down icon Chevron up icon
13. Notifications Chevron down icon Chevron up icon
14. App Search Chevron down icon Chevron up icon
15. Optimizing Performance Chevron down icon Chevron up icon

Customer reviews

Filter icon Filter
Top Reviews
Rating distribution
Empty star icon Empty star icon Empty star icon Empty star icon Empty star icon 0
(0 Ratings)
5 star 0%
4 star 0%
3 star 0%
2 star 0%
1 star 0%

Filter reviews by


No reviews found
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

How do I buy and download an eBook? Chevron down icon Chevron up icon

Where there is an eBook version of a title available, you can buy it from the book details for that title. Add either the standalone eBook or the eBook and print book bundle to your shopping cart. Your eBook will show in your cart as a product on its own. After completing checkout and payment in the normal way, you will receive your receipt on the screen containing a link to a personalised PDF download file. This link will remain active for 30 days. You can download backup copies of the file by logging in to your account at any time.

If you already have Adobe reader installed, then clicking on the link will download and open the PDF file directly. If you don't, then save the PDF file on your machine and download the Reader to view it.

Please Note: Packt eBooks are non-returnable and non-refundable.

Packt eBook and Licensing When you buy an eBook from Packt Publishing, completing your purchase means you accept the terms of our licence agreement. Please read the full text of the agreement. In it we have tried to balance the need for the ebook to be usable for you the reader with our needs to protect the rights of us as Publishers and of our authors. In summary, the agreement says:

  • You may make copies of your eBook for your own use onto any machine
  • You may not pass copies of the eBook on to anyone else
How can I make a purchase on your website? Chevron down icon Chevron up icon

If you want to purchase a video course, eBook or Bundle (Print+eBook) please follow below steps:

  1. Register on our website using your email address and the password.
  2. Search for the title by name or ISBN using the search option.
  3. Select the title you want to purchase.
  4. Choose the format you wish to purchase the title in; if you order the Print Book, you get a free eBook copy of the same title. 
  5. Proceed with the checkout process (payment to be made using Credit Card, Debit Cart, or PayPal)
Where can I access support around an eBook? Chevron down icon Chevron up icon
  • If you experience a problem with using or installing Adobe Reader, the contact Adobe directly.
  • To view the errata for the book, see www.packtpub.com/support and view the pages for the title you have.
  • To view your account details or to download a new copy of the book go to www.packtpub.com/account
  • To contact us directly if a problem is not resolved, use www.packtpub.com/contact-us
What eBook formats do Packt support? Chevron down icon Chevron up icon

Our eBooks are currently available in a variety of formats such as PDF and ePubs. In the future, this may well change with trends and development in technology, but please note that our PDFs are not Adobe eBook Reader format, which has greater restrictions on security.

You will need to use Adobe Reader v9 or later in order to read Packt's PDF eBooks.

What are the benefits of eBooks? Chevron down icon Chevron up icon
  • You can get the information you need immediately
  • You can easily take them with you on a laptop
  • You can download them an unlimited number of times
  • You can print them out
  • They are copy-paste enabled
  • They are searchable
  • There is no password protection
  • They are lower price than print
  • They save resources and space
What is an eBook? Chevron down icon Chevron up icon

Packt eBooks are a complete electronic version of the print edition, available in PDF and ePub formats. Every piece of content down to the page numbering is the same. Because we save the costs of printing and shipping the book to you, we are able to offer eBooks at a lower cost than print editions.

When you have purchased an eBook, simply login to your account and click on the link in Your Download Area. We recommend you saving the file to your hard drive before opening it.

For optimal viewing of our eBooks, we recommend you download and install the free Adobe Reader version 9.