How my react js code can interact with existing Obj-C logic? - react-native

I have existing Obj-C project with rich business logic. I wanna try using React Native on the particular screen (I mean View Controller in terms of Cocoa), but every example I see in repo contains logic in javascript. How can I treat React Native as rendering, but pass user actions to my Objective-C code?
EDIT 31 march 2015:
Native view module does not seem to be a good solution, because native modules get instanciated from React code. Thus if I want to use already created view model for that view controller, I need to have some singleton, which will be like a some shared state on the side. I think this is bad.

It's not possible for React views to call native methods directly without going through JavaScript, unless you create custom native view plugins for literally everything onscreen.
Your best bet is probably to create a custom native module that exports all the native methods you want to call, then write a minimal React JavaScript application that does nothing except forward touch events from the views to your module by calling those methods.
If you need to communicate back to the JS application, your module can either use callbacks passed to your exported methods, or broadcast events which the JS code can observe.
To get the most out of React Native though, I'd recommend that you try to keep all the view and controller logic in the JS part, and only expose the business logic from the native side. Otherwise you lose all the benefits of rapid reloading to test changes, etc.

I have the same problem and what I did to solve it:
Create Native Module wrapper around NSNotificationCenter, so javascript code could publish iOS events.
Subscribe for that event within ReactController-wrapper around React-Native code.
Raise event when user clicked button (when we need to give control back to objective-c) with needed data to pass as dictionary.
Catch event from ReactController-wrapper (objective-c), process data, open other controllers/etc.
Using React Native within existing iOS app for some views only

Have a look at the documentation:
http://facebook.github.io/react-native/docs/nativemodulesios.html#content
React Native provides "Native Modules" as a way of bridging between Obj-C and JavaScript. There's also a little bit more about this on the React Native homepage:
http://facebook.github.io/react-native/
Under the "Extensibility" heading, it shows how to expose custom iOS views.

IF you implement RCTRootView in your view controller you can use it to access RTCBridge and then from this gain access to your module.
This will allow you to keep a reference of your view controller inside your module, making you able to pass calls to method directly instead of relying on NSNotifications.
example :
In your view controller
let reactView = RCTRootView(bundleURL: jsCodeLocation, moduleName: "MyReactApp", initialProperties: nil, launchOptions: nil)
self.reactBridge = reactView.bridge
let myModule = self.reactBridge.module(for: MYModuleClass.self) as! MYModuleClass
myModule.viewController = self
And in your module keep the reference :
#objc(MYModuleClass)
class MYModuleClass: NSObject {
weak var viewController: MyViewController!
}

Related

How to declare View class method definition in an Expo native view module

