Objective-C To Swift Migration – Illustrated With A Demo App

Migrating an existing Objective-C project to Swift by gradually introducing Swift classes is a common strategy for legacy iOS apps, where it is typically not feasible to rewrite the entire app to take advantage of Swift features. Let’s look at how to approach this gradual migration scenario with a concrete example.

I like to provide a demo app with my posts and this one is no exception. To help readers who are starting to think about their migration strategy, a complete sample project in GitHub is provided to demonstrate introducing a shiny new Swift controller that is instantiated from a legacy Objective-C controller, with two way communication and interoperability between Objective-C and Swift.

Update 20 Feb 2017: I’ve updated the sample app to be Swift 3 compliant.

We’ll start out with a simple Objective-C app that has a MainViewController, and a second controller that I’ll call the ObjCViewController. The goal in this project is to add a SwiftViewController and launch it from the MainViewController. Since many legacy Objective-C apps use Xib files instead of Storyboards, I’ll use Xib files in this project, but rest assured that you can mix Swift and Objective-C controllers in the same Storyboard as well.

Note: The code in the Objective-C controllers is fairly standard, so I’m not going to walk through it all, but you can download the complete project to follow along or quickly run it. When we write the SwiftViewController we’ll take a look at the Swift code to briefly discuss some of the specifics.

With only Objective-C in the project, there are two controllers as referenced above, as well as a delegate protocol defined in its own file, PhraseUpdatedDelegate.h. This is what the project looks like.

Screen Shot 2016-07-25 at 11.30.53 AM

The main screen has the current phrase of the day, iOS Apps. It can be updated by clicking on ObjC Controller, which displays the controller to update the phrase and pass it back to the main screen using the delegate pattern. There is also a button for the planned Swift controller.

This is a simple app, but let’s keep it that way to easily illustrate how to add a new Swift controller into the project.

Here are the screens when the app is running and the phrase is updated via the Objective-C controller.

Simulator Screen Shot Jul 25, 2016, 12.39.47 PM
Simulator Screen Shot Jul 25, 2016, 12.58.11 PM
Simulator Screen Shot Jul 25, 2016, 12.40.00 PM

 

In this project, the new Swift controller will be the equivalent of the Objective-C version. This mimics a migration strategy of gradually replacing Objective-C with equivalent Swift functionality. Some projects have no short term intention of replacing the bulk of existing Objective-C, but would like to develop new features with Swift. The new controller we’ll implement in this project could equally be something new instead of the equivalent of an existing controller, so it illustrates either strategy for migrating from Objective-C to Swift.

Before getting started with Swift code, we need to tell Xcode that there will be Swift code in the app. Go to your target’s Build Settings, and under Build Options, you’ll see EMBEDDED_CONTENT_CONTAINS_SWIFT. Set this to YES, which tells Xcode to embed Swift standard libraries in the app. You should now have the following.

Screen Shot 2016-07-17 at 12.03.16 PM

Note: If you want to read more about this, see the Apple Technical Q&A. In a subsequent step, we’ll import an automatically generated header into Objective-C code to be able to call Swift classes.

The next thing we do is create the SwiftViewController Cocoa Class. Make sure it is a subclass of UIViewController, has language set to Swift, and check the Also create XIB file box, since we’re going to leave the Objective-C controller and view as they are for this project.

Screen Shot 2016-07-16 at 11.55.43 AM
As soon as you click Next, Xcode will prompt you as to whether you would like to create an Objective-C bridging header.

Screen Shot 2016-07-16 at 11.56.07 AM

Accept Xcode’s offer to automatically create the bridging header, and once files have been created, you’ll see the SwiftViewController, the corresponding Xib file, and the new bridging header which has the name format [Project Name]-Bridging-Header.h.

Screen Shot 2016-07-25 at 1.03.11 PM

The bridging header is needed if you want to call Objective-C from Swift code. When you are starting a new Swift app and want to utilize legacy Objective-C code and frameworks, you need this bridging header. In our case, we will indeed be calling Objective-C code when coming back from the SwiftViewController. Going to the SwiftViewController, we’ll be doing the reverse and calling Swift code from Objective-C, and our earlier step of telling Xcode to embed Swift standard libraries partially handles that case. Additional steps will follow shortly.

Header files for each Objective-C class you want to call from Swift need to be added to the bridging header. The only knowledge the SwiftViewController needs of the Objective-C code is the PhraseUpdatedDelegate. Therefore, the only import we need in the bridging header is the PhraseUpdatedDelegate.h. Let’s add this import to the ObjCToSwiftMigration-Bridging-Header.h, so that we can reference the existing Objective-C delegate from Swift in order to pass the new phrase of the day back to the MainViewController, which implements the delegate protocol.

The next step is to implement the specifics of the new Swift controller so it is equivalent to the original Objective-C controller. Since the goal is to duplicate the Objective-C controller, we’ll also define the user interface in a separate Xib file where the Custom Class is the SwiftViewController.

Note: When you create a Swift class and want to use it from Objective-C, if it is not a descendent of an Objective-C class, then you must mark it with the @objc keyword, such as @objc class MyClass: MyParentClass. You can read more about this in the Mix and Match documentation from Apple. It is a common misconception that you always need to use @objc, but as Apple states, you only need it if your Swift class is not a descendent of an Objective-C class. Therefore, we won’t be using it in this project, since the Swift class we will create is a UIViewController.

The code in the SwiftViewController class is illustrated below.

The only class this controller is using from the existing Objective-C classes is the PhraseUpdatedDelegate, which is imported via the bridging header. The delegate is declared as weak to avoid a retain cycle, and the passDataBack method is where the delegate is called to provide the new phrase. The delegate in this case is the MainViewController.

Once the Swift code has been prepared, in order to call it from Objective-C, there is one more step. Recall earlier we specified that we would mix and match Swift and Objective-C. You can see what Xcode now has setup by looking at Build Settings > Swift Compiler – Code Generation.

Screen Shot 2016-07-25 at 8.27.28 AM

You can see the ObjCToSwiftMigration-Bridging-Header.h, which is needed to call Objective-C from Swift. For calling Swift from Objective-C, Xcode created the ObjCToSwiftMigration-Swift.h header name (using the $SWIFT_MODULE_NAME variable). This header file doesn’t actually exist, but is created on the fly. It is needed in the Objective-C class that will call Swift code, so add an import for it in MainViewController.m. This is how the legacy Objective-C code will be able to create the new SwiftViewController, set relevant values, and then push the new controller onto the UINavigationController. Here is the completed MainViewController class so we can compare what happens for both the Objective-C and Swift controller button pushes.

Creating the Swift controller is done just as though it was an Objective-C controller, thanks to the Xcode-generated header file that exposes Swift files to Objective-C.

The app can now be launched to change the phrase of the day seamlessly from either the Swift or Objective-C controller. If this was a real app, the Objective-C controllers could be replaced one by one by new Swift controllers, or they could be left in place and new controllers implemented using Swift.

If you are planning to migrate an Objective-C app to Swift, I hope that being able to see and run a simple but complete sample app will help get your migration process off the ground.

The completed project is here on GitHub.

Leave a Comment: