16

I created my first react native app using the following command.

react-native init PropertyFinder

This created a project for ios in Objective-C. The problem is I don't know Objective-C but I work on Swift.

This gist shows how to link react-components to a swift project. But is there a way to directly create the project in Swift language?

harsh_v
  • 3,193
  • 3
  • 34
  • 53

6 Answers6

17

This excellent post helped me solving the problem.

Solution

  1. Add Bridging Header
#import "RCTBridgeModule.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#import "RCTRootView.h"
#import "RCTUtils.h"
#import "RCTConvert.h"
  1. Add AppDelegate.Swift
var bridge: RCTBridge!

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    /**
     * Loading JavaScript code - uncomment the one you want.
     *
     * OPTION 1
     * Load from development server. Start the server from the repository root:
     *
     * $ npm start
     *
     * To run on device, change `localhost` to the IP address of your computer
     * (you can get this by typing `ifconfig` into the terminal and selecting the
     * `inet` value under `en0:`) and make sure your computer and iOS device are
     * on the same Wi-Fi network.
     */

    let jsCodeLocation = NSURL(string: "http://localhost:8081/index.ios.bundle?platform=ios&dev=true")

    /**
     * OPTION 2
     * Load from pre-bundled file on disk. The static bundle is automatically
     * generated by "Bundle React Native code and images" build step.
     */

    // jsCodeLocation = NSBundle.mainBundle().URLForResource("main", withExtension: "jsbundle")

    let rootView = RCTRootView(bundleURL:jsCodeLocation as URL!, moduleName: "PropertyFinder", initialProperties: nil, launchOptions:launchOptions)

    self.bridge = rootView?.bridge

    self.window = UIWindow(frame: UIScreen.main.bounds)
    let rootViewController = UIViewController()

    rootViewController.view = rootView

    self.window!.rootViewController = rootViewController;
    self.window!.makeKeyAndVisible()

    return true
  }
  1. Remove the AppDelegate.h, AppDelegate.m and the main file.

  2. Clean and build your project.

harsh_v
  • 3,193
  • 3
  • 34
  • 53
  • i tried this. RN 0.42; app compiles and runs ok, but, the Xcode project never finds the AppDelegate.swift file. The simulator shows a black screen. – ajonno Nov 25 '17 at 22:35
  • I think those files are probably not added to the `Project`. Select the `Appdelegate file` and check in the file `File inspector` if under `Target Membership` your project name is checked. And if the files are not even showing in the Xcode then you should search for _ Add files to Xcode project_ – harsh_v Nov 27 '17 at 07:27
  • 2
    This answer is obsoleted. Do not use this – MJ Studio May 02 '20 at 06:34
6

If you're having trouble with the imports

#import "RCTBridgeModule.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#import "RCTRootView.h"
#import "RCTUtils.h"
#import "RCTConvert.h"

try

#import <React/RCTBridgeModule.h>
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTRootView.h>
#import <React/RCTUtils.h>
#import <React/RCTConvert.h>
5

This actually works for me:

import Foundation

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?
  var bridge: RCTBridge!

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    let jsCodeLocation: URL

    jsCodeLocation = RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index.ios", fallbackResource:nil)
    let rootView = RCTRootView(bundleURL: jsCodeLocation, moduleName: "REPLACE_WITH_YOUR_PROJECT_NAME", initialProperties: nil, launchOptions: launchOptions)
    let rootViewController = UIViewController()
    rootViewController.view = rootView

    self.window = UIWindow(frame: UIScreen.main.bounds)
    self.window?.rootViewController = rootViewController
    self.window?.makeKeyAndVisible()

    return true
  }
}
imaginair
  • 519
  • 1
  • 7
  • 12
  • Note: if you experience an error saying "Creating more than one application" when using this `@UIApplicationMain` macro, you can try writing the implementation of the macro manually instead: https://stackoverflow.com/a/50750540/5951226 – Jamie Birch Jun 08 '18 at 19:23
2

This what worked for me:

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, RCTBridgeDelegate {
    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
}

var window: UIWindow?
var bridge: RCTBridge!

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    self.bridge = RCTBridge(delegate: self, launchOptions: launchOptions)

    let rootView = RCTRootView(bridge: self.bridge, moduleName: "SolutoPartnersApp", initialProperties: nil)

    self.window = UIWindow(frame: UIScreen.main.bounds)
    let rootViewController = UIViewController()

    rootViewController.view = rootView

    self.window!.rootViewController = rootViewController;
    self.window!.makeKeyAndVisible()

    return true
}

func applicationWillResignActive(_ application: UIApplication) {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

func applicationDidEnterBackground(_ application: UIApplication) {
    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}

func applicationWillEnterForeground(_ application: UIApplication) {
    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}

func applicationDidBecomeActive(_ application: UIApplication) {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}

func applicationWillTerminate(_ application: UIApplication) {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}

}

Shai
  • 41
  • 6
1

1) Open Worksapce project in Xcode

2) Delete these files:

  • AppDelegate.h
  • AppDelegate.m
  • main.m

3) Create a new Swift file with the name AppDelegate & on next also click on create Bridging header

4) Copy below in AppDelegate.swift

import Foundation
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?
  var bridge: RCTBridge!

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let jsCodeLocation: URL

    jsCodeLocation = RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index.ios", fallbackResource:nil)
    let rootView = RCTRootView(bundleURL: jsCodeLocation, moduleName: "REPLACE_WITH_YOUR_PROJECT_NAME", initialProperties: nil, launchOptions: launchOptions)
    let rootViewController = UIViewController()
    rootViewController.view = rootView

    self.window = UIWindow(frame: UIScreen.main.bounds)
    self.window?.rootViewController = rootViewController
    self.window?.makeKeyAndVisible()

    return true
  }
}

5) Copy below code in Bridging header file

#import "React/RCTBridgeModule.h"
#import "React/RCTBridge.h"
#import "React/RCTEventDispatcher.h"
#import "React/RCTRootView.h"
#import "React/RCTUtils.h"
#import "React/RCTConvert.h"
#import "React/RCTBundleURLProvider.h"

6) Run your project now

KPS250
  • 317
  • 4
  • 8
  • 1
    This sort-of worked in 2021, but I had to change `forBundleRoot: "index.ios"` to just be "index". Also, don't forget to add your moduleName! – flunder May 11 '21 at 12:38
0

I looked into it and the functionality is not supported by react-native.

What react-native init is doing is taking a template and changing some properties in it.

The template is written in Objective-C so there is nothing you can do beside modifying the result manually.

However, that shouldn't really concern you. You can use Swift even in Objective-C projects. No need to rewrite the files to Swift.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • 1
    Yeap, eventually I figured it out. My main concern was the AppDelegate file that was written in Obj-C. So, I replaced those with the swift version and it started working. I was actually missing the bridging header. Thanks for the help btw (y) – harsh_v Oct 17 '16 at 11:33