Trying to get a React Native oF project up and running


#1

EDIT Managed to do it. I uploaded an example on GitHub: https://github.com/razvanilin/oFReactNative

Hi everyone,

As the title says, I’m currently working on getting a React Native + OF project to run properly.
I made progress towards this and I successfully integrated OF into an ejected create-react-native-app

Next on the list was to get communication between RN and the iOS native code going. That was quite easy, so next was to try to launch a native view with OF code. This is where I’m currently stuck on and I think the reason for this is that is my very limited knowledge in Obj-C and especially iOS. I read articles and everything I found and this is what I came up with in the end:

this is the main.m file

#import <UIKit/UIKit.h>

#import "AppDelegate.h"
#import "SquareApp.h"


int main(int argc, char * argv[]) {
  //  here are the most commonly used iOS window settings.
  //------------------------------------------------------
  ofiOSWindowSettings settings;
  settings.enableRetina = true; // enables retina resolution if the device supports it.
  settings.enableDepth = false; // enables depth buffer for 3d drawing.
  settings.enableAntiAliasing = false; // enables anti-aliasing which smooths out graphics on the screen.
  settings.numOfAntiAliasingSamples = 0; // number of samples used for anti-aliasing.
  settings.enableHardwareOrientation = false; // enables native view orientation.
  settings.enableHardwareOrientationAnimation = false; // enables native orientation changes to be animated.
  settings.glesVersion = OFXIOS_RENDERER_ES1; // type of renderer to use, ES1, ES2, etc.
  
  ofAppiOSWindow * window = (ofAppiOSWindow *)(ofCreateWindow(settings).get());
  
  bool bUseNative = true;
  if (bUseNative){
    /**
     *
     *  Below is how you start using a native ios setup.
     *
     *  First a ofAppiOSWindow is created and added to ofSetupOpenGL()
     *  Notice that no app is being sent to ofRunApp() - this happens later when we actually need the app.
     *
     *  One last thing that needs to be done is telling ofAppiOSWindow which AppDelegate to use.
     *  This is a custom AppDelegate and inside it you can start coding your native iOS application.
     *  The AppDelegate must extend ofxiOSAppDelegate.
     *
     **/
    
    window->startAppWithDelegate("AppDelegate");
  }
  else {
    /**
     *
     *  This is the normal way of running an app using ofxiOS.
     *  This code has been left in this example to show that ofxiOS still works
     *
     **/
    
    return ofRunApp(new SquareApp());
  }
  
//  @autoreleasepool {
//    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
//  }

}

Then in the AppDelegate class, I made the rootViewController accessible outside of the class

AppDelegate.h

#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (nonatomic, strong) UIWindow *window;

@property (nonatomic, strong) UIViewController *rootViewController;

@end

AppDelegate.m

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"SurgTracMobile"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;

  // Set reference to class property
  self.rootViewController = rootViewController;

  [self.window makeKeyAndVisible];
  return YES;
}

@end

Then I created a class to act as a bridge between the RN and native iOS

OFStarter.h

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>

@interface OFStarter : NSObject <RCTBridgeModule>

@end

OFStarter.m

#import "OFStarter.h"
#import "SquareAppViewController.h"
#import "AppDelegate.h"
#import "SquareApp.h"

@implementation OFStarter

// The React Native bridge needs to know our module
RCT_EXPORT_MODULE()

- (NSDictionary *)constantsToExport {
  return @{@"greeting": @"Welcome to native"};
}

RCT_EXPORT_METHOD(squareMe:(int)number:(RCTResponseSenderBlock)callback) {
  SquareAppViewController *squareInstance = [[SquareAppViewController alloc] initWithFrame:[[UIScreen mainScreen] bounds]
                                                                                        app:new SquareApp()];

  AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
  [delegate.rootViewController presentViewController:squareInstance animated:YES completion:nil];
  callback(@[[NSNull null], [NSNumber numberWithInt:(number*number)]]);
}

@end

This bridge works and I am able to call squareMe from javascript. Now the next step that I want to solve is to be able to launch a new ViewController with OF code. The code above changes the ViewController, but the draw() and update() methods never get called. I use SquareApp and SquareAppViewController from the iosNativeExample. When I try to switch the view controller I get a black screen.

Sorry for the long post. I’m pretty sure this can be done easily but I’m missing some obvious bits about OF on iOS. Will appreciate any help with this. Thanks!

SquareAppViewController.h

#import "ofxiOSViewController.h"

@interface SquareAppViewController : ofxiOSViewController

@end

SquareAppViewController.m

#import "SquareAppViewController.h"
#include "ofxiOSExtras.h"
#include "ofAppiOSWindow.h"

@implementation SquareAppViewController

- (id) initWithFrame:(CGRect)frame app:(ofxiOSApp *)app {
  
  ofxiOSGetOFWindow()->setOrientation( OF_ORIENTATION_DEFAULT );   //-- default portait orientation.
  
  return self = [super initWithFrame:frame app:app];
}

- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
  return NO;
}

@end

SquareApp.h

#pragma once

#include "ofxiOS.h"

class SquareApp : public ofxiOSApp {
  
public:
  
  SquareApp ();
  ~SquareApp ();
  
  void setup();
  void update();
  void draw();
  void exit();
  
  void touchDown(ofTouchEventArgs &touch);
  void touchMoved(ofTouchEventArgs &touch);
  void touchUp(ofTouchEventArgs &touch);
  void touchDoubleTap(ofTouchEventArgs &touch);
  void touchCancelled(ofTouchEventArgs &touch);
  
