I followed this guide to setup the OneSignal background notification in My react native App: https://documentation.onesignal.com/docs/rn-android-native-module-setup-for-notification-service-extension#ios-notification-service-extension-module
Background notification is working as expected when I installed the App directly to My device through Xcode. But when I archive the build and install it from TestFlight, background notification doesn't work. Unless emitNotificationEvent event I added is not get triggered, even though the push notification is received.
When I trace the issue with Archived Build in Xcode (Using device console), noticed that _instance is null in NotificationExtensionModule.m. Anyone experienced similar issue or any idea what could be the reason ?
Note
Xcode Version: 13.4
Receiving the push notification in both instance (from test flight or direct install)
To put a new version in TestFlight, used to increase the build number with same version number.
Tried cleaning the build folder, re-installing the pods, nothing worked still
Adding the code snippet for further understanding of My issue:
AppDelegate.h
#import <Foundation/Foundation.h>
#import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate,RCTBridgeDelegate,UNUserNotificationCenterDelegate>
#property (nonatomic, strong) UIWindow *window;
#end
AppDelegate.m
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
//access NotificationServiceExtensionModule emitNotificationEvent method
[NotificationServiceExtensionModule.sharedInstance emitNotificationEvent:userInfo ];
completionHandler(UIBackgroundFetchResultNoData);
}
NotificationServiceExtensionModule.h
#import <foundation/Foundation.h>
// NotificationServiceExtensionModule.h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#interface NotificationServiceExtensionModule : RCTEventEmitter <RCTBridgeModule>
+ (NotificationServiceExtensionModule*) sharedInstance;
- (void)emitNotificationEvent:(NSDictionary *)userInfo;
#end
NotificationServiceExtensionModule.m
#import <Foundation/Foundation.h>
// NotificationServiceExtensionModule.m
#import "NotificationServiceExtensionModule.h"
#implementation NotificationServiceExtensionModule
static NotificationServiceExtensionModule* _instance = nil;
+(NotificationServiceExtensionModule*) sharedInstance {
// #synchronized( _instance ) {
// if( !_instance ) {
// _instance = [[NotificationServiceExtensionModule alloc] init];
// }
// }
return _instance; // this returns null when installed from TestFlight.
}
// To export a module named NotificationServiceExtensionModule
RCT_EXPORT_MODULE();
- (NSArray<NSString *> *)supportedEvents
{
NSLog(#"Supported EVENTS__________________________");
_instance = self;
return #[#"NotificationEvent"];
}
- (void)emitNotificationEvent:(NSDictionary *)userInfo
{
NSString *eventName = userInfo[#"custom"][#"a"];
[self sendEventWithName:#"NotificationEvent" body:#{#"notificationPayload": eventName}];
}
#end
You can use the following code to check if the app is running in the background or foreground:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
//access NotificationServiceExtensionModule emitNotificationEvent method
[NotificationServiceExtensionModule.sharedInstance emitNotificationEvent:userInfo ];
if (application.applicationState == UIApplicationStateActive) {
// App was in the foreground when the notification was received
} else {
// App was in the background when the notification was received
}
completionHandler(UIBackgroundFetchResultNoData);
}
Related
I am trying to develop an iPhone app using xcode13 with iPhone 11 simulator. I used Xcode template to create a basic app. Now I created a new class (not derived from UIViewController) and tried to get orientation notifications, but I am getting nothing. I tried to add the same code on class that derived from UIViewController and it worked. why ? why ?
Why it is only working from the UIViewController's derived class?
Can I make it work from a class that isn't derived from UIViewController ?
here is my code that is not working :
main.m
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "Orientation/Orientation.h"
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
#autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
NSLog(#"Hello world !");
OrientationService * os = [[OrientationService alloc] init];
[os onStart];
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
Orientation/Orientation.h
#ifndef Orientation_h
#define Orientation_h
#interface OrientationService : NSObject {
NSInteger * orientation_state;
}
- (void) onStart;
- (void) orientationStateDidChange:(NSNotification*) notification;
#end
Orientation/Orientation.m
#endif /* Orientation_h */
#import <Foundation/Foundation.h>
#import "Orientation.h"
#import <UIKit/Uikit.h>
#implementation OrientationService
-(void) orientationStateDidChange:(NSNotification*) notification {
NSLog(#"got notified");
}
-(instancetype)init{
self = [super init];
NSLog(#"OrientationService alloc");
return self;
}
-(void)onStart{
NSLog(#"starting orientation service");
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationStateDidChange:) name:(UIDeviceOrientationDidChangeNotification) object:nil];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}
#end
I spent so long on this but it just hit me! It's because os in main() isn't being retained in memory. Instead, either implement the functionality in the App Delegate, or retain an instance of your OrientationService object in the App Delegate. For example:
AppDelegate.h
#import <UIKit/UIKit.h>
#include "Orientation.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) OrientationService *os;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize os;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
os = [[OrientationService alloc] init];
[os onStart];
return YES;
}
#end
I have created a framework with a native sdk that the company I work has for push notifications. I have an Appdelegate inside this framework because there are some files that use it. I have added this framework as a plug in for a Cordova test application but as I see I can use only one of the two AppDelegates that exist now(one in framework and one in cordova app). I am trying to use only the framework's AppDelegate but to do this I must somehow use CDVAppDelegate. The problem is that the app in initialization doesn't run the correct didFinishLaunchingWithOptions function and I can't see the default first page of the cordova app. I have this files inside the plug in:
AppDelegateExtension.m
#import "xxxSDK/AppDelegate.h"
#import "xxxSDK/xxx.h"
#import "MainViewController.h"
#import "MyPlugin.h"
#implementation AppDelegate (MyPlugin)
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
#if (DEBUG == 1)
[xxx launchWithAppUUID:#"1256fbeb638b4426a98e4bacbc5f56f5" launchOptions:launchOptions];
#else
[xxx launchWithAppUUID:#"1256fbeb638b4426a98e4bacbc5f56f5" launchOptions:launchOptions];
#endif
[[xxx sharedService].pushManager registerForRemoteNotifications];
[[xxx sharedService].pushManager resetBadge];
MyPlugin.myPlugin.viewController = [[MainViewController alloc] init];
[MyPlugin.myPlugin application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
MyPlugin.h
#import <Cordova/CDVAppDelegate.h>
#interface MyPlugin : CDVAppDelegate
// Public static method
+ (CDVAppDelegate*) myPlugin;
#end
MyPlugin.m:
#import <Foundation/Foundation.h>
#import "MyPlugin.h"
#import "MainViewController.h"
#implementation MyPlugin
// Private static reference
static CDVAppDelegate* myPlugin;
// Public static method
+ (CDVAppDelegate*) myPlugin {
return myPlugin;
}
#end
Inside the framework the AppDelegate.h is this:
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#end
I have tried declare MyPlugin as a CDVPlugin too but the problem didnt get solved. Any thoughts?
Because AppDelegateExtension.m is a category of the AppDelegate class it extends the original class rather than implementing a subclass, so any methods you implement in your category which already exist in the main class (i.e. AppDelegate.m auto-generated by Cordova) will be replaced.
So in this case your didFinishLaunchingWithOptions in AppDelegateExtension.m is replacing didFinishLaunchingWithOptions in AppDelegate.m.
TL;DR: You need to swizzle the method to avoid destroying the original method:
#implementation AppDelegate (MyPlugin)
+ (void)load {
Method original = class_getInstanceMethod(self, #selector(application:didFinishLaunchingWithOptions:));
Method swizzled = class_getInstanceMethod(self, #selector(application:swizzledDidFinishLaunchingWithOptions:));
method_exchangeImplementations(original, swizzled);
}
- (BOOL)application:(UIApplication*)application swizzledDidFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
{
#if (DEBUG == 1)
[xxx launchWithAppUUID:#"1256fbeb638b4426a98e4bacbc5f56f5" launchOptions:launchOptions];
#else
[xxx launchWithAppUUID:#"1256fbeb638b4426a98e4bacbc5f56f5" launchOptions:launchOptions];
#endif
[[xxx sharedService].pushManager registerForRemoteNotifications];
[[xxx sharedService].pushManager resetBadge];
MyPlugin.myPlugin.viewController = [[MainViewController alloc] init];
[MyPlugin.myPlugin application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
You can see an example of this in cordova-plugin-firebasex.
Ok, i did this:
#implementation AppDelegate (MyPlugin)
+ (void)load {
Method original = class_getInstanceMethod(self, #selector(application:didFinishLaunchingWithOptions:));
Method swizzled = class_getInstanceMethod(self, #selector(application:swizzledDidFinishLaunchingWithOptions:));
method_exchangeImplementations(original, swizzled);
}
- (BOOL)application:(UIApplication*)application swizzledDidFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
#if (DEBUG == 1)
[Warply launchWithAppUUID:#"1256fbeb638b4426a98e4bacbc5f56f5" launchOptions:launchOptions];
#else
[Warply launchWithAppUUID:#"1256fbeb638b4426a98e4bacbc5f56f5" launchOptions:launchOptions];
#endif
[[Warply sharedService].pushManager registerForRemoteNotifications];
[[Warply sharedService].pushManager resetBadge];
CDVAppDelegate *appDelegate = [[CDVAppDelegate alloc]init];
[appDelegate application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
CDVAppdelegate's didFinishLaunchingWithOptions is running but I can not see in my device index.html view. My sdk runs properly but the cordova app is not running at all.
I assume that the thing is that AppDelegate : UIResponder can not support what I want to do and I am starting to believe that what I want can not be implemented.
I am trying to build a plug in for a Cordova App. In this plug in I have some native code for push notifications. I have added the files needed inside the plug in and now i am trying to call a header from app's AppDelegate.m. But the import has the error "file not found". I have added the header file in the plugin's plugin.xml as: < header-file src="src/ios/xxxxx/xxxxx.h" />
Any thoughts?
AppDelegate.m is auto-generated when Cordova creates the iOS platform project so making direct modifications to it is not recommended since if you rebuild the platform project with cordova plugin rm ios && cordova platform add ios, any changes will be lost.
Plugins can work around this by using a category extension to the AppDelegate class which allows you to bundle code which extends the AppDelegate in your plugin repo without affecting the auto-generated file.
For example, you would create myplugin/src/ios/AppDelegate+MyPlugin.h:
#import "AppDelegate.h"
#interface AppDelegate (MyPlugin) <UIApplicationDelegate>
#end
Then in myplugin/src/ios/AppDelegate+MyPlugin.m you can implement your app delegate, for example:
#import "AppDelegate+MyPlugin.h"
#implementation AppDelegate (MyPlugin)
// A UIApplication delegate
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(#"Remote notification received");
}
...
To allow your AppDelegate category to interact with your Cordova plugin class, you can expose a public method which returns its singleton instance which the AppDelegate category can call; for example:
myplugin/src/ios/MyPlugin.h:
#import <Cordova/CDV.h>
#interface MyPlugin : CDVPlugin
// Public static method
+ (MyPlugin*) myPlugin;
// A public instance method
- (void)logMessage: (NSString*)msg;
#end
myplugin/src/ios/MyPlugin.m:
#import "MyPlugin.h"
#implementation MyPlugin
// Private static reference
static MyPlugin* myplugin;
// Public static method
+ (MyPlugin*) myplugin {
return myplugin;
}
// implement CDVPlugin delegate
- (void)pluginInitialize {
myplugin = self;
}
// A public instance method
- (void)logMessage: (NSString*)msg
{
NSLog(#"MyPlugin: %#", msg);
}
#end
Then in myplugin/src/ios/AppDelegate+MyPlugin.m you can make use of your plugin methods:
#import "AppDelegate+MyPlugin.h"
#import "MyPlugin.h"
#implementation AppDelegate (MyPlugin)
// A UIApplication delegate
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[MyPlugin.myPlugin logMessage:#"Remote notification received"];
}
...
An example of a plugin which takes this approach is cordova-plugin-firebasex.
I'm messing around with Rdio's iOS SDK. I've set up everything correctly in "Getting Started" outlined here (http://www.rdio.com/developers/docs/libraries/ios/). The track key in the app delegate works and plays after I launch the app.
Now I'm trying to get a simple UIButton click to play a track, and I can't for the life of me get it to work.
I have this in ViewController.h
#import <UIKit/UIKit.h>
#import <Rdio/Rdio.h>
#interface ViewController : UIViewController <RdioDelegate, RDPlayerDelegate>
#property (readonly) Rdio *rdio;
#end
And in ViewController.m
- (IBAction)playButton:(UIButton *)sender
{
[self.rdio preparePlayerWithDelegate:nil];
NSArray *sources = [NSArray arrayWithObjects:#"t1", #"p1", #"a1", nil];
[self.rdio.player playSources:sources];
}
I really appreciate the help!
I resolved my issue. My issue was I wasn't calling the initializer initWithConsumerKey... that I had in my App Delegate. I had also failed to set it as a delegate properly.
So my App Delegate looks like this:
#import "AppDelegate.h"
static AppDelegate *launchedDelegate;
#interface AppDelegate ()
#end
#implementation AppDelegate
+ (Rdio *)rdioInstance
{
return launchedDelegate.rdio;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
launchedDelegate = self;
_rdio = [[Rdio alloc] initWithConsumerKey:#"removed" andSecret:#"removed" delegate:nil];
return YES;
}
and in ViewController.m:
- (IBAction)listenButton:(UIButton *)sender
{
_rdio = [AppDelegate rdioInstance];
[self.rdio preparePlayerWithDelegate:nil];
[self.rdio.player playSource:#"p12691138"];
}
Feeling silly that I didn't get that at first! Leaving it up here in case it helps anybody.
In my iOS newsstand app i need to access the default created viewContoller object in my appDelegate to start the background download when a notification arrives.
The problem is the viewController i want to access is not the root viewcontroller, if thats the case i can access it as follows,
MyViewController* mainController = (MyViewController*) self.window.rootViewController;
It would be great if anyone could guide me on this.
thanks.
have you tried as following methods,
In your AppDelegate.h.
#property (nonatomic, retain) HAViewController * haViewController;
In your AppDelegate.m
#synthesize haViewController;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.haViewController = [[HAViewController alloc] init];
return YES;
}
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
[self.haViewController startupdate];
}
Thanks!