React Native iOS模板转Swift遇黑屏,求转换方案及无桥接调用方法
Hey there! Let's tackle your React Native + Swift issues step by step— I’ve helped plenty of devs make this switch with RN 0.52, so let’s get your app out of that black screen and clarify the Swift bridging question.
First: Correct Steps to Convert RN Template to Swift AppDelegate (RN 0.52 Compatible)
The black screen usually comes from missing configuration steps or mismatched setup. Let’s walk through the exact process that works for this RN version:
1. Clean Up Old Objective-C Files
First, get rid of the default OC files:
- Delete
AppDelegate.mandViewController.mfrom your project (make sure to select "Move to Trash" to remove them entirely). - You can keep
main.mfor now, but we’ll handle that later.
2. Create Your Swift AppDelegate
- Make a new Swift file named
AppDelegate.swift. When Xcode asks if you want to create a bridging header, select "Don’t Create" (we don’t need it just for the AppDelegate, and I’ll explain why later). - Paste this code into the file, replacing
YourProjectNamewith the actual name of your RN project (the one you used when runningreact-native init):import UIKit import React @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var bridge: RCTBridge! func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Set up the JS bundle location let jsCodeLocation: URL #if DEBUG jsCodeLocation = RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index", fallbackResource: nil) #else jsCodeLocation = Bundle.main.url(forResource: "main", withExtension: "jsbundle")! #endif // Initialize RN bridge and root view bridge = RCTBridge(delegate: self, launchOptions: launchOptions) let rootView = RCTRootView(bridge: bridge, moduleName: "YourProjectName", initialProperties: nil) // Set up the window with RN's root view window = UIWindow(frame: UIScreen.main.bounds) let rootViewController = UIViewController() rootViewController.view = rootView window?.rootViewController = rootViewController window?.makeKeyAndVisible() return true } // Required RCTBridgeDelegate method func sourceURL(for bridge: RCTBridge!) -> URL! { #if DEBUG return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index", fallbackResource: nil) #else return Bundle.main.url(forResource: "main", withExtension: "jsbundle") #endif } // Handle deep links (optional but recommended) func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { return RCTLinkingManager.application(application, open: url, options: options) } }
3. Tweak Project Configurations
- Go to your project’s Build Settings, search for
Main Interface, and clear the field (since we’re using@UIApplicationMainto launch the app, we don’t need a storyboard entry point). - Check your
Info.plist: Make sureNSAppTransportSecurity>NSAllowsArbitraryLoadsis set toYES— this is required for RN 0.52 to load the local JS bundle in debug mode. - If you deleted
ViewController.m, double-checkInfo.plistand remove theMain storyboard file base nameentry (or set it to empty) so Xcode doesn’t try to load a non-existent storyboard.
4. Optional: Remove main.m
Since we’re using @UIApplicationMain on our Swift AppDelegate, Xcode will automatically generate the app’s entry point. You can safely delete main.m now— if you see an error, just double-check that the @UIApplicationMain annotation is at the top of your AppDelegate class.
5. Rebuild and Run
- First, run
npm installto make sure all dependencies are up to date. - In Xcode, clean the build folder with
Cmd+Shift+K, then build withCmd+B, and finally run withCmd+R. Make sure Metro is running (you can start it withnpx react-native startif it’s not).
Second: Can You Call Swift Code Without a Bridging Header in RN 0.52?
Short answer: Not fully, but you can avoid writing a custom bridging header for basic cases (like just using a Swift AppDelegate).
Here’s the breakdown:
- RN 0.52’s core bridge is still built on Objective-C. For RN to call your Swift code, Swift needs to expose itself to the Objective-C runtime.
- If you’re only replacing the AppDelegate with Swift (no need to expose custom Swift modules to RN), you don’t need a bridging header at all— the code we wrote above works because we’re conforming to RN’s Objective-C protocols (like
RCTBridgeDelegate) using Swift’s@objccompatibility. - If you want to create custom Swift modules that RN can call, you’ll need to use the auto-generated bridging files:
- When you create your first Swift module, Xcode will offer to make a
YourProjectName-Bridging-Header.h— keep this file (you don’t need to add anything to it for basic cases). - Mark your Swift class and methods with
@objcanddynamicso the Objective-C runtime can see them:@objc class SwiftUtils: NSObject { @objc dynamic func getGreeting() -> String { return "Hey from Swift!" } } - You don’t need to import anything into the bridging header— instead, Xcode auto-generates a
YourProjectName-Swift.hfile that Objective-C can use. For RN, you can just call the module directly from JS:import { NativeModules } from 'react-native'; const { SwiftUtils } = NativeModules; console.log(SwiftUtils.getGreeting());
- When you create your first Swift module, Xcode will offer to make a
Why Did Your Previous Attempt Result in a Black Screen?
Most likely one of these issues:
- You didn’t replace
YourProjectNamein themoduleNamefield ofRCTRootView— RN couldn’t find your root component. - The
Main Interfacesetting wasn’t cleared, so Xcode tried to load a default storyboard instead of RN’s root view. - Metro wasn’t running, so the JS bundle failed to load.
- You had leftover references to deleted Objective-C files causing compilation conflicts.
Go through the steps above, and you should get past the black screen in no time.
内容的提问来源于stack exchange,提问作者Steve

