Building Mixed Swift and Objective-C Apps - OOP REVISITED - Understanding Swift Programming: Swift 2 (2015)

Understanding Swift Programming: Swift 2 (2015)

PART 4: OOP REVISITED

35. Building Mixed Swift and Objective-C Apps

It is quite easy to build an app using both Swift and Objective-C. The biggest problem is actually switching between languages: once you get used to not typing semicolons in Swift, it's hard to remember to put them in for Objective-C. And when you do, they look funny, as do many other things about the language.

You can create a project in either Swift or Objective-C, and then create files in the other language and call them from the language you started with.

Calling Objective-C Code From a Swift Project

We can see a simple example of calling Objective-C code from a project that is started up in Swift. This is the most common scenario. For example, you might have existing Objective-C code that you want to reuse in a new project.

The standard way to demonstrate code with Xcode is to use the “Single View Application” template. You first create a new project by going to File > New > Project and then select the "Single View Application" template. (“Application under IOS” should be selected in the left column.) Click on Next and you will get a window that asks for a Project Name (use “MixedCode1”), Organization Name (whatever you want to call yourself) and an Organization Identifier (anything). “Language” has a menu with “Swift” and “Objective-C” as choices and you should select “Swift”. Under Devices select iPhone and do not check the box for Core Data. Clicking Next again will give you a popup that allows you to select a place to put the project in your file system, “Desktop” is reasonable and clicking on “Create” will create the project files.

In the leftmost column you will see a file called "ViewController.swift" that contains the Swift code for the ViewController class, including a ViewDidLoad method as part of a template. (Single click on it.) The template looks like this:

override func viewDidLoad() {

super.viewDidLoad()

// Do any additional setup after loading the view, typically from a nib.

}

Just below the "Do any additional setup" line, add the following code:

1 print("Hi from the Swift code")

2 let hi: HiFromObjectiveC = HiFromObjectiveC()

3 hi.sayHi()

(If you are running this on an Xcode version earlier than Xcode 7, which has Swift 2, use “println” rather than “print”.)

This simple program starts up, writes "Hi from the Swift code" to the console log, and then calls an Objective-C method that writes "Hi from the Objective-C code" to the console log. The first line here does the "Hi from the Swift Code" greeting.

The second line creates an instance of the class HiFromObjectiveC and assigns a reference to it to the constant hi. The third line calls a method in the HiFromObjectiveC class, sayHi.

The sayHi method, written in Objective-C, looks like this:

- (void) sayHi {

NSLog(@"Hi from the Objective-C code");

}

To create the files to allow us to call this method, we mostly have to do the same thing we would do if we were adding a new class to an Objective-C project. We use Xcode to create a new class (a subclass of NSObject), selecting Objective-C as the language. With the Xcode menu at the very top of your screen, select File > New > File, then choose “Source” under iOS in the left column, and Cocoa Touch Class from the icons in the right column. Click on Next. In the next screen we provide a name for the class, e.g,. HiFromObjectiveC, choose the class that this is a subclass of, NSObject, and choose Objective-C as the language. (Make sure it is not Swift.)

After clicking Next, you should be prompted with the directory that you created the project in. If you click on Create, it will create two new files: HiFromObjectiveC.h and HiFromObjectiveC.m.

When Xcode creates these files, it will do one extra thing: it will ask you if you want a bridging header. See the screenshot below. You should say “Yes”.

It will then create a third file, called something like MixedCode1-Bridging-Header.h.

