featured

By Steven StevensonJuly 19, 2014

Biggest Day-to-Day Changes from Obj-C to Swift (Part 2)

Alright, you've seen Swift a little bit here and there. You've seen some cool block syntax—now let's try to bring the main parts together so that we can start building objects and classes.

Swift offers many new exciting features that Objective-C has not taken advantage of. With a base understanding of how to use Swift, you can start to venture into the full utility that Swift offers.

1. Func-tions

DECLARATION

If you’re a long-time developer, Swift may look familiar already. Its basic coding style is similar to JavaScript, but maintains much of the C structure that makes it even more powerful.

For those who want a quick refresher, the basics of the the Obj-C function are:

      • instance or class method,
  1. (void) return type,
  2. someFunction:, name of the function, (NSString *someString)
  3. the parameters of the function.

Objective-C is special in that you can have additional names and parameters. Let’s take a look:

Objective-C:

- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{ ... }

Without going too far into Objective-C description, let’s talk about how Swift is different. Walking through previous function declaration, the class method vs. instance is a little different in Swift.

  1. An instance method is simply defined as func, whereas a class method is actually defined with class func. That’s right, you can have a class method inside a class object (both instance and class methods will be shown below).
  2. The return type is pushed to the end of the function with -> String.
  3. The name of the function only differs to the first part of the Obj-C’s name or description. From the previous example, that means that the name would be: tableView( ... ), where everything inside of the parentheses becomes argument descriptors and types.
  4. The parameters keep their names—tableView and indexPath in this example, but indexPath gets an added descriptor didSelectRowAtIndexPath.

The Result:

Swift:

func tableView( tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) { ... }

Recall the ‘!’? That means that the tableView that is sent is forcible unwrapped for the method to use. Most times you will use non-optional variables, but you may find it helpful for declaration (specifically for delegate methods).

Now, let’s take another look. Below, there are two examples of declaring functions—one instance and one class.

Objective-C (SomeObject.m):

@implementation SomeObject {
    + (void)someClassMethodWithoutParameters() 
    { ...}

    - (NSString*)someInstanceMethodWithInteger:(NSInteger)integer andString:(NSString*)string 
    { ... }
}

Swift

class SomeObject : NSObject {
    /*class method*/
    class func someClassMethodWithoutParameters() { ... }   

