Cordova plugin with framework with Appdelegate - ios

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.

Related

React Native OneSignal Background Notification Issue in iOS

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);
}

Can't get Orientation notification on iPhone without UIViewController

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

Send custom iOS native errors with sentry-expo

I want to capture the iOS native errors to Sentry and also capture some custom messages.
I'm using the sentry-expo library but this only captures javascript errors even with the parameter:
deactivateStacktraceMerging: false
I'v been following the official installation guide https://docs.sentry.io/clients/react-native/ but I'm stuck when trying to configure the AppDelegate.m file
#import "AppDelegate.h"
#import "ExpoKit.h"
#import "EXViewController.h"
#import <Fabric/Fabric.h>
#import <Crashlytics/Crashlytics.h>
#if __has_include(<React/RNSentry.h>)
#import <React/RNSentry.h> // This is used for versions of react >= 0.40
#else
#import "RNSentry.h" // This is used for versions of react < 0.40
#endif
#interface AppDelegate ()
#property (nonatomic, strong) EXViewController *rootViewController;
#end
#implementation AppDelegate
-(EventsObserver*)getEventsObserver
{
return EventsObserver.shared;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[Fabric with:#[[Crashlytics class]]];
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
_window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
_window.backgroundColor = [UIColor whiteColor];
[[ExpoKit sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
_rootViewController = [ExpoKit sharedInstance].rootViewController;
_window.rootViewController = _rootViewController;
[_window makeKeyAndVisible];
[self getEventsObserver];
[RNSentry installWithRootView:rootView];
return YES;
}
At this point I don't have the rootView object of type RCTRootView. I'm using a EXViewController and the library does not support that type of object to invoke the function installWithRootView. Am I following a wrong path?

Why can't I call object methods from AppDelegate? No known class method for selector

I have a DataHandler class which acts like a singleton, and it has sharedHandler object method. I use it throughout the whole project, but for some reason, I can't use it in AppDelegate.
DataHandler.h
#import <Foundation/Foundation.h>
#interface DataHandler : NSObject
+ (id)sharedHandler;
- (void)logout;
#end
DataHandler.m
#import "DataHandler.h"
/**
* Singleton static method
* #return singleton instance
*/
+ (id)sharedHandler {
static DataHandler *sharedHandler = nil;
#synchronized (self) {
if (sharedHandler == nil)
sharedHandler = [[self alloc] init];
}
return sharedHandler;
}
-(void) logout {
// ...
}
#end
AppDelegate.m
#import "AppDelegate.h"
#import "DataHandler.h"
#implementation AppDelegate {
- (void)applicationWillResignActive:(UIApplication *)application {
[[DataHandler sharedHandler] logout];
}
#end
I keep getting:
Error: no known class method for selector 'sharedHandler`
Error: no known instance method for selector 'logout'
What is the cause of this behavior?
You have two files named DataHandler.h and the import in AppDelegate.m is picking up the wrong file. Note that it may be picking up a file that's not actually in your project, as long as it's in the folder on disk.

Changing AppDelegate file of example I want to use in my app

Hi all I'm new in creating app for iOS , so sorry if the question stupid a little .
I'm trying to build app where log in to Facebook and getting friend list is only a part, so I want to use the example providing from Facebook to developers , but it have appDelegate file with some methods like this:
#import "FPAppDelegate.h"
#import "FPViewController.h"
#implementation FPAppDelegate
#synthesize window = _window;
#synthesize rootViewController = _rootViewController;
- (BOOL)application:(UIApplication *)application
.......
return [FBSession.activeSession handleOpenURL:url];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[FBFriendPickerViewController class];
........
return YES;
}
- (void)applicationWillTerminate:(UIApplication *)application {
[FBSession.activeSession close];
}
And it have xib file for FPViewController with h and m files, so what the better way to use this example in my app, I understand that I can't use two appDelegate files, and I trying to change FPAppDelegate that is UIResponder to UIViewController class to push it from some place in my app like this:
- (IBAction)loginFacebook:(id)sender
{
NSLog(#"Facebook");
delegateViewController *appDelegate = [[delegateViewController alloc] initWithNibName:nil bundle:NULL];
[self.navigationController pushViewController: appDelegate animated:YES];
}
where delegateViewController is FPAppDelegate of example I changing to:
#import "delegateViewController.h"
#import "AppDelegate.h"
#import "FPViewController.h"
#interface delegateViewController ()
#end
#implementation delegateViewController
#synthesize window2=_window2;
#synthesize rootViewController2=_rootViewController2;
- (BOOL)application:(UIApplication *)application
......
return [FBSession.activeSession handleOpenURL:url];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[FBFriendPickerViewController class];
......
self.window2 = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window2.rootViewController = navigationController;
[self.window2 makeKeyAndVisible];
return YES;
}
- (void)applicationWillTerminate:(UIApplication *)application {
........
[FBSession.activeSession close];
}
but it's not working, so, if I need to change also xib file? Or to change appDelegate file of my project? Or existing another way to use this example in my app? I would like you to explain this to me.
You should start by working through the Apple tutorials so you can understand the UIKit framework to start developing an iOS app.
http://developer.apple.com/library/ios/#referencelibrary/GettingStarted/RoadMapiOS/chapters/Introduction.html

Resources