  void lostFocus();
  void gotFocus();
  void gotMemoryWarning();
  void deviceOrientationChanged(int newOrientation);
  
  ofTrueTypeFont font;
};

SquareApp.mm

#include "SquareApp.h"

//--------------------------------------------------------------
SquareApp :: SquareApp () {
  cout << "creating SquareApp" << endl;
  setup();
}

//--------------------------------------------------------------
SquareApp :: ~SquareApp () {
  cout << "destroying SquareApp" << endl;
}

//--------------------------------------------------------------
void SquareApp::setup() {
  cout << "HEEEEEELLLLO-------------------------" << endl;
  ofSetDataPathRoot("bin/data");
  ofBackground(127);
  ofSetOrientation(OF_ORIENTATION_DEFAULT);
  
  int fontSize = 8;
  if (ofxiOSGetOFWindow()->isRetinaSupportedOnDevice())
    fontSize *= 2;
  
  font.load("/Users/eoSim/Documents/dev/of_v0.9.8_ios_release 2/apps/myApps/SurgTracM/ios/bin/data/fonts/mono0755.ttf", fontSize);
}

//--------------------------------------------------------------
void SquareApp::update(){
  
}

//--------------------------------------------------------------
void SquareApp::draw() {
  ofEnableAlphaBlending();
  cout<<"drawing"<<endl;
  int w = MIN(ofGetWidth(), ofGetHeight()) * 0.6;
  int h = w;
  int x = (ofGetWidth() - w)  * 0.5;
  int y = (ofGetHeight() - h) * 0.5;
  int p = 0;
  
  ofSetColor(ofColor::red);
  ofDrawRectangle(x, y, w, h);
  
  x = ofGetWidth()  * 0.2;
  y = ofGetHeight() * 0.11;
  p = ofGetHeight() * 0.035;
  
  ofSetColor(ofColor::white);
  font.drawString("frame num      = " + ofToString( ofGetFrameNum() ),    x, y+=p);
  font.drawString("frame rate     = " + ofToString( ofGetFrameRate() ),   x, y+=p);
  font.drawString("screen width   = " + ofToString( ofGetWidth() ),       x, y+=p);
  font.drawString("screen height  = " + ofToString( ofGetHeight() ),      x, y+=p);
}

//--------------------------------------------------------------
void SquareApp::exit() {
  //
}

//--------------------------------------------------------------
void SquareApp::touchDown(ofTouchEventArgs &touch){
  
}

//--------------------------------------------------------------
void SquareApp::touchMoved(ofTouchEventArgs &touch){
  
}

//--------------------------------------------------------------
void SquareApp::touchUp(ofTouchEventArgs &touch){
  
}

//--------------------------------------------------------------
void SquareApp::touchDoubleTap(ofTouchEventArgs &touch){
  
}

//--------------------------------------------------------------
void SquareApp::lostFocus(){
  
}

//--------------------------------------------------------------
void SquareApp::gotFocus(){
  
}

//--------------------------------------------------------------
void SquareApp::gotMemoryWarning(){
  
}

//--------------------------------------------------------------
void SquareApp::deviceOrientationChanged(int newOrientation){
  
}


//--------------------------------------------------------------
void SquareApp::touchCancelled(ofTouchEventArgs& args){
  
}

#2

I would try making the SquareAppViewController and SquareApp properties of the OFStarter class

This is untested - you may have to tweak

in OFStarter.h

@interface OFStarter
{
    SquareApp* squareApp;
}
@property (nonatomic, strong) SquareAppViewController *squareInstance;

in OFStarter.m


RCT_EXPORT_METHOD(squareMe:(int)number:(RCTResponseSenderBlock)callback) {

squareApp = new SquareApp();
self.squareInstance = [[SquareAppViewController alloc] initWithFrame:[[UIScreen mainScreen] bounds] app:squareApp];

#3

Thanks for the suggestion! I will try this out as soon as I can and come back with the results :slight_smile:


#4

Finally had time to try that and no joy unfortunately :frowning:

Also modified the AppDelegate to extend ofxiOSAppDelegate but it still doesn’t work. SquareApp is instantiated, but none of the OF methods are called: setup(), update(), draw(), etc…

@interface AppDelegate : ofxiOSAppDelegate

I tried to call setup() and then draw() manually. Setup works fine, but when I call draw() it breaks when ofGetWidth() is called inside the method.

It breaks in ofAppiOSWindow.mm

ofPoint	ofAppiOSWindow::getWindowSize() {
	return *[[ofxiOSEAGLView getInstance] getWindowSize];
}

This leads me to believe that the OF environment isn’t properly created somehow. I keep looking at the examples and other than the react native way of creating the root view nothing else is different.


#5

Ok, I managed to do it in the end. Apparently, the reason why I couldn’t switch the Views was not doing this from the main thread. All I had to do was to add the following code to the OFStarter implementation:

- (dispatch_queue_t)methodQueue
{
  return dispatch_get_main_queue();
}

Now I am able to run a RN app and change to OF views whenever I want to do some fancy stuff. I might get an example up on GitHub at some point. Let me know if you would be interested in something like this.


#6

wow! I’m interested in an example!


#7

It’s up: https://github.com/razvanilin/oFReactNative (iOS only for now)

If you encounter any issues with the setup or have any suggestions, feel free to open issues on GitHub and I’ll check them out.

:beer: