Confusing Swift type annotations for React Native Promises - objective-c

I'm playing around with React Native and attempting to write some native code that communicates over bluetooth. I'm confused by the type annotations that I need to use in order for it to work. Could someone please explain why I have to have the "resolver" and "rejecter" bits in the following two code snippets? Is there a way to write this without those unused parts?
My implementation, MyAsyncModule.swift:
#objc(MyAsyncModule)
class MyAsyncModule: NSObject {
#objc func echoAsync(
input: NSNumber,
resolver resolve: RCTPromiseResolveBlock,
rejecter reject: RCTPromiseRejectBlock
) -> Void {
resolve(input)
}
}
From my bridge file, MyAsyncModuleBridge.m
RCT_EXTERN_METHOD(echoAsync:
(nonnull NSNumber *)input
resolver:(RCTPromiseResolveBlock *)resolve
rejecter:(RCTPromiseRejectBlock *)reject
)
I am coming from scripting land so types are foreign to me, but it seems too weird that React Native refuses to identify my the echoAsync method unless both the implementation and the bridge include the resolver and rejecter bits...

The resolver and reject calls are needed to have the framework generate a "promise". A promise can be thought of as a placeholder for a value that will be made available in the future. The resolver is called when the native code is done doing its work and is ready to pass the results back to the JavaScript land. reject is used when the native side detects an error and is used to report that error from native to JavaScript land.
To get a bit deeper, when you're JavaScript calls a native function, it doesn't pause and wait for native to finish up like a normal function call. It instead just goes on executing the next line of code (notice how React-Native prevents you from setting a return value for your exported functions meaning they are explicitly making sure you don't try and wait for a return value).
So then how does native code ever report the results back to JavaScript? There are two options
callbacks (in native these have the type RCTResponseSenderBlock) when called, cause a JavaScript function to run with the passed arguments
promises (with the types RCTPromiseResolveBlock and RCTPromiseRejectBlock) which causes you success handler to run with the passed arguments when resolver is called or causes your error handler to run when reject is called.
As for async function you MUST use promises.
For more info on JavaScript promises checkout:
http://www.html5rocks.com/en/tutorials/es6/promises/
https://facebook.github.io/react-native/docs/native-modules-ios.html#promises

Related

React Native App Working in Development, 'Error: 'undefined' is not a function' in Production

I have completed an app that builds on both Android and iOS. It works as expected when I build the app from the CLI or when I build it via XCode / Android Studio. However, on TestFlight, it gets errors that simply do NOT exist when I build it locally. These errors only appear on TestFlight, and thus I have little to no idea on how to go finding them down or even resolving them. Does anyone have better expertise in this area?
I'm not sure how common an issue this is-- I've never heard of it before to be honest, but the components that were not working were components that utilized ({props}) in a component. For example, any component wthat utilized the following declarations did NOT work
function Example({props}){
// stuff
}
// OR //
const Example = ({props}) => {
// stuff
}
all of the values inside of props were unreadable. I fixed this by simply moving EVERYTHING into a recoil state instead and that mitigated any errors. For example...
navigation.navigate("path",{prop1: value})
// AND //
return (<Example prop={value} prop={value})
would not work unless the prop was a single value-- a string, an int, a bool, these would work. Objects and Arrays did NOT properly carry over.
Hope this helps someone in the future.

I am getting "Illegal callback invocation from native module. This callback type only permits a single invocation from native code."

I am getting an error as
"Illegal callback invocation from native module. This callback type only permits a single invocation from native code."
I have used react-native-image-crop-picker to crop an image, now If I choose the cropped image I am getting the above error.
You need to provide example of your code and how are you using the callbacks from the native code.
But in general you can only invoke a callback from native code only once. However you can store callback and call it later as well.
More info on callbacks in general: https://facebook.github.io/react-native/docs/native-modules-ios#callbacks

React: Calling JS function after bridge has been destroyed --- How to find which function

I'm working on an update for our app. I've added a HeadlessTask and I've started seeing this warning in the console:
React: Calling JS function after bridge has been destroyed
How can I get the name of the function being executed?
From the error message I assume you're in java (react-native Android):
When you reload on react-native, what happens behind the scenes is that the react context is getting destroyed, and a new one is being created.
That error get's thrown whenever a react-native Native Module is trying to do work, by using the old react context (the one that was valid before the reload).
The last time I saw that error it also included an explanation as to which module tried to do work by using the old context. Typically it's the RCTDeviceEventEmitter module trying to send a message to javascript.
You'll have to open logcat on Android studio and read the full error message.
p.s: If you're using react-native-navigation in your project, (after you discover which module is the trouble maker by using the logcat), make sure to search on their issues as they are heavily using the native side of react-native android, and I've seen lot's of similar issues so far.
Never found a good solution to this until recently, so thought I'd put this here in case it helps someone else. I finally got around this error by doing the following:
public class RNMyNativeModule extends ReactContextBaseModule {
private ReactApplicationContext currentContext;
public RNMyNativeModule(ReactApplicationContext reactContext) {
super(reactContext);
currentContext = reactContext;
}
public void myEmitFn(String emitMe) {
currentContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("RNMyNativeModuleEvent", emitMe);
}
}
Basically, in your module constructor, make sure you capture the currentContext in it's own variable, and just use that whenever you need it. Otherwise it gets overwritten and destroyed whenever a live reload happens, and getReactApplicationContext() is not actually giving you the current context.

iOS9 storyboard what is unhandled action (handleNonLaunchSpecificActions)?

I've noticed the following error popping up in the console when running my app on iOS 9 when using a storyboard. I'm using xCode7. Is this something I need to be concerned about?
-[UIApplication _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion:] ** unhandled action -> <FBSSceneSnapshotAction: 0x176bfb20> {
handler = remote;
info = <BSSettings: 0x176a5d90> {
(1) = 5;
};
}
There is nothing wrong with your code. This is a logging message internal to Apple, and you should file a radar about it.
There are two hints that show that this is probably Apple's code:
The underscore leading the method name _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion is a convention indicating that the method is private/internal to the class that it's declared in. (See this comment.)
It's reasonable to guess that the two letter prefix in FBSSceneSnapshotAction is shorthand for FrontBoard, which according to Rene Ritchie in "iOS 9 wish-list: Guest Mode" is part of the whole family of software related to launching apps:
With iOS 8, Apple refactored its system manager, SpringBoard, into several smaller, more focused components. In addition to BackBoard, which was already spun off to handle background tasks, they added Frontboard for foreground tasks. They also added PreBoard to handle the Lock screen under secure, encrypted conditions. [...]
I have no idea what the BS prefix in BSSettings is for, but
BS is shorthand for BackBoard Settings, and an analysis of this log message would indicate that it's not anything you did, and you should file a radar with steps to reproduce the logging message.
If you want to try and grab a stack trace, you can implement the category linked to here. Some would argue that overriding private API is a bad idea, but in this case a temporary injection to grab a stack trace can't be too harmful.
EDIT:
But, we still want to know what this action is. So I put a breakpoint on -[UIApplication _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion] and started printing out register values and found a class called FBSceneImpl which had a whole bunch of information about my application:
We are able to find out which private method is called next (stored in the program counter, instruction pointer, register 15.)
I tried finding the un-handled FBSceneSnapshotAction referenced in the log, but no dice. Then, I subclassed UIApplication, and overrode _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion. Now I was able to get at the action directly, but still, we don't know what it is.
Then, I looked at the FBSceneSnapshotAction again. Turns out it has a superclass called BSAction.
Then I wrote a tool similar to RuntimeBrowser and looked up all of the subclasses of BSAction. It turns out that there's quite a list of them:
The two method names we have (one from the log and one from the program counter on the devices) indicate that these actions are used under the hood for passing actions around the system.
Some actions are probably sent up to the app delegate's callbacks, while others are handled internally.
What's happening here is that there is an action that wasn't handled correctly and the system is noting it. We weren't supposed to see it, apparently.
AFAIK, the info above is related to iOS during snapshot the screen (i suppose for double click home multitask related behaviour).I deeply investigated my application and seems that it does not get any side behaviours. You can safely ignore it, for now.
You can use the following gist simple category to test yourself against the calls to the above function:
I have figured it out, it will happen when you have IBAction method declared in .h or .m file but you have not bind it to any control.
.m example:
- (IBAction)click:(id)sender{
}
but not assigned this method to any control in storyboard.
haven't find out why it happens in my app, but at least you can catch the exception, if you want to keep this from popping up in your log pane. It's not a solution, but it might give you more insight why it is happing by inspecting any of the arguments that are passed in the catch.
swift 2 version:
import UIKit
extension UIApplication {
func _handleNonLaunchSpecificActions(arg1: AnyObject, forScene arg2: AnyObject, withTransitionContext arg3: AnyObject, completion completionHandler: () -> Void) {
//whatever you want to do in this catch
print("handleNonLaunchSpecificActions catched")
}
}

VMR-9 Renderless mode: SelectAtPosition() crashes

How to solve the SelectAtPosition() crash when using VMR-9 in renderless mode?
The SelectAtPosition() crashing problem when renderless mode is used can be solved by implementing IVMRWindowlessControl9 interface in your custom-allocator.
In addition to IVMRSurfaceAllocator9 & IVMRImagePresenter9, implement IMRWindowlessControl9, too!
class CAllocator : public IVMRSurfaceAllocator9, IVMRImagePresenter9, IVMRWindowlessControl9
Define all the virtual functions of IVMRWindowlessControl9. In my case, I needed to put extra codes
on the following functions to make the mouse handling properly work: GetAspectRatioMode(), SetAspectRatioMode() ,SetAspectRatioMode(),GetVideoPosition() & GetNativeVideoSize(). The other functions just return S_OK.
M$ SDK documentation did not mention that we need to implement this!!! After hours of debugging,
I've noticed that quartz.dll tries to query this interface from the custom-allocator. Sometimes it really pays to be patient!