I have my android app (react-native based) and the deep link first time when I run it while the app is not running in the background works great, but when the app runs in the background and i click on the deep-link it doesn't even open the app ... I'm not sure even where to start with this bug I tried a few console.logs in lifecycle events but they don't even run.
please guide me where should I look for the issue and how to fix it, thanks!
The deep linking is working as expected for me even the app is in background. Please check the below specifications.
Node Version : v12.18.x OR Greater
NPM Version : v6.14.x OR Greater
react-native-cli : 2.0.1
react-native : 0.63.x OR Greater
Please check if you have added below line in your AppDelegate.m.
#import <React/RCTLinkingManager.h>
It must be added above #ifdef FB_SONARKIT_ENABLED line. Adding it below this line will cause failing of build while Archiving it for release.
Please check if you have added below code in your AppDelegate.m which is responsible for deep linking.
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
It will work for app cold boot, but it will not work if your app is in background. For this, you need to add below code in your AppDelegate.m
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id> *_Nullable))restorationHandler {
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
This should work irrespective of your AppState: active **OR** background.
This worked for me as expected! Give it a try. This is should definitely work.
Thanks in advance!
componentDidMount() {
Linking.addEventListener('url', this._handleDeepLinkingURL);
this._handleDeepLinkingURL(); /* Invoking method manually on launching app very first time */
},
componentWillUnmount() {
Linking.removeEventListener('url', this._handleDeepLinkingURL);
},
_handleDeepLinkingURL(event) {
/*
console.log(event.url);
event.url will hold the url of the app if the app launched when its running in background
event param will be undefined when the app launched from kill state or first time
*/
Linking.getInitialURL()
.then(url => {
if (url && url.indexOf("?params=") !== -1) {
const paramsString = url.substr(
url.indexOf("?params=") + 8
);
alert('paramsString is : ' + paramsString);
}
})
}
Related
When opening my app in simulator via a deeplink, url event never fires.
Note that getInitialurl works if app was closed.
But if app is already open and I run npx uri-scheme open "mychat://bar" --ios, app focuses but no url event fires...
Anyone had this problem ?
I'm running XCode 13.4.1
MacOS 12.5.1
React Native 0.70
repo to reproduce bug
In my case, I was adding the required code block of AppDelegate below #interface AppDelegate, but have to add inside #implementation AppDelegate which below interface.
The event needs some additional configuration within your AppDelegate.m file in order to emit the events as mentioned in the docs. Either open your Project from XCode and edit AppDelegate.m or open ./ios/{YOUR_PROJECT_NAME}/AppDelegate.m (or AppDelegate.mm) file and add the following lines at the end of the file before the #end tag comes:
// Add this inside `#implementation AppDelegate` above `#end`:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
// Add this inside `#implementation AppDelegate` above `#end`:
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
#end
Important: If you get this working, you have two ways of handling the deeplink event and you have to handle them seperately! I think the second one will help you to solve your problem.
1 - The app is closed and will open by the deeplink:
Linking.getInitialURL().then(url => {
if(url != null) {
//DoSomethingWithUrl
}
});
2 - The app is already running and will be focused using deeplink:
Linking.addEventListener('url',(url)=>{
if(url != null) {
//DoSomethingWithUrl
}
});
Putting these lines within your apps view and assumed your app has some sort of state (e.g. using the useState hook or redux) it'll called on every state change that occurs, due to everything beside the state itself will be re-rendered on changing the state. Therefore I would suggest you to call these two methods only one time when app is starting and you can accomplish that by doing so:
const [isInitialStart, setInitialStart] = useState(true);
if(isInitialStart){
Linking.getInitialURL().then(url => {
if(url != null) {
//DoSomethingWithUrl
}
});
Linking.addEventListener('url',(url)=>{
if(url != null) {
//DoSomethingWithUrl
}
});
setInitialStart(false);
}
I hope this will help you to solve your problem.
Our React Native project has both Segment and CodePush integrated.
Segment works fine, normally.
But there's a scenario where Segment stops working right after a CodePush update has been installed. Nothing gets flushed out and none of the events are shown in the Live Debugger list - except for events that are tracked in the native layer (eg: "Application Opened" and "Application Backgrounded" - only these show up).
So I assumed that any Segment call in the RN - Javascript layer only stops working after the app restarts the bundle (by CodePush).
The user should close and kill the app and reopen the app for it to work normally again.
Happens to both iOS and Android (when installed from the store).
And weirdly enough, I cannot recreate the issue on Android when I run a debug or release build directly to my Android phone from my machine (it just works 🤷♂️). Same goes for iOS simulators.
Any ideas or workarounds on this?
This is how the setup looks like:
import React, { Component } from "react";
import CodePush from "react-native-code-push";
import segment from "#segment/analytics-react-native";
...
class App extends Component {
constructor(props) {
super(props);
this.setupSegment();
}
private setupSegment(): void {
segment.setup("<Segment Write Key>", {
debug: true,
trackAppLifecycleEvents: true,
ios: {
trackAdvertising: true
}
});
}
...
}
export default CodePush({
installMode: CodePush.InstallMode.IMMEDIATE,
checkFrequency: CodePush.CheckFrequency.ON_APP_RESUME,
updateDialog: null
})(App);
react-native: 0.61.5
react-native-code-push: 6.1.0
#segment/analytics-react-native: 1.1.1-beta.2 (also tried 1.1.1-beta.5)
By setting breakpoints, i noticed that the handler was getting called when the app is exiting.
Am I doing something wrong? If not, why would branch want to do this?
// This is in app delegate
branch.initSessionWithLaunchOptions(launchOptions, automaticallyDisplayDeepLinkController: true, deepLinkHandler: { params, error in
// breakpoint
if error == nil {
self.handleDeepLink(params)
}
else {
error?.showInToast()
}
})
}
Alex from Branch here: this is not expected behavior from our SDK. Are you overriding any of the other app lifecycle methods (e.g., applicationDidBecomeActive or applicationWillResignActive)?
I write in Objective-C.
I'm using remote push up notification for app control. I don't want to disturb users with alerts and sounds when app is not active. What i need to do - if app is not active - do nothing when notification is recieved.
Thank you.
You want to send silent notifications. A notification sent without an aps key is silent. iOS will not play a sound, nor will it display a banner on the device. Your app will still receive such notifications, if it's running, and can respond as it chooses. If the app is in the background, it can display a local notification, or it can just ignore it.
Here are palyload structure which you need to receive for your case:
{
"aps" : {
"badge" : 5,
},
"acme1" : "bar",
"acme2" : [ "bang", "whiz" ]
}
BTWL: you can not mention badge as well, if you don't need it.
just add below code into your Appdelegate.m file
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
UIApplicationState state = [application applicationState];
if(state == UIApplicationStateActive)
{
//app in active mode
// add your code as you want to handle
}
else{
// app is in terminated mode
}
}
It was easier than i thought. Just no alert and sound elements in array in section aps.
No alert - nothing to show.
No sound - nothing to play.
My React Native app wants to synchronize its local data with an API when the user opens the app. This should happen whenever the user returns to the app, not just when it first starts. Essentially, what I would like is the equivalent of AppDelegate's applicationDidBecomeActive callback, so that I can run synchronization code there. Obviously, I would like to do this in React Native instead.
As far as I can tell, the componentWillMount / componentDidMount callbacks on the root component only run when the app is initially loaded, not after the user leaves the app and comes back later (without explicitly quitting the app).
I thought that the AppState API would provide this functionality, but its change listeners don't fire in this case, either.
This seems like obvious functionality to have, so I must be missing something glaringly obvious. Help!
I fixed this by using the AppStateIOS API instead of AppState. The later works as expected on iOS devices: when the app goes to the background the "change" event fires, and again when it comes back to the foreground. The AppState API doesn't seem to fire the "change" event at all on an iOS device, as of React Native v0.18, as far as I can tell. The project's conventions suggest that AppState should be a cross-platform wrapper around AppStateIOS, but that doesn't seem to be the case here.
The following example should demonstrate the issue:
React.createClass({
componentDidMount() {
AppStateIOS.addEventListener('change', state =>
console.log('AppStateIOS changed to', state)
)
AppState.addEventListener('change', state =>
console.log('AppState changed to', state)
)
},
render() {
return <View/>
}
})
When this component is mounted into a React Native app, you will see "AppStateIOS changed to ..." when closing and opening the app. You will never see "AppState changed to ..." as far as I'm aware.
Update
It appears that this was fixed in React Native recently (as of v26). You can now use AppState as advertised on iOS and Android.
I don't think that you're missing something. This functionality is just not provided by react-native out of the box. Maybe the idea was to simplify, since for most apps it's enough to perform data sync when the app returns to foreground.
You can either create your own native module which provides this functionality or you can go with a simple (sort of hacky?) solution:
In your AppDelegate save a reference to the react root view:
#interface AppDelegate()
#property (nonatomic, strong) RCTRootView *rootView;
#end
When initializing the view, set your property and use it:
self.rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:#"MyApp"
initialProperties:nil
launchOptions:launchOptions];
Now implement the AppDelegate methods for handling the active state like you would do in your iOS apps and pass the information as props:
-(void)onAppActiveStateChanged:(BOOL)appBecameActive
{
NSMutableDictionary *newProps = [NSMutableDictionary dictionary];
if (self.rootView.appProperties != nil) {
[newProps addEntriesFromDictionary:self.rootView.appProperties];
}
newProps[#"appDidBecomeActive"] = #(appBecameActive);
self.rootView.appProperties = newProps;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
[self onAppActiveStateChanged:NO];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[self onAppActiveStateChanged:YES];
}
On the JavaScript side, do whatever you need when the props change:
componentWillReceiveProps(nextProps) {
if (nextProps.appDidBecomeActive) {
alert('app did become active');
}
}
I'm not sure if this is the best approach, but it's supposed to work...
I think you answered the question your self already. Why don't you use a combination of componentWillMount and AppState -> 'change'? That should cover all cases. I use this for syncing my app via CodePush.