Migrating Objective-C Framework to Swift.png

Migrating Objective-C Framework to Swift

Before you start converting Objective-C code to Swift first you will have to ask yourself if is it worth it. Why do you want to do it?

For us it was easy, we wanted to have a framework that will be fully compatible with Swift, use new patterns and features found only in Swift, be easier to read, and maintain and have a modern codebase for new developers joining the team.

I thought that the process would be fairly straightforward, but I was wrong. I faced a lot of challenges that I will describe in order to help you out if you decide to start with this project (and also to have a reference for myself if I have to do it again).

Let’s start!

Step 1 – Modernize your Objective-C code

First of all make sure that every new file you create, if possible, is in Swift, if not, modernize your Objective-C code. Here are some examples:

  • Instead of id as a return type your class uses instancetype, which will improve type safety.
  • Use properties instead of instance variables where applicable. When creating properties you get by default getter and setter methods automatically, you will also have access to property attributes such as: weak, assign, retain etc.
  • Change enum to NS_ENUM, this will improve code competition in Xcode and will specify the type and size of your enums.

This is recommended by Apple and you can read more details about it on this documentation page.

Step 2 – Importing Swift code in ObjC

Migrating Objective-C Framework to Swift 2.png

To import Swift code in Objective-C first of all you need to have an Objective-C bridging header. This is generated automatically by Xcode when you create or insert Swift files into your project.

If you don’t have a bridging header, it’s very simple to create one manually.

Create a new Swift file and the popup will appear like on the image above, just click on “Create Bridging Header”.
This generated interface header should be named as follows: “YourSDK-Swift.h”. This is important since we will be using it to import Swift code into Objective-C.

Migrating Objective-C Framework to Swift 3.png

Next, go to “Build Settings” of your framework project and locate “Defines Module” and set it to “YES”

Now that we are all set up, we can import our Swift file into Objective-C like this:

#import <YourSDK/YourSDK-Swift.h>

and you can start using Swift if you are lucky.

Step 3 – How to use Swift code inside ObjC

We are not done yet. To use Swift, we need to make some adjustments to our code. The generated header file is part of the framework's public interface and only Swift classes which are marked as “Public” or “Open” will be accessible from Objective-C code.

They also have to be marked with @objc before the class name and they have to inherit from NSObject. Like so:

@objc public class MyClass: NSObject { }

This also applies to everything else, functions, for example, need to be marked as “Public” and have @objc in front, like so:

@objc public func doSomething().

You can also add @objcMembers instead of @objc in front of the class name and it will apply to all functions in your class so you don’t have to add it separately. For example: 

@objcMembers public class MyClass: NSObject { }
public func doSomething().

Step 4 – Making it private

Now we can start using Swift inside Objective-C code, yay ?. But wait, we have a huge problem now.

Adding public in front of our Swift classes and methods we are making them, you guess, public. Our framework needs to have private classes and we cannot proceed like this. 

So what do we do? Well, there is a way:

  1. Let’s take our test class, we need to add Objective-C annotation like:
@objc (MyClass)
public class MyClass: NSObject { }

@objc (doSomething)
func doSomething().
  1. Then we create Objective-C “.h” file, let’s call it “SwiftInterfaceFiles.h”, where we create the interface of our Swift class and method:
@interface MyClass()
- (void)doSomething;
  1. The last step is to import “SwiftInterfaceFiles.h” into our Objective-C “.m” file.
#import “SwiftInterfaceFiles.h”

MyClass *instanceOfMyClass = [MyClass new];
[instanceOfMyClass doSomething];

Step 5 – Does everything work?

So far so good. We can create new Swift code, create Objective-C wrappers to handle public problems and use Swift. There are some restrictions, however. 

Here are some Swift features that are not available in Objective-C:

  • We can use only Swift enums of type Int
  • Global variables
  • Type aliases
  • Structs
  • Subclassing from Swift
  • Top-level functions

If you encounter an issue when a Swift method cannot be seen in Objective-C and you have done everything as described above, that means, probably, you are trying to use Swift features that are not available in Objective-C. One way to deal with this is to make a wrapper class that can process these unavailable features and be called when talking to the Objective-C classes.

Step 6 – The end?

We covered how to use Swift in Objective-C, for that part we are good, it’s the end.

But what if I want to use Objective-C in my Swift code? Well, that’s a different story.

Your framework probably has a header file where you are exposing files to be used in projects. Those files are public and can be used in Swift. That’s great! The problem comes when we want to use private Objective-C code in Swift. We cannot access it. We cannot make it public, it should stay private.

Unfortunately, Apple doesn’t have a solution for this, they say to make it public, which is not really good practice. We could use modulemaps but that’s messy and sometimes doesn’t work and our private files are not really private, they can still be accessed from outside.

The only viable solution I have found is in this article.

That’s it for now, thank you for reading, and hopefully, Apple will come up with a better solution for using Objective-C private files in Swift. Until we meet again.

Stefan Markovikj

Jul 29, 2021




Objective CSwift

Let's talk.

By submitting this, you agree to our Terms and Conditions & Privacy Policy