    /*instance method*/
    func someInstanceMethodWithInteger(integer: NSInteger, andString string:String) -> String { ... }

We have different methods types: class and instance. Within these, we have the return types that are following the name (in this case, the class method has no return object). Then we maintain the same feel of Objective-C by continuing the descriptor names inside of the parameters of the function. To see these methods in use, let’s see how they are called using Swift:

Swift:

/*using a class method*/
SomeObject.someClassMethodWithoutParameters();

/*using an instance method*/
var objInstance = SomeObject();
let value = objInstance.someInstanceMethodWithInteger(5, andString:"Hello");

If you noticed in the instance variable, we have the name of the function and then an internal “descriptor” for multiple parameters. This is where Swift really looks different from languages like JavaScript while trying to keep the same feel as Objective-C.

FUNCTION TYPES

The function type is Swift’s way of using blocks to pass functions around to be used in different scopes. Just like any variable, declaring a function type can infer the parameters and return variables. Here’s an example not using the inferred method:

Swift:

func multiplyIntegers(anInteger:Int, anotherInteger:Int) -> Int {
    return anInteger*anotherInteger
}

var multiplyFunctionType: (Int, Int) -> Int = multiplyIntegers;

This is very close to what Objective-C called blocks, but with the main change of being able to store our functions as variables. If we have a function that was defined in a super class, we can now take that function and pass it through our other functions.

TRAILING CLOSURES

Probably the most important aspect of function types is being able to have a method take a function type, similar to Objective-C’s completion blocks. In Objective-C you used blocks to define procedures to be called in another function’s procedure. In Swift, this is not different, but you have more options. You can pass other functions as function types, or use trailing closures (much like you would do with blocks):

Swift:

func doSomeActionAndCompleteWithBlock(closure:(Int) -> Int ) -> Int {
    return 5 + closure(10);
}

/* passing a function to the function */
func multipleTwiceAndPrint(anInt:Int) ->Int {
    return anInt*2
}
let multiplyFunc = multipleTwiceAndPrint;

/* returns 25 */
doSomeActionAndCompleteWithBlock(multiplyFunc);

/*adding a function with a trailing closure 
 returns 35
*/
doSomeActionAndCompleteWithBlock({ anInt in
    return anInt*3
})

Notice that the trailing closure has an extra feature. In this example we had an Int in just after the starting curly brace. This is because trailing closures still makes use of parameters, but they are defined at the start of the trailing closure. The formula from Apple’s docs is:

Swift:

{ (parameters) -> return type in

    statements
}

Read more about blocks in Writing Completion Blocks With Closures In Swift.

2. Tuples

A favorite data type of programmers now exists within an iOS framework: Tuples! Tuples are containers for a small collection of different types. For example, you can have a single variable (tuple) containing other data types such as integers, strings, and custom objects:

Swift:

/* storing code and string for httpStatus */
let httpStatus = (404, "Not Found")

/* returning an an integer and object, 
   possibly index and object in an array
*/
let someObjectFound = (25, someObject())

Sending Tuples can be very helpful when you want to send grouped information back from your function. An HTTP status is often represented as an array, but now you can use a more lightweight data type. Tuples have their places, such as defining quick references, and passing small amount of variables. Tuples are best used when you have literal types (“Hello”, 0, 50.20). They are not a replacement for arrays that can store large amounts of data; think of tuples as best used as a short array without any functional methods. Let’s see an example of how you can use tuples and when to use arrays:

Swift:

/* tuple use */
func giveMeAceOfSpades() -> (rank: Int, suit: String) {
    return (1, "Spades");
}

/* mix of using arrays and tuples */
func giveMeADeckOfCards() -> Array<(rank:Int, suit:String)>{
    var deckArray = Array<(rank:Int, suit:String)>();
    let suits = ["Spades", "Hearts", "Diamonds", "Clubs"];
    
    for (var s=0; s < suits.count; s++) {
        for (var r = 1; r <= 13; r++ ) {
            deckArray += (rank: r, suit: suits[s]);
        }
    }
    
    return deckArray;
}
    
let aceSpades = giveMeAceOfSpades();
print(aceSpades.rank) /* gives the rank of 1 */

var deck :Array<(rank:Int, suit:String)> = giveMeADeckOfCards()
deck.removeLast()   /* remove the last tuple in array */

To summarize, Tuples are great quick data types that can store information quickly and retrieve it fast. Tuples do not have methods and are best for sharing small groups of information. If your data needs to contain state, methods, or private information, you should use class objects.

Unlike an array, Tuples can contain different types and, for that reason, are great for storing multiple types. This is because Tuples use generic types.

3. Generics

What if you wanted to use a class or a function across multiple types? Say there was a swap function for objects, you could create a swap for Integer type and swap for String type, but why not use a single method to swap? This is generics!

In generics, you put a placeholder in angle brackets to represent a type to be filled out later. In this example, we use a swap function to be able to swap any two elements in memory. The objects swap but are replaced in each other’s memory referenced so that you can ensure that you are accessing the swapped value.

Swift:

func swapper<T>(inout x: T, inout y: T) {
    let tmp = x
    x = y
    y = tmp
}

First, we recognize that T is the generic placeholder. When we call the method, we will want to replace that T with the type that we want (String, Integer, Array, etc). Notice that we are using T also as the parameter types. We do this to ensure that all generic types in this function are the same. Next we see this odd inout descriptor in our parameter list, which is important because we want to modify the parameter that is given to the function. Here we use & before the passed variables to say that we want to edit that reference in memory. To call the swap, here are a couple of examples:

Swift:

let arr = ["Hello", "World"]
swapper(&arr[0],&arr[1])
/* arr now has: ["World", "Hello"] */

let arr2 = ["Cheese", "Burger"]

swapper(&arr[0], &arr2[0])
/*arr now has: ["Cheese", "Hello"]
  arr2 now has: ["World", "Burger"] */

An interesting thing about declaring constants with let is that these constants cannot be modified from their original declaration. In this instance, we have two constants of array length 2. That is all that really matters. Changing the contents of these two constants is feasible because it doesn’t affect the original constant. Constants, however, do not allow an increase or decrease in the size of arrays.

You may not have realized that you were using generics already. When we use an array or dictionary, we have to tell specifically what is being stored in there. Swift uses generics to define these types so that we can do:

Dictionary<String, data-preserve-html-node="true" String> 

or:

Array<Integer data-preserve-html-node="true"> 

without having to have custom array or dictionary types for each basic type.

Another example of using generics is in a class. Say that we wanted to create a small object that we call a stack. This stack can take any type, but even more we want two or more stacks of different types:

Swift:

struct Stack<T> {
    var elements = T[]()

