Skip to content
tezvyn:
Menu

Writing React Native iOS Modules in Swift

MediumHow cards are made

React Native speaks Objective-C on iOS, so Swift needs an Objective-C wrapper to join. Write logic in Swift, expose it via an Objective-C class, and register with RCT_EXPORT_MODULE. Skip the bridging header and the compiler never sees your Swift code.

Why it exists

React Native's core iOS infrastructure is built on Objective-C and Objective-C++. The native module system expects classes and methods that conform to this runtime. Swift is the modern default for iOS development, but it cannot directly interface with the React Native bridge because the bridge relies on macros, dynamic dispatch, and C++ types that Swift does not natively expose. Bridging exists to let teams write native modules in Swift without rewriting React Native itself.

The mental model

Think of Swift as a guest at a party where everyone speaks Objective-C. Swift can attend, but only if an Objective-C host introduces it. The host is a thin Objective-C wrapper class that translates between the JavaScript bridge and your Swift implementation. The Swift code does the real work, but the Objective-C wrapper holds the name tag that React Native recognizes.

How it works

First, you enable a bridging header in your Xcode project so Objective-C can see Swift classes. Then you write your module logic in a Swift class. Next, you create an Objective-C wrapper that imports the generated Swift header and implements RCTBridgeModule. You expose methods using RCT_EXPORT_METHOD. The wrapper forwards calls to your Swift instance and returns results. The JavaScript layer invokes the Objective-C wrapper, which delegates to Swift behind the scenes.

When to use it

Use this pattern when you have an existing Swift library to expose, when your iOS team prefers Swift over Objective-C, or when you need modern iOS APIs like SwiftUI or Combine that are painful to access from Objective-C. It is also useful when you want to share business logic between a React Native app and a pure Swift iOS target.

When not to use it

Do not use this pattern if you only need simple synchronous calls that are already covered by community modules. If your module is performance-critical and called thousands of times per second, the extra hop through Objective-C can add overhead. Also avoid it if you are not comfortable debugging build issues in Xcode, because mixing Swift and Objective-C++ in the same project can produce cryptic linker errors when header search paths are misconfigured.

One canonical example

Imagine you are building a module that reads Apple Health data. You write a HealthKitManager class in Swift that queries step counts. You then create HealthKitBridge.m and HealthKitBridge.h in Objective-C. The header declares RCT_EXPORT_MODULE and an RCT_EXPORT_METHOD method called getDailySteps. The implementation imports YourProject-Swift.h, instantiates HealthKitManager, and forwards the JavaScript call. When JavaScript calls NativeModules.HealthKit.getDailySteps, the bridge finds HealthKitBridge, which delegates to your Swift HealthKitManager and returns the step count through a resolver.

Interview question

Why can a Swift class not directly become a React Native iOS module without an Objective-C wrapper?

  • a.Because Xcode prohibits Swift files from importing React Native headers directly without an Objective-C intermediary
  • b.Because the wrapper acts as a compiler that translates Swift method signatures into JavaScript-compatible promises
  • c.Because Swift lacks a JavaScriptCore runtime and the wrapper provides the JS execution environment
  • d.Because React Native's bridge expects Objective-C runtime features such as macros, dynamic dispatch, and C++ types that Swift does not natively exposeCorrect
Why?

The card states that React Native's bridge relies on Objective-C macros, dynamic dispatch, and C++ types that Swift cannot natively expose, so a wrapper is required for conformance. Option A is tempting because the card mentions Xcode linker errors when mixing languages, but those are build-configuration side effects, not the fundamental reason a wrapper is mandatory.

Just read this? Test yourself on what you have been reading.

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on react native — each one lists the topics its interview covers.

See open roles