React Native and Objective C delegates - objective-c

I am quite new to react native and and the bridging mechanism with native code, especially when the framework has delegates. Assume I am trying to bridge the following framework:
#protocol BRPtouchNetworkDelegate;
#class PLNetworkModule;
#interface BRPtouchNetworkManager : NSObject <NSNetServiceBrowserDelegate,NSNetServiceDelegate>
#property(retain, nonatomic) NSMutableArray* registeredPrinterNames;
#property(assign, nonatomic) BOOL isEnableIPv6Search;
- (int)startSearch: (int)searchTime;
- (NSArray*)getPrinterNetInfo;
- (BOOL)setPrinterNames:(NSArray*)strPrinterNames;
- (BOOL)setPrinterName:(NSString*)strPrinterName;
- (id)initWithPrinterNames:(NSArray*)strPrinterNames;
- (id)initWithPrinterName:(NSString*)strPrinterName;
#property (nonatomic, assign) id <BRPtouchNetworkDelegate> delegate;
#end
#protocol BRPtouchNetworkDelegate <NSObject>
-(void) didFinishSearch:(id)sender;
#end
The following is the bridge module I implemented:
RCTBRPtouchNetworkManager.h
#import <React/RCTBridgeModule.h>
#import <BRPtouchPrinterKit/BRPtouchPrinterKit.h>
#interface RCTBRPtouchNetworkManager : NSObject <RCTBridgeModule, BRPtouchNetworkDelegate>
#end
RCTBRPtouchNetworkManager.m
#import "RCTBRPtouchNetworkManager.h"
#import <BRPtouchPrinterKit/BRPtouchPrinterKit.h>
#import <React/RCTLog.h>
#implementation RCTBRPtouchNetworkManager {
BRPtouchNetworkManager *_networkManager;
}
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(#"Pretending to create an event %# at %#", name, location); //a dummy method to test the bridge
}
RCT_EXPORT_METHOD(startSearchWithTimeout:(int)time) {
RCTLogInfo(#"Bridge started search with time %d", time);
_networkManager = [[BRPtouchNetworkManager alloc] init];
_networkManager.delegate = self; //I'm setting delegate here
_networkManager.isEnableIPv6Search = NO;
NSString * path = [[NSBundle mainBundle] pathForResource:#"PrinterList" ofType:#"plist"];
if( path )
{
NSDictionary *printerDict = [NSDictionary dictionaryWithContentsOfFile:path];
NSArray *printerList = [[NSArray alloc] initWithArray:printerDict.allKeys];
[_networkManager setPrinterNames:printerList];
} else {
RCTLogInfo(#"PrinterList path not found");
}
// Start printer search
[_networkManager startSearch: 5.0];
}
- (void)didFinishSearch:(id)sender {
NSLog(#"didFinishedSearch"); //this delegate method is not called
}
#end
I can easily call the dummy method and see the results in the logs. However, the delegate method didFinishSearch() is never called. I call this from javascript as follows:
componentDidMount() {
let networkManager = NativeModules.BRPtouchNetworkManager;
networkManager.startSearchWithTimeout(5.0);
}
I there something I am missing? Am I implementing delegate properly? Is this kind of functionality even possible (can't seem to not since the delegate method was used by iOS community for a long time). Your help is much appreciated.
EDIT
I found that adding the following to my bridge manager file made the delegate to fire (thanks to this post)
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
However, even though this solves the problem, I'd like a more technical understanding on what is going on here since I can't seem to exactly grasp it. Thank you

I know this isn’t an an answer to the post but for the bit where you’ve asked for a more technical understanding - dispatch_get_main_queue(); puts the delegate method responses on to the main thread. Since JS is single threaded any process on the background thread won’t be visible to it.

Related

Delegating the even callback in objective-C

I am trying to wrap my head around how does the an event callback delegation works. So far I have written following code which btw works just fine:
Bridge.h
#protocol BridgeDelegate <NSObject>
- (void) bridgeLock;
#end
#interface Bridge : NSObject
+(instancetype) sharedInstance;
#property (weak, nonatomic) id<BridgeDelegate> bridgeDelegate;
- (void) wipe;
#end
Bridge.m
#implementation Bridge
+(instancetype) sharedInstance {
static dispatch_once_t pred;
static id shared = nil;
dispatch_once(&pred, ^{
shared = [[super alloc] initUniqueInstance];
});
return shared;
}
-(instancetype) initUniqueInstance {
return [super init];
}
- (void) wipe
{
NSLog(#"lock in bridge called");
if(self.bridgeDelegate)
{
[self.bridgeDelegate bridgeLock];
}
}
#end
Plugin.h
#interface Plugin : NSObject<BridgeDelegate>
#property (strong, nonatomic) Bridge *bridge;
- (void) pluginInitialize;
#end
Plugin.m
#implementation Plugin
- (void) pluginInitialize
{
self.bridge = [Bridge sharedInstance];
self.bridge.bridgeDelegate = self;
}
- (void)bridgeLock
{
NSLog(#"lock in plugin called");
}
#end
When I call the following code in applicationDidBecomeActive
Bridge* bridge = [Bridge sharedInstance];
Plugin* plugin = [[Plugin alloc] init];
[plugin pluginInitialize];
[bridge wipe];
I get the following expected output:
lock in bridge called
lock in plugin called
Now my questions:
How exactly is the delegate work? In the sense, Plugin is only implementing the function bridgewipe(), right? Why and how bridgeLock is being called at first place?
Does this have anything to the fact that Bridge is a singleton. Had I made Bridge a non singleton class, will the end result be same.
1.How exactly is the delegate work? In the sense, Plugin is only implementing the function bridgewipe(), right? Why and how bridgeLock is being called at first place?
In the above pasted code "Plugin.m" is implementing - (void)bridgeLock
Does this have anything to the fact that Bridge is a singleton. Had I made Bridge a non singleton class, will the end result be same.
No
Bridge* bridge = [Bridge sharedInstance];
this bridge we call it B1;
B1 is a instance make by the method "sharedInstance";
then you call the following code :
Plugin* plugin = [[Plugin alloc] init];
[plugin pluginInitialize];
pluginInitialize method your code is
{
self.bridge = [Bridge sharedInstance];
self.bridge.bridgeDelegate = self;
}
when code executed, self.bridge is also a instance make by the method "sharedInstance"; it's equal to B1 with address and also make B1's delegate == self;
so when you call [bridge wipe];
It will nslog #"lock in bridge called";
Because self.bridgeDelegate is not nil, so delegate will call the bridgeLock method;
Then nslog #"lock in plugin called".
About your second question, when you make Bridge a non singleton class, I think the result will be different.
#hariszaman, explanation is right but I would like to expand more on this so it can help someone in the future. Basically this is what happening.
I am creating an instance of the Bridge class. This instance in the memory has a reference variable of type BridgeDelegate.
As I instancetiate Plugin, BridgeDelegate variable starts pointing to the the Plugin class instance.
Now when lock is called, it calls the bridgelock method of the class that is pointed by BridgeDelegate pointer which in this case is Plugin.
It doesn't matter if the Bridge class is not singleton because following line in pluginInitialize:
{
self.bridge = [Bridge sharedInstance];
self.bridge.bridgeDelegate = self;
}
will be changed to :
{
self.bridge = [[Bridge alloc] init];
self.bridge.bridgeDelegate = self;
}
and steps 1,2 and 3 will be repeated the same way.

set Property method called tens of thousands of times when I use a custom UI view in react native iOS

I created a custom UI view under this facebook tutorial https://facebook.github.io/react-native/docs/native-components-ios.html#content
MyCustomView.h
#interface MyCustomView : UIView
#property (nonatomic, copy) NSString *test;
#end
MyCustomView.m (init method is omitted)
- (void)setTest:(NSString *)test
{
self.test = test;
}
And I expose this property to JS in MyCustomViewManager
MyCustomViewManager.h
#interface ReactVideoViewManager : RCTViewManager
#end
MyCustomViewManager.m
RCT_EXPORT_MODULE()
- (UIView *)view
{
MyCustomView *videoView = [[MyCustomView alloc] init];
return;
}
RCT_EXPORT_VIEW_PROPERTY(test, BOOL);
I used this view in JS as follows:
<MyCustomView style={styles.row} test={true}></MyCustomView>
However, when the MyCustomView object initialized, the setTest method called tens of thousands of times and crashed at last.
Did anyone know the reason?
a working solution might be:
- (void)setTest:(NSString *)test
{
_test = test;
}

How to read infos from /S/L/E kext info.plist file?

I’m just a newbie in Mac OS X programming so please, be patient with me. I’m trying to make a cocoa app which the goal is to read some infos from Info.plist kext’s file, which the full path is /System/Library/Extensions/NVDAResman.kext/Contents/Info.plist
h.
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (weak) IBOutlet NSTextField *nvidiaNameTextField;
#end
m.
#import "AppDelegate.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[self setnvidiaNameTextField];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
// This will allow the application to quit instead of just closing the window
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
{
return YES;
}
// Find NVIDIA Kernel Extension Name
-(void)setnvidiaNameTextField
{
NSString *nvidiaNameTextField = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleName"];
self.nvidiaNameTextField.stringValue = [NSString stringWithFormat:#"%#", nvidiaNameTextField];
}
#end
it works but with my project Info.plist file, and it’s not what I want.
So my question is how can I read Info.plist from NVDAResman.kext?
Thank you in advance
PS: I’m using Xcode 7.1 beta (7B60)
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:#"/System/Library/Extensions/NVDAResman.kext/Contents/Info.plist"];
Please mind that any search using "[NSBundle mainBundle]" will results within your application package (under .app folder structure). Also if you plan to sandbox your application the above code will not work and there is no solution to make it work with Sandboxing (security of OS X platform).
here’s how I did it (thanks to xhruso00)
m.
// Find NVIDIA Kernel Extension Name
-(void)setnvidiaVersionTextField
{
NSDictionary *infoDict = [[NSDictionary alloc] initWithContentsOfFile:#"/System/Library/Extensions/NVDAResman.kext/Contents/Info.plist"];
NSString* nvidiaVersionTextField = [infoDict objectForKey:#"CFBundleName"];
self.nvidiaVersionTextField.stringValue = [NSString stringWithFormat:#"%#", nvidiaVersionTextField];
}

Cocoa Programming, setting the delegate

I'm moving on from iOS to Cocoa and trying to muddle through my first few programs. I thought it would be simple to add an NSComboBox to my form, well that part was. I added <NSComboBoxDelegate, NSComboBoxDataSource> to my interface, two data callbacks, and the notifier:
#interface spcAppDelegate : NSObject <NSApplicationDelegate,
NSComboBoxDelegate, NSComboBoxDataSource>
- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index;
- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox;
- (void)comboBoxSelectionDidChange:(NSNotification *)notification;
#end
I control dragged the combobox to the app delegate (which is the only class in my simple default app) and wired up the delegate and data source but none of those events fire. I thought app delegate was correct but since it didn't fire, I also tried "file owner" and "application". I didn't think those would work and they didn't.
Whats the right way to wire up the delegate/data source for an NSComboBox in a Cocoa app?
Thanks!
Provided you've actually implemented those methods in your spcAppDelegate.m file, you may want to double-check that Uses Data Source is checked for the NSComboBox in the nib file in Interface Builder:
Note that it wasn't set by default in a quick test project I created. Running without that checkbox set should log the following to console when you launch the app:
NSComboBox[2236:403] *** -[NSComboBox setDataSource:] should not be called when
usesDataSource is set to NO
NSComboBox[2236:403] *** -[NSComboBoxCell setDataSource:] should not be called
when usesDataSource is set to NO
While the NSComboBox Class Reference is somewhat helpful, when I was first learning, I found that if there were companion guides linked to for a class, those were much more helpful in understanding how one should use the class in practice. If you look at the top of the NSComboBox class reference at the Companion Guide, you'll see Combo Box Programming Topics.
To set up a combo box that uses a data source, you could use something like the following:
spcAppDelegate.h:
#import <Cocoa/Cocoa.h>
#interface spcAppDelegate : NSObject <NSApplicationDelegate,
NSComboBoxDelegate, NSComboBoxDataSource> {
IBOutlet NSWindow *window;
IBOutlet NSComboBox *comboBox;
NSMutableArray *comboBoxItems;
}
#property (assign) IBOutlet NSWindow *window;
#end
spcAppDelegate.m:
#import "spcAppDelegate.h"
#implementation spcAppDelegate
#synthesize window;
- (id)init {
if ((self = [super init])) {
comboBoxItems = [[NSMutableArray alloc] initWithArray:
[#"Cocoa Programming setting the delegate"
componentsSeparatedByString:#" "]];
}
return self;
}
- (void)dealloc {
[comboBoxItems release];
[super dealloc];
}
- (NSInteger)numberOfItemsInComboBox:(NSComboBox *)aComboBox {
return [comboBoxItems count];
}
- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(NSInteger)index {
if (aComboBox == comboBox) {
return [comboBoxItems objectAtIndex:index];
}
return nil;
}
- (void)comboBoxSelectionDidChange:(NSNotification *)notification {
NSLog(#"[%# %#] value == %#", NSStringFromClass([self class]),
NSStringFromSelector(_cmd), [comboBoxItems objectAtIndex:
[(NSComboBox *)[notification object] indexOfSelectedItem]]);
}
#end
Sample Project: http://github.com/NSGod/NSComboBox.
I was having a similar situation yesterday until I remembered to hook up the File Owner data source to the IBOutlet in IB:

No access to global instance (build by factory) on iOS

this is a follow-up question to my last one here: iOS: Initialise object at start of application for all controllers to use .
I have set my application up as follows (ignore the DB Prefix):
DBFactoryClass // Built a DataManaging Object for later use in the app
DBDataModel // Is created by the factory, holds all data & access methods
DBViewControllerA // Will show some of the data that DBDataModel holds
moreViewControllers that will need access to the same DBDataModel Object
i will go step by step through the application, and then post the problem in the end
AppDelegate.h
#import "DBFactoryClass.h"
AppDelegate.m
- (BOOL)...didFinishLaunching...
{
DBFactoryClass *FACTORY = [[DBFactoryClass alloc ]init ];
return YES;
}
DBFactoryClass.h
#import <Foundation/Foundation.h>
#import "DBDataModel.h"
#interface DBFactoryClass : NSObject
#property (strong) DBDataModel *DATAMODEL;
#end
DBFactoryClass.m
#import "DBFactoryClass.h"
#implementation DBFactoryClass
#synthesize DATAMODEL;
-(id)init{
self = [super init];
[self setDATAMODEL:[[DBDataModel alloc]init ]];
return self;
}
#end
ViewControllerA.h
#import <UIKit/UIKit.h>
#import "DBDataModel.h"
#class DBDataModel;
#interface todayViewController : UIViewController
#property (strong)DBDataModel *DATAMODEL;
#property (weak, nonatomic) IBOutlet UILabel *testLabel;
#end
ViewControllerA.m
#import "todayViewController.h"
#implementation todayViewController
#synthesize testLabel;
#synthesize DATAMODEL;
- (void)viewDidLoad
{
todaySpentLabel.text = [[DATAMODEL test]stringValue]; // read testdata
}
#end
DBDataModel.h
#import <Foundation/Foundation.h>
#interface DBDataModel : NSObject
#property (nonatomic, retain) NSNumber* test;
#end
DBDataModel.m
#import "DBDataModel.h"
#implementation DBDataModel
#synthesize test;
-(id)init{
test = [[NSNumber alloc]initWithInt:4]; // only a testvalue
return self;
}
#end
the app builds fine, and starts up but the label stays blank. so either the object does not exist (but i guess this would result in an error message), or something else is wrong with my setup. any thoughts?
Two notes:
Your have a shotgun approach to asking questions: everytime you hit a stumbling block, you ask a question and if the answer does not work immediately, you ask another one. You have to spend some energy in between the questions debugging and poking into the code on your own, otherwise you will depend on the external help forever.
Use the common coding style please. CAPS are reserved for macros.
Now to the code:
- (BOOL) …didFinishLaunching…
{
DBFactoryClass *factory = [[DBFactoryClass alloc] init];
return YES;
}
This simply creates an instance of the DBFactoryClass and then throws it away. In other words, it’s essentially a no-op. Judging by the comments in the previous answer you create the controllers using the Storyboard feature. How are they supposed to receive the reference to the data model? The reference isn’t going to show up by magic, you have to assign it somewhere.
I’m not familiar with the Storyboard feature. The way I would do it is to create the view controllers using separate XIB files, then you can create the controller instances in the Factory class and pass them the needed reference to the model. In the end the application delegate would create the factory, ask it to assemble the main controller and then set it as the root view controller for the window. Just like in my sample project. It’s possible that there’s a way to make it work with storyboards, but as I said, I am not familiar with them.