How to inject the Firebase messaging isHeadless prop into AppDelegate.mm file with react-native 0.69.1 - push-notification

System Information:
System:
OS: macOS 12.4
CPU: (8) arm64 Apple M1
Memory: 97.44 MB / 8.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 16.16.0 - /opt/homebrew/opt/node#16/bin/node
Yarn: 1.22.19 - /opt/homebrew/bin/yarn
npm: 8.11.0 - /opt/homebrew/opt/node#16/bin/npm
Watchman: 2022.07.04.00 - /opt/homebrew/bin/watchman
Managers:
CocoaPods: 1.11.3 - /opt/homebrew/bin/pod
SDKs:
iOS SDK:
Platforms: DriverKit 21.4, iOS 15.5, macOS 12.3, tvOS 15.4, watchOS 8.5
Android SDK: Not Found
IDEs:
Android Studio: 2020.3 AI-203.7717.56.2031.7583922
Xcode: 13.4.1/13F100 - /usr/bin/xcodebuild
Languages:
Java: 11.0.15 - /usr/bin/javac
npmPackages:
#react-native-community/cli: Not Found
react: 18.0.0 => 18.0.0
react-native: 0.69.1 => 0.69.1
react-native-macos: Not Found
npmGlobalPackages:
*react-native*: Not Found
Hello folks, I am working on a react-native 0.69.1 project that uses Firebase push-notification service. I confirm that I am successfully getting push notifications when the app is in the foreground, background and in a killed state. In order to correctly update my badge count when receiving a push notification outside the app, I need to add to index.js the firebase method setBackgroundMessageHandler and handle if the app was opened via isHeadless prop.
import {AppRegistry} from 'react-native';
import App from './src/App';
import {name as appName} from './app.json';
import messaging from '#react-native-firebase/messaging';
// Register background handler
messaging().setBackgroundMessageHandler(async remoteMessage => {
//Update badge count in here
});
function HeadlessCheck({isHeadless}) {
if (isHeadless) {
// App has been launched in the background by iOS, ignore
return null;
}
return App;
}
AppRegistry.registerComponent(appName, () => HeadlessCheck);
I am aware that the headless prop does not exist on iOS and needs to be injected into AppDelegate.m/AppDelegate.mm file. However firebase has not updated their documentation to inject the isHeadless prop into the AppDelegate.mm file created by react-native-0.69.1.
Below is the snipped of my AppDelegate.mm file where I have to inject the isHeadless prop in.
#import "AppDelegate.h"
#import "RNBootSplash.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTAppSetupUtils.h>
#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>
#import <Firebase.h>
#import <TSBackgroundFetch/TSBackgroundFetch.h>
#if RCT_NEW_ARCH_ENABLED
#import <React/CoreModulesPlugins.h>
#import <React/RCTCxxBridgeDelegate.h>
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
#import <React/RCTSurfacePresenter.h>
#import <React/RCTSurfacePresenterBridgeAdapter.h>
#import <ReactCommon/RCTTurboModuleManager.h>
#import <react/config/ReactNativeConfig.h>
static NSString *const kRNConcurrentRoot = #"concurrentRoot";
#interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
RCTTurboModuleManager *_turboModuleManager;
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
facebook::react::ContextContainer::Shared _contextContainer;
}
#end
#endif
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[FIRApp configure];
RCTAppSetupPrepareApp(application);
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = RCTAppSetupDefaultRootView(bridge, #"amigo", initProps);
if (#available(iOS 13.0, *)) {
rootView.backgroundColor = [UIColor systemBackgroundColor];
} else {
rootView.backgroundColor = [UIColor whiteColor];
}
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
// Define UNUserNotificationCenter
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
// [REQUIRED] Register BackgroundFetch
[[TSBackgroundFetch sharedInstance] didFinishLaunching];
[RNBootSplash initWithStoryboard:#"BootSplash" rootView:rootView];
return YES;
}
//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}
// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
[RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler
{
[RNCPushNotificationIOS didReceiveNotificationResponse:response];
}
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
///
/// #see: https://reactjs.org/blog/2022/03/29/react-v18.html
/// #note: This requires to be rendering on Fabric (i.e. on the New Architecture).
/// #return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
- (BOOL)concurrentRootEnabled
{
// Switch this bool to turn on and off the concurrent root
return true;
}
- (NSDictionary *)prepareInitialProps
{
NSMutableDictionary *initProps = [NSMutableDictionary new];
#ifdef RCT_NEW_ARCH_ENABLED
initProps[kRNConcurrentRoot] = #([self concurrentRootEnabled]);
#endif
return initProps;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:#"index"];
#else
return [[NSBundle mainBundle] URLForResource:#"main" withExtension:#"jsbundle"];
#endif
}
#if RCT_NEW_ARCH_ENABLED
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
}
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
{
return nullptr;
}
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
initParams:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
return RCTAppSetupDefaultModuleFromClass(moduleClass);
}
#endif
#end
RN Community , please help me out if you experience the same issue :)