In the .h (header) file for HiFromObjectiveC, it will initially look like this (I've removed some blank lines):

#import <Foundation/Foundation.h>

@interface HiFromObjectiveC : NSObject

@end

You just need to add the following line after the @interface line:

- (void) sayHi;

This tells the outside world that the class HiFromObjectiveC has a method called sayHi that has no input parameters and that returns no values, and that can be executed from instances of the class.

Then you need to add the actual method to the .m (implementation) file. When you look at that file, it will initially look like this:

#import "HelloFromObjectiveC2.h"

@implementation HelloFromObjectiveC2

@end

You need to add the following after the line that starts with “@implementation”:

- (void) sayHi {

NSLog(@"Hi from the Objective-C code");

}

This implements the method sayHi.

We then need to do one more thing: There should be a file called something like mixedCode1-Bridging-Header.h. Click on it. It will look like this:

//

// Use this file to import your target's public headers that you would like to expose to Swift.

//

We add the following line to it:

#import "HiFromObjectiveC.h"

This tells the Swift code that there is an Objective-C class it can call and provides the name of the header file it can get information from about the interface for that class.

To build the project and run it, click on the “Run” button (shown above) in the upper left hand corner of the Xcode window:

If we run this project, we get the following:

Hi from the Swift code

2015-06-29 11:04:47.244 MixedCode1[1451:53739] Hi from the Objective-C code

The time and date stamps, etc. are because Objective-C used an NSLog statement rather than the print statement that would be used from Swift.

Calling Swift Code from an Objective-C Project

We can also create an Objective-C project and call Swift code from an Objective-C method.

We again create a new project in Xcode:

Xcode > File > New > Project

Again choose a template. iOS, Application should be in the left column. The templates are in the right column. Choose Single View Application from among the icons in the right column, then click on Next.

A window will pop up and you should fill in the information. Use “MixedCode2” for the project name this time. For the Language, this time choose “Objective-C” from the menu. Click on Next.

You will again be presented with a location in your file system where it will create the project. Click on “Create” and the project, with its files, will be created.

Instead of the file ViewController.swift, you will get a pair of files called "ViewController.h" and "ViewController.m". We’ll get to them later, but first we will create the Swift code. (I’m doing this in a particular order to avoid some error messages.)

Select Xcode > New > File, then select Source under iOS and the icon for a Cocoa Touch class. Click on “Next”.

In the following screen, we provide the class name HiFromSwift, and define it as a subclass of NSObject (which should be presented as the default). Make sure that the language is “Swift”—the default you will be presented with is “Objective-C”, and you will have to select Swift from the menu. Click on “Next”.

Xcode will then present the location in your file system where the new file will be saved, which should be the directory “MixedCode2”. Click on “Create”.

You will likely get a popup asking “Would you like to configure an Objective-C bridging header.” The appropriate answer is “No”, since you don’t need this kind of bridging header. Instead, a bridging header with a different name (your project name followed by “-Swift.h”) will be automatically generated so that the Objective-C class will know about your Swift class.

A new file HiFromSwift.swift, containing the code for the new Swift class, will be created. It will initially look like this (I've removed some blank lines):

import UIKit

class HiFromSwift: NSObject {

}

This makes the new Swift class a subclass of NSObject.

(If you like, you can edit this to make the new class a base class, but if you do, you should put @objc before the class keyword, so that the compiler knows to make this available to Objective-C.)

We add the following code just after the line starting with "class":

func sayHi () {

print ("Hi from the Swift code")

}

This implements a method sayHi in the class HiFromSwift that prints to the console log. If you are running Xcode 6, use “println” instead of “print”.

We now have created the Swift class and method, and can go back to the Objective-C code.

You should click on the “ViewController.m” file to edit it.

At the top of the file, you will see:

#import "ViewController.h"

You should add the following line just below it:

#import "MixedCode2-Swift.h"

This will allow the Objective-C code to see the Swift class. “MixedCode2-Swift.h” is the file that was automatically generated by Xcode.

Next, scroll down and you will see the same ViewDidLoad method that you did in the earlier example. The template looks just a little different:

- (void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loading the view, typically from a nib.

}

Add the following code just after the "Do any additional setup" line:

NSLog(@"Hi from the Objective-C code");

HiFromSwift *hi = [ [HiFromSwift alloc] init];

[hi sayHi];

This code will display “Hi from the Objective-C code” on the console log, and then create an instance of the (Swift) class HiFromSwift. It will assign a pointer to that new instance to the variable hi. It will then send a message to the new object hi specifying sayHi that will cause the method sayHi to be executed. Although it is calling a Swift method, it calls it in the way that it would call an Objective-C method.

You may see an error message or two, due to Xcode not immediately picking up the information in the header file, but they should disappear when you build the project.

Now click on the Run button to build and run the project. You should see something like the following in the console log:

2015-06-29 12:03:49.855 MixedCode2[1946:68413] Hi from the Objective-C code

Hi from the Swift code

You will not see the file "MixedCode2-Swift.h" in your project files. It is hidden, often very deep in the directory Library/Developer/ Xcode/DerivedData. If you are having difficulties, you might want to look at it to make sure that it has a reference to the Swift class you created.

The file "[name of project]-Bridging-Header.h" is only used for bridging to Objective-C files from Swift files, and it not needed for bridging to Swift files from Objective-C. However, bridging from Objective-C to Swift can be buggy, and sometimes the compiler wants to see it. It can be easier to just include a file with the correct name just to keep the compiler happy, even though it is not used. (Although I think that in the more recent versions of Xcode this bug is gone.)

This chapter applies to using mixed Swift and Objective-C code in an app. (Apple calls this "mix and match".) If you are building a framework, the procedure is slightly different. See the Apple documentation.

Hands-On Exercises

Go to the following web address with a Macintosh or Windows PC to do the Hands-On Exercises.

For Chapter 35 exercises, go to

understandingswiftprogramming.com/35