Chapters

Hide chapters

Expert Swift

Second Edition · iOS 18.5 · Swift 6.1 · Xcode 16.3

12. Objective-C Interoperability
Written by Ehab Amer

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

You love Swift. Otherwise, you probably wouldn’t be reading this book! Heck, you might’ve even started your iOS journey with Swift without touching its much more battle-tested ancestor — Objective-C.

Objective-C is a Smalltalk-inspired superset on top of the C programming language. Moreover, It is a heavily used language in legacy codebases and apps that have been in production for many years. Put that together with the fact that most third-party SDKs are still provided in Objective-C for compatibility reasons, and it could turn out to be quite useful to know at least some key portions of it.

In your own apps, you’ll often have a sizable Objective-C codebase that just doesn’t feel at home inside your Swift code or want to use some of your shiny new Swift code in your Objective-C code.

Luckily, Apple provides relatively thorough interoperability — hence, the ability for Objective-C code to “see” Swift code and vice versa. But there’s only so much Apple can do for you automatically, which is where this chapter kicks in!

What you’ll learn

As a relatively new father, tracking what my child does takes up a huge part of my day. Like other things that need optimizing, there’s an app for that!

In this chapter, you’ll work on an app called BabyTrack, which lets you track what your baby does: eat, drink, sleep, etc.

The version of the app you’ll start with uses its own Objective-C based framework — BabyKit. You’ll spend this entire chapter creating a wholesome experience for consumers of both the Objective-C and Swift portions of your codebase in a way that feels as if it were designed for either.

Getting started

Open the starter project in the projects/starter folder and then BabyTrack.xcodeproj.

Bridging and umbrella headers

Bridging and umbrella headers are two headers that do the same thing, in essence: They notify their consumers which portions are exposed to their use, in the header’s context.

Umbrella header

You can think of an umbrella header as the master header of a framework. In the context of a framework, it tells its consumers which framework portions are publicly available without the headers needing to be manually imported one by one.

Bridging header

A bridging header belongs to the scope of an app instead of a framework. As its name suggests, it bridges Objective-C files into Swift by exposing any headers imported into it to your Swift files.

Making the app launch

To expose ViewController.h, start by right-clicking Boilerplate in your project navigator and selecting New File from Template…. Then, select Header File and name it BabyKit-Bridging.h. Make sure you select the BabyTrack target.

Enriching FeedItem

Open FeedCell.m and find configureWithFeedItem:. You’ll notice that there is absolutely nothing in this configuration method that modifies the title or icon on the left. The only “right” piece is the subtitle — the date itself.

Exposing Swift to Objective-C

Although you needed a fancy bridging header to bridge Objective-C code into Swift, going the other way is quite simple. Simply annotate Swift methods, classes and properties with @objc, and they’ll be available to your Objective-C code.

What can and can’t be directly bridged

Bridging happens automatically in many cases. For example, when you create a Swift class that inherits from an Objective-C class or write Swift code that extends an Objective-C object, that Swift code is automatically exposed to your Objective-C code as long as you annotate it with @objc. Exceptions to this are Swift-only features, such as:

Extending without extending

To still support accessing the Swift-based extensions on FeedItemKind, you can simply wrap the properties in methods. Go back to FeedItem+Ext.swift and add the following static methods to the FeedItem extension:

Setting explicit names for @objc members

Although a method called emoji(for:) makes sense for Swift, Objective-C consumers would expect a method simply called emojiForKind:. The automatic bridging doesn’t really get this correctly, but no worries!

Improving Objective-C enums

As you noticed before, your FeedItemKind enum is bridged as a non-finite standard C enum, which is not optimal when working in a Swift codebase where you’re used to working with a finite set of strongly typed cases.

Objective-C and … SwiftUI ?!

You heard it. In this section, you’re going to pretend your Objective-C app doesn’t exist. You’ve just been handed this Objective-C framework, and you want to build a brand new SwiftUI app that uses it.

Improving nullability

Nullability in Objective-C is the parallel of using Optional in Swift. Generally speaking, these nullability constraints are automatically bridged in a decent way but not good enough for a demanding developer, such as yourself.

Setting up SwiftUI

With Feed optimized, it’s time for you to start working on the SwiftUI part of your app.

Improving FeedItemKind’s naming

There is already some room for improvement here from the Swift perspective. Notice the type FeedItemKind.

Understanding the problem with BabyKit.Feed

Although your UIKit-based Objective-C code uses a regular UITableView with an associated delegate and imperatively reloads the table and reads items from a Feed object, SwiftUI is quite different.

Refining the Feed object

Open Feed.h. Above the @interface line, add the following line:

Improving property mirroring with @dynamicMemberLookup

Right now, isBabySleeping simply mirrors legacyFeed.isBabySleeping. This is fine for a single item, but it can become quite tedious and full of boilerplate as you add more and more properties to your Objective-C Feed.

Finalizing FeedView

You have almost everything ready to go to finalize FeedView. Head over to FeedView.swift.

Reacting to tapping the action bar

Inside the AddFeedItemBar closure, add the following code:

Letting the user pick a moment photo

When the user wants to add a new moment, the Objective-C version of the app modally presents a PHPickerViewController to let the user pick a photo.

Key points

  • Objective-C is a powerful language, with relatively comprehensive bridging to Swift. When that automatic bridging isn’t enough, you can get as granular as you want with your Swift-specific customizations.
  • A bridging header exposes Objective-C headers inside your app, while an umbrella header exposes all Objective-C headers for a given framework.
  • Because nullability is a given in Objective-C’s dynamic nature, you can use _Nullable or _Nonnull to define the appropriate nullability or use NS_ASSUME_NONNULL_BEGIN and NS_ASSUME_NONNULL_END to make an entire definition block non-nullable.
  • You can use the @objc or @objc(name) annotation to expose Swift types to Objective-C.
  • You can use NS_CLOSED_ENUM to bridge Objective-C enums into entirely Swifty enums.
  • Although full-blown generics aren’t fully supported, lightweight generics pack quite a powerful punch for most common needs.
  • If you want to have Swift-specific names for properties, objects or even global functions as getters, use NS_SWIFT_NAME.
  • If you want to hide an Objective-C implementation so you can wrap it in an improved Swift interface, use NS_REFINED_FOR_SWIFT. This allows you to leverage Swift-specific features that are otherwise inaccessible to your Objective-C based code.
  • If you want to make a method or property entirely unavailable to Swift, use NS_SWIFT_UNAVAILABLE.
  • Don’t give up on Objective-C — it’s here to stay.

Where to go from here?

Congratulations on completing the BabyTrack app! You’ve modernized an old-school Objective-C SDK and app, while wrapping most SDK components with modern Swift-centric APIs and keeping the same interface for your Objective-C consumers, creating a wholesome experience that feels as if you designed the code for either language.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now