    mutating func push(element: T) {
        elements.append(element)
    }

    mutating func pop() -> T {
        return elements.removeLast()
    }
}

var intStack = Stack<Int>()
intStack.push(50)
/* stack now contains 50 */ 

var stringStack = Stack<String>()
stringStack.push("Hello")
/* stack now contains "Hello" */

Using generics allows you to make it more or less repetitive, and gives the chance to build very powerful frameworks. All programmers should try to attain the most DRY code possible, and generics are your friend.

4. Classes

We gave a quick look at classes with declaring class methods above. Let’s look more into how Swift uses classes in the UIKit framework, starting with a controller class:

Swift

import UIKit

class ViewController: UIViewController, UITableViewDataSource {
    @IBOutlet var textLabel : UILabel
    var dataArray:Array<AnyObject>?

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
        return UITableViewCell()
    }

    func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
    
        return 0
    }
    
    @IBAction func buttonPushed(sender : AnyObject) {
        /* do something */
    }
}

The first thing to note is that because there is no header, we are defining our class method on a global scope. Over time, Apple will announce access control methods (private, protected, and public), but, for this example, let’s assume that all of methods are global. Next, we see that we create a controller class ViewController that is the subclass of UIViewController. We know this because it is the first element in the class’s extensions. We are also extending this class to conform to the UITableViewDataSource protocol.

Next we see class properties. Here we have an @IBOutlet to a UILabel in the storyboard. You won’t see too many @ in swift, but they are definitely around; you will also see them for @IBActions. Notice that our dataArray variable is of type optional. This is common, because your array likely won’t have any data yet. We do this so that we can load that data from CoreData or other data sources. We also use generics to say that the array can contain any object, because at this point we are unsure of what is going to be stored in that array.

Overriding a function is important in the UIKit framework because it informs the compiler that this function is part of this parent’s class hierarchy. This is easy to miss when you switch over, and Xcode will happily remind you that override is necessary for functions that are defined up in the inheritance hierarchy. The should be self-explanatory and is just the syntax difference between Swift and Objective-C. For comparison, let’s see what is different line for line:

Class definition:

Objective-C:

/* in ViewController.h */
@interface ViewController : UIViewController <UITableViewDataSource>

@end

Swift removes the necessity for a header file and puts everything in the implementation of the class. Now you just need one file for ViewController.swift

Swift:

class Viewcontroller : UIViewController, UITableViewDataSource

The next thing that we need for any class are Property definitions/declarations. Let’s take a look at some IBOutlets as they are also represented differently in Swift.

Objective-C:

@property (nonatomic, weak) IBOutlet UILabel * textLabel;
@property (nonatomic, strong) NSMutableArray * dataArray;

Note the simplicity of Swift’s declarations and how IBOutlet has been moved to the front of the variable declaration.

Swift:

@IBOutlet var textLabel : UILabel
var dataArray:Array<AnyObject>?

Next, we establish the methods of the class. Here, we showcase the required UITableViewDatasSource methods:

Objective-C:

- (void)viewDidLoad
{
    [super viewDidLoad];
}

- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexpath {
    return [[UITableViewCell alloc] init]
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 0;
}

- (IBAction)buttonPushed(id)sender {
    /* do something */
}

Recall that functions in Swift keep the same feel of Objective-C functions, in that the secondary parameter names or descriptors are still maintained inside the parameter block:

Swift:

override func viewDidLoad() {
    super.viewDidLoad()
}

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
    return UITableViewCell()
}

func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {    
    return 0
}
    
@IBAction func buttonPushed(sender : AnyObject) {
    /* do something */
}

Next…

That’s just the tip of the iceberg! In the coming months, there will be exciting new design flow, as well as the new features that Swift brings that will broaden our perspectives on how we design, develop, and deploy apps in the future.

Until next time!

Biggest day-to-day changes from Obj-C to Swift (Part 1)


Get job-ready in Swift »