I created an Expo native module like below. I can declare properties of the view. But I have not figured out how to declare class member method yet, like play/pause method of the player view. I am not sure if it's just impossible in Expo. Many thanks
View(VideoPlayerView.self) {
Prop("speed") { (view: VideoPlayerView, speed: Double) in
view.setSpeed( speed )
}
I searched the source code of Expo
https://github.com/expo/expo/blob/753557f6db11f4ecd1ab25545f2768b0fd863cf7/ios/versioned/sdk47/ExpoModulesCore/ios/Swift/Views/ViewDefinition.swift
and it seems it only support LifeCycleMethod, not generic method. If so, just want to get a confirmation to avoid waste of time

In React Native Library How Can I get ios current UIViewController

react native library in android can use "getCurrentActivity()" to replace "this"
final Activity activity = getCurrentActivity();
And In ios How can use to replace "self" to get current UIViewController
For "current UIViewController" you most probably needs RCTPresentedViewController function from RCTUtils.h file:
#import <React/RCTUtils.h>
// ...
UIViewController* vc = RCTPresentedViewController();
// ...
Note: Very old RN versions do not has this API, but it can be easy implemented by hands, implementation is easy enough: RCTUtils.m
On iOS - unless you use wix's react native navigation - there is generally only one UIViewController, instantiated in your AppDelegate.
1st question: Why do you need your app UIViewController ?
if you really need to access it, here is a suggestion : UIApplication.sharedApplication().window.rootViewController (this code might not work for complex native setup, but for simple cases, it should give you your app ViewController)

Will a JS root view always receive events from native after it has been removed from its superview?

My (heavily simplified) code looks like this:
// objc
self.currentSwipeUpView = [[RCTRootView alloc]
initWithBridge:_bridge moduleName:#"PhotoSwipeUpView"
initialProperties:nil
];
// elsewhere...
[self.currentSwipeUpView removeFromSuperview];
self.currentSwipeUpView = nil;
// js
function PhotoSwipedUpView() {
return <TextInput style={{flex: 1}} onChangeText={console.warn} />
}
AppRegistry.registerComponent('PhotoSwipeUpView', () => PhotoSwipeUpView)
self.currentSwipeUpView is removed from its superview and dereferenced. When this happen, is it possible for the JS thread to not receive a pending onChangeText event? Would it lead to console.warn to being with the new text?
I'm imagining a case where the event would have been sent to the JS thread after the RCTRootView was removed from its superview and dereferenced.
Furthermore, I'm curious if it's possible to be notified when a RCTRootView is removed from its superview from the JS side.
I'm using ARC and I'm not holding any other references to self.currentSwipeUpView.
Edit: I have two RCTRootViews. Only one of them will be unloaded, the other will persist across the state of the application. They use the same bridge, and therefore use the same bundle and JS environment.
Loading/Unloading JS Bundles
Your loaded js bundle is tied to the root view and is not persistent other than in that view.
Once you remove the root view from it's parent, the bundle is also unloaded. If the root view is removed and dereferenced, the text change listeners should also be removed and should not trigger.
The JSX in the bundle is used to ultimately setup native elements plus their listeners. The JSX is basically the script. The JSX with a RN TextInput basically results in a native UITextField. If the JSX specifies a listener, then a native listener for text changes is added to the native element instance.
Even if you had two root views loaded, they would operate independently from each other. If one root view is unloaded, the listeners associated with the TextInputs on the screen should no longer receive events. Your second Root View should be unaffected as it would have separate listeners setup for the TextInput.
Events/Timing issue
I suppose there is a small chance with perfect timing, and depending on where the removeFromSuperview is called, that the js could just receive the event before the view is completely removed and dereferenced, but if that has a possibility of affecting the functionality of your app, I would suggest using another pattern to interact with your data and remove the screen.
Possible Solution
It's hard to say without knowing what you're trying to do, but you could, for example, post a notification to close the React Native view using RCTEventEmitter/NativeEventEmitter, then have the React Native screen close itself with a native function that just removes it from the super view. This type of pattern would also answer your second question (in a sense), because you're effectively telling the RN screen before it should close allowing it to take any actions necessary before being removed.

Application Delegate Usage

I'm fairly new to Objective-C and cocoa programming, so I don't really understand the concept of App Delegates.
When we create a cocoa application, do we store our code (Methods, actions, outlets) in the App Delegate files or do we create a new file that will act as a controller and code from there. Right now, I put all of my code in those two files, but from what I read, your goal is to try to make your App Delegate files as slim as possible.
My question is: What's the usage of the app delegate files?
Talking about applicationDidFinishLaunching::
It's just your application entry point. Normally you only create the window and your first ViewController, or your Tabbar - your main starting interface class - here.
All the other delegate methods of the NSApplicationDelegate have other functions of course. Most of them are the point, where you react on the state of the app. Opened / Closed / Backgrounded / Reopened etc.
But you should probably have a look at the programming tutorials in the iPhone documentation. There is a lot of information on how to structure your objc projects. E.g. look here: Start Developing iOS Apps Today
Or if your looking for OSX Apps, look here:
1) Your First Mac App
2) Mac App Programming Guide
There is also a bunch of Sample code.
The App Delegate is a handler location to handle events that occur on the application. Things like open and close. It also hangs around the whole time the application is executing and you can grab the singleton instance at any point by doing [[NSApplication sharedApplication] delegate].
This comes in handy for handing objects between controllers and serving as a router for events. You can also store some data on the delegate if you need to modify/have access to it in different parts of the code.
This all works well for simple applications, but as things become more complex, you need to have some division of responsibilities. The AppDelegate should really only be responsible for actions that occur on the application itself, not on another view or a controller. Putting all/most of your code in the AppDeligate is certainly bad practice and will lead to horrible code as things get more complex and need to be maintained.

How to structure the code for a Cocoa Application

I'm writing a Cocoa application where I have an auto generated app delegate:(MyAppDelegate.h/MyAppDelegate.m)
So I am not sure of the best way to structure the class files for this Cocoa application. I understand MVC thoroughly (on the iPhone) but I'm having a block as to try organising the source properly in a Cocoa app.
I need to spawn two separate fullscreen OpenGL views. The problem is that I could simply create classes for "OpenGLView" then instantiate and call all this code into the app delegate, but it seems messy and it's aparently not the place to do it.
How would I best achieve the code structure?
Instantiate your application delegate in the MainMenu.xib file and hook it up. When I've done Cocoa fullscreen stuff, I've instantiated the views in the -applicationDidFinishLaunching method of the application delegate. It *is* messy, because for fullscreen views it doesn't really make sense to use interface builder. This is the same way that other folks do fullscreen apps in Cocoa.