You can change this
UIView *rootView = RCTAppSetupDefaultRootView(bridge, #"amigo", initProps);
to
NSDictionary *appProperties = [RNFBMessagingModule addCustomPropsToUserProps:nil withLaunchOptions:launchOptions];
NSMutableDictionary *initialProps = [initProps mutableCopy];
[initialProps addEntriesFromDictionary:appProperties];
UIView *rootView = RCTAppSetupDefaultRootView(bridge, #"amigo", initialProps);

Related

No visible #interface for 'InstanceName' declares the selector'

My name is Bob. I code for the Parks Department of NYC. I'm starting to learn Objective C and have a "Semantic Issue." I must have a blind spot, because I checked all the following code and don't see the problem. Thank you for ant help you can give
"No visible #interface for 'Greeter' declares the selector 'setGreetingText::"
#import <Foundation/Foundation.h>
#import "Greeter.h"
int main (int argc, const char * argv[])
{
#autoreleasepool {
Greeter* myGreeter = [[Greeter alloc] init];
// error on this line
[myGreeter setGreetingText: #"Hello Objective-C!!" : #"Hello VIP Objective-C!!"];
[myGreeter issueGreeting];
}
return 0;
}
======================================
import "Greeter.h"
import "NewGreeter.h"
#implementation Greeter
NewGreeter *VTS;
- (NSString*) greetingText
{
return [self greetingText];
}
- (void) setGreetingText:(NSString*) newText01
andThisToo:(NSString*) newText02
{
greetingText = newText01;
[VTS setNewGreetingText: newText02];
}
- (void) issueGreeting
{
NSLog(#"%#", greetingText);
}
#import "NewGreeter.h"
#implementation NewGreeter
- (NSString*) NewGreetingText
{
return greeting;
}
- (void) setNewGreetingText:(NSString*) newText
{
greeting = newText;
}
- (void) issueNewGreeting
{
NSLog(#"%#", [self NewGreetingText]);
}
#end
#import <Foundation/Foundation.h>
#import "NewGreeter.h"
#interface Greeter : NSObject
{
NSString *greetingText;
}
- (void) setGreetingText:(NSString*) newText01
andThisToo:(NSString*) newText02;
- (NSString*) greetingText;
- (void) setGreetingText:(NSString*) newText;
- (void) issueGreeting;
#import <Foundation/Foundation.h>
#interface NewGreeter : NSObject
{
NSString *greetingText;
}
- (NSString*) NewGreetingText;
- (void) setNewGreetingText:(NSString*) newText;
- (void) issueNewGreeting;
#end
#import <Foundation/Foundation.h>
#interface NewGreeter : NSObject
{
NSString *greetingText;
}
- (NSString*) NewGreetingText;
- (void) setNewGreetingText:(NSString*) newText;
- (void) issueNewGreeting;
#end
You implement the method named -setGreetingText:andThisToo:, but you're trying to use it incorrectly. You need to change your call to:
[myGreeter setGreetingText: #"Hello Objective-C!!" andThisToo:#"Hello VIP Objective-C!!"];
Notice the andThisToo bit.
If you're only trying to set your object's properties, then you don't need to expose custom methods for that. Instead, you could do:
// In Greeter.h
#interface Greeter: NSObject
#property NSString *greetingText;
#end
// In Greeter.m
#implementation Greeter
#synthesize greetingText = _greetingText; // Create a backing instance variable
#end
This creates an instance variable setter method named setGreetingText and an instance variable getter named greetingText. I'd recommend you read up on creating object properties.

swipeGestureRecognizer not working when restarting app in Ios Simulator

Im toying around with SwipeGestureRecognizer.
It´s working as I expect, but when I exit the app in the simulator and run it again, the SwipeGestureRecognizer no longer responds. If i run it again after quitting iOS simulator, it works.
This is what I have tried:
#import <UIKit/UIKit.h>
#interface swipeTestUI : UIViewController
#property (strong, nonatomic) IBOutlet UIImageView *imageView;
- (IBAction)mangeSwipeControl:(UIGestureRecognizer*)sender;
#end
Implementation file:
#import "swipeTestUI.h"
#interface swipeTestUI ()
#end
#implementation swipeTestUI
#synthesize imageView;
int listOfImages = 0;
- (IBAction)mangeSwipeControl:(UIGestureRecognizer *)sender {
NSLog(#"swipe ok");
NSArray *images=[[NSArray alloc]initWithObjects:
#"1.png",
#"2.png",
#"3.png",
#"4.png",
#"5.png",
#"5.png",nil];
UISwipeGestureRecognizerDirection direction = [(UISwipeGestureRecognizer *) sender direction];
switch (direction) {
case UISwipeGestureRecognizerDirectionLeft:
listOfImages++;
break;
case UISwipeGestureRecognizerDirectionRight:
listOfImages--;
break;
default:
break;
}
listOfImages = (listOfImages < 0) ? ([images count] -1):listOfImages % [images count];
imageView.image = [UIImage imageNamed:[images objectAtIndex:listOfImages]];
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
return (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
I think this is an iOS simulator issue. I have modified the code a bit, I think its more effective
the header file is the same, the implementation file is as followed.
-(IBAction)manageSwipeControl:(UISwipeGestureRecognizer *)gesture {
NSArray *images=[[NSArray alloc]initWithObjects:
#"1.png",
#"2.png",
#"3.png",
#"4.png",
#"5.png",
#"6.png",nil];
if (gesture.direction == UISwipeGestureRecognizerDirectionLeft)
{
listOfImages++;
}
else if (gesture.direction == UISwipeGestureRecognizerDirectionRight)
{
listOfImages--;
}
listOfImages = (listOfImages < 0) ? ([images count] -1):listOfImages % [images count];
imageView.image = [UIImage imageNamed:[images objectAtIndex:listOfImages]];
//[self.imageView removeGestureRecognizer:sender];
}
Works like a charm. Funny thing. When I quit the application in the iOS simulator, and reopen it. It still works. If I quit and remove it from iOS simulator memory - it does not. If I launch the iOS simulator directly from the OS, no problem what so ever. I can both quit and remove it from memory, and it still works.
Still, a fun way to spend on a rainy weekend. Hope this info is useful to other new developers like myself.

how to get text of push notification (Urban Airship)

I searched my questioin and didn't find the answer. So i'm using urban airship to push notifications and it is working fine. So now i want to make this: when user taps on notification to open the app i want to give him text of notification again in uialertview.
So where i can take the text of notification?
can i do this in this method
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
UIApplicationState appState = UIApplicationStateActive;
if ([application respondsToSelector:#selector(applicationState)]) {
appState = application.applicationState;
}
[[UAPush shared] handleNotification:userInfo applicationState:appState];
[[UAPush shared] resetBadge];}
Thank you very much.
I can read the push notification that opened my app using didReceiveRemoteNotification:(NSDictionary *)userInfo - Here is my didReceiveRemoteNotification:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
// TestFlight API
TESTFLIGHT_CHECKPOINT(#"Receive Remote Notification");
// Urban Airship
[[UAPush shared] handleNotification:userInfo applicationState:application.applicationState];
NSLog(#"userInfo: %#",userInfo);
[UAInboxPushHandler handleNotification:userInfo];
}
When I launch my app from an aps notification I have this in the debug window:
2013-01-17 20:52:49:947 iFlightBag[8229:2311] -[AppDelegate_Shared application:didReceiveRemoteNotification:] [Line 2403] userInfo: {
"_" = "l0-mgGElEeKbHAAbIbyL6A";
aps = {
alert = "Manual: IOM update 2013-01-17T20:14:42-08:00";
badge = 0;
};
operation = update;
tab = manuals;
}

Thread 1:EXC_BAD_ACCESS(code=2, address=0xbf7ffffc)

Am writing a simple multi view application with one root controller and two view controllers (blue and yellow). When I try to run it in the iPhone Simulator, I get an error by the #synthesize property. I have commented out the error on that line.
Can you tell me what the error means, and how can I get the app to run?
Thank you.
#import "SwitchViewController.h"
#import "BlueViewController.h"
#import "YellowViewController.h"
#interface SwitchViewController ()
#end
#implementation SwitchViewController
#synthesize yellowViewController;
#synthesize blueViewController; //Thread 1:EXC_BAD_ACCESS(code=2, address=0xbf7ffffc)
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)loadView
{
// If you create your views manually, you MUST override this method and use it to create your views.
// If you use Interface Builder to create your views, then you must NOT override this method.
}
- (void)viewDidLoad
{
self.blueViewController = [[BlueViewController alloc]initWithNibName:#"BlueView" bundle:nil];
[self.view insertSubview:self.blueViewController.view atIndex:0];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (IBAction)switchViews:(id)sender
{
if(self.yellowViewController.view.superview==nil) {
if(self.yellowViewController==nil) {
self.yellowViewController =
[[YellowViewController alloc] initWithNibName:#"YellowView" bundle:nil];
}
[blueViewController.view removeFromSuperview];
[self.view insertSubview:self.yellowViewController.view atIndex:0];
} else {
if (self.blueViewController == nil) {
self.blueViewController =
[[BlueViewController alloc] initWithNibName:#"BlueView" bundle:nil];
}
}
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use
if (self.blueViewController.view.superview == nil) {
self.blueViewController = nil;
} else {
self.yellowViewController = nil;
}
}
#end
Comment out the loadView method in your SwitchViewController, BlueViewController, and YellowViewController. The empty application template was changed to leave these uncommented in recent versions of XCode, but the Beginning iOS Development book you are following used an older version with the stubbed methods pre-commented.

block inside NSOPeration subclass crashing in the device but not in the simulator

I have been struggling with this piece of code for a while and I just don't know why it happens that when running in the device ... the app crashes with an EXC_BAD_ACCESS Error but when running in the simulator it runs fine.
The scenario: A subclass of NSOperation that makes an async connection with NSURLConnection and gets custom data. When finished, it calls the block with the downloaded data.
Here is the .h file:
#interface FileDownloader : NSOperation <NSURLConnectionDataDelegate>
typedef void (^CompletionBlockForFile)(NSData *);
- (id)initWithCompletionBlock:(CompletionBlockForFile)block;
#end
and the .m file:
#interface FileDownloader ()
#property (strong, nonatomic) CompletionBlockForFile completionBlock;
#property (strong, nonatomic) NSMutableData *downloadedData;
- (void)downloadFileWithCompletionBlock:(CompletionBlockForFile)block;
#end
#implementation FileDownloader
#synthesize downloadedData = _downloadedData;
#synthesize completionBlock = _completionBlock;
- (id)initWithCompletionBlock:(CompletionBlockForFile)block
{
self = [super init];
if (self) {
_completionBlock = block;
}
return self;
}
- (void)main
{
if (self.isCancelled) return;
if (_completionBlock) {
[self downloadFileWithCompletionBlock:_completionBlock];
}
}
- (void)downloadFileWithCompletionBlock:(CompletionBlockForFile)block
{
NSURL *url = [NSURL URLWithString:#"http://www.google.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
[connection start];
});
}
... delegate methods of NSURLConnection
#end
And Finally, the method that adds the operation object to the queue at the MainViewController Class:
- (void)viewDidLoad
{
[super viewDidLoad];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
FileDownloader *fileDownloader = [[FileDownloader alloc] initWithCompletionBlock:^(NSData *data){ // <----- HERE IS THE EXC_BAD_ACCES ERROR JUST WHEN RUNNING IN THE DEVICE !!! :S
NSLog(#"%#", data);
}];
[queue addOperation:fileDownloader];
}
Can anybody explain me what am I doing wrong? And is it correct to put strong for the block var in the property? Why not assign? Or weak ?
Thanks in advance :)
Blocks should be copied and not retained.
Change
#property (**strong**, nonatomic) CompletionBlockForFile completionBlock;
to
#property (**copy**, nonatomic) CompletionBlockForFile completionBlock;
and it should work

Resources