SSG logoSubscribe
Digital art by Anonymous

Swift and Objective-C Interoperability

Interoperability of programming languages is the ability for two or more languages to interact as part of the same system. Interoperability can offer many benefits for developers. For example, the ability to reuse existing code and libraries across languages and platform, increases the portability and scalability of code. This can save time and effort while also improving knowledge sharing and code quality. There are many ways programming languages are interoperable with one another. Shared object models, foreign function interfaces, common runtime environments and virtual machines are some of these ways. For instance, web browsers frequently perform some kind of interoperability. Browsers parse HTML to handle information to be shown on a page, and language like JavaScript handle any real-time functionality on the page.

Interoperability in Apple Ecosystem

When you’re building an app target, it is legal for a target to be a bilingual target — one that contains both Swift files and Objective-C files. There are several reasons why a bilingual target could be helpful:

  • You might want to use third-party code written in Objective-C or Swift.
  • You might want to combine your own legacy project written in Objective-C.
  • Your app itself may have been written in Objective-C originally, and now you want to migrate part of it (or all of it, in stages) into Swift.

The critical question is how Swift and Objective-C access each other's code within a single target.

Unlike Swift, Objective-C files are unable to automatically see one another. Instead, each Objective-C file that needs to see another Objective-C file must be instructed explicitly to see that file, with an #import declaration at the top of the file.

Typically, an Objective-C class declaration is divided into two files: a header file (.h) containing the @interface section, and a code file (.m) containing the @implementation section. Importing is limited to .h files only, so if you want class members accesiable for other files, it must be defined in header files.

Visibility of Swift and Objective-C works through header files.

Visibility works on both way thanks to two special Objective-C header files:

How Swift sees Objective-C

When you add a Swift file to an Objective-C target, or an Objective-C file to a Swift target, Xcode offers to create a bridging header.

images/bridging-header

This is a header(.h) file in the project. Its default name is derived from the target name. It takes its name from the target name by default. However, the name is arbitrary. If you choose “Don’t Create”, you can create a header file manually and point to it in the Bridging Header build setting. If you #import an Objective-C header file in this bridging header, Swift will then be able to see it.

How Objective-C sees Swift

When you build your target, the relevant top-level declarations of your Swift files are automatically translated into Objective-C and are used to construct a generated interface header within the build folder for this target, deep inside your DerivedData folder.

images/bridging-header

Your Swift code is exposed to Objective-C in general through the generated interface header. Your Swift declarations will be visible to your own Objective-C files if you #import the generated interface header into every Objective-C file that requires access to them.

Interoperability at Compile Time

Let’s take closer look what happens when we build bilingual target at compiler level.

How does Swift import and use Objective-C declarations:

  • The Swift compiler phases are parse, semantic analysis, SILGen, IRGen and LLVM.
  • Swift compiler embeds Clang (the LLVM compiler) as a library. When you ask Swift to import a module, it attempts to load the module in Swift space by that name. If that fails, it ask Clang to load a Clang module by that name.
  • The Clang Importer, a Swift compiler component, will generate comparable Swift declarations for the Clang declarations in that module. The compiler is aware that these declarations actually correspond to declarations in Clang.
  • The Compiler checks if the declerations imported from Clang, it calls into Clang IRGen(LLVM’s intermediate representation, this is one of the crucial benefit of LLVM architecture) to generate the code needed.

How does Swift make “@objc” declarations usable from Objective-C:

  • Swift IRGen generate Objective-C-compatible metadata for these declarations. It also generate Objective-C-compatible wrapper functions—for Swift functions and methods that should be accessible from Objective-C.
  • There are some runtime data structures that were intentionally developed to work with Objective-C. For instance, native Swift root classes actually inherit from a class that conforms to the NSObject protocol. Swift automatically bridges certain types between the two languages, such as NSString and String, NSArray and Array.

To Sum Up

In a nutshell, Swift does not convert Swift code into Objective-C; nor does Clang convert Objective-C code into Swift. Instead, Swift and Clang both translate the source code of their respective languages into IR, which is subsequently passed to LLVM for machine code generation. Interoperation is enabled by various tricks. The header files which is created for visibility between swift and Objective-C is just one these tricks.