问题
I'm working on iOS native extension for Adobe AIR that will get device token for push notifications. Unfortunately I'm not that munch objective-C programmer and I'm not sure if there's something wrong in the code I'm using. It compiles with no problem, I can use the extension with AIR but looks like registering for notifications doesn't return neither positive nor negative effect. So what I'm trying to do is register for notifications when RegisterDevice function is called from AIR and if it does register store the device token in deviceTokenString and if it doesn't register and comes back with the error I store the error in this string. When GetToken function is called I return deviceTokenString to AIR so it's either token or error. In AIR application I launch first RegisterDevice function and later GetToken function by clicking buttons. Unfortunately I don't get neither token nor error (also popup asking for permission doesn't show up). I was also trying to put registering part in didFinishLaunchingWithOptions but it looks like didFinishLaunchingWithOptions is never launched. If you guys can have a look if the code is OK I would be really grateful. Or maybe you have any ideas what else can be wrong? I have push SSL certificate enabled in provisioning portal. Here's the code I'm using
"NativePush.m":
#import "UIKit/UIKit.h"
#import "include/FlashRuntimeExtensions.h"
@implementation NativePush
@synthesize tokenString = _tokenString;
NSString *deviceTokenString = @"";
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return YES;
}
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSString *str =
[NSString stringWithFormat:@"%@",deviceToken];
deviceTokenString = str;
}
- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err
{
NSString *str = [NSString stringWithFormat: @"Error: %@", err];
deviceTokenString = str;
}
void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx,
uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet) {
*numFunctionsToTest = 2;
FRENamedFunction* func = (FRENamedFunction*)malloc(sizeof(FRENamedFunction)*2);
func[0].name = (const uint8_t*)"RegisterDevice";
func[0].functionData = NULL;
func[0].function = &RegisterDevice;
func[1].name = (const uint8_t*)"GetToken";
func[1].functionData = NULL;
func[1].function = &GetToken;
*functionsToSet = func;
}
void ContextFinalizer(FREContext ctx) {
return;
}
void ExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet,
FREContextFinalizer* ctxFinalizerToSet) {
*extDataToSet = NULL;
*ctxInitializerToSet = &ContextInitializer;
*ctxFinalizerToSet = &ContextFinalizer;
}
void ExtFinalizer(void* extData) {
return;
}
FREObject RegisterDevice(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
[[UIApplication sharedApplication]
registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeAlert |
UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound)];
return NULL;
}
FREObject GetToken(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]) {
NSString* tokenS = deviceTokenString;
char* tokenChar = [tokenS UTF8String];
FREObject tokenObject = NULL;
FRENewObjectFromUTF8( strlen(tokenChar)+1 , (const uint8_t*)tokenChar, &tokenObject);
return tokenObject;
}
@end
and header file "NativePush.h":
import "Foundation/Foundation.h"
import "include/FlashRuntimeExtensions.h"
@interface NativePush : NSObject
@property (nonatomic, retain) NSString* tokenString;
FREObject RegisterDevice(
FREContext ctx,
void* funcData,
uint32_t argc,
FREObject arg[]
);
FREObject GetToken(
FREContext ctx,
void* funcData,
uint32_t argc,
FREObject arg[]
);
void ContextInitializer(
void* extData,
const uint8_t* ctxType,
FREContext ctx,
uint32_t* numFunctionsToTest,
const FRENamedFunction** functionsToSet
);
void ContextFinalizer(FREContext ctx);
void ExtInitializer(
void** extDataToSet,
FREContextInitializer* ctxInitializerToSet,
FREContextFinalizer* ctxFinalizerToSet
);
void ExtFinalizer(void* extData);
@end
回答1:
Ok,
After tearing my hair out for 3 days I figured it out. I dont set the delegate, because that will break all Adobe's stuff. I create a custom subclass of the existing delegate and override the delegate functions that have to do with APNS. My code is below.
//empty delegate functions, stubbed signature is so we can find this method in the delegate
//and override it with our custom implementation
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken{}
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error{}
//custom implementations of empty signatures above
void didRegisterForRemoteNotificationsWithDeviceToken(id self, SEL _cmd, UIApplication* application, NSData* deviceToken)
{
NSLog(@"My token is: %@", deviceToken);
}
void didFailToRegisterForRemoteNotificationsWithError(id self, SEL _cmd, UIApplication* application, NSError* error)
{
NSLog(@"Failed to get token, error: %@", error);
}
// ContextInitializer()
//
// The context initializer is called when the runtime creates the extension context instance.
void PushContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx,
uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet)
{
//injects our modified delegate functions into the sharedApplication delegate
id delegate = [[UIApplication sharedApplication] delegate];
Class objectClass = object_getClass(delegate);
NSString *newClassName = [NSString stringWithFormat:@"Custom_%@", NSStringFromClass(objectClass)];
Class modDelegate = NSClassFromString(newClassName);
if (modDelegate == nil) {
// this class doesn't exist; create it
// allocate a new class
modDelegate = objc_allocateClassPair(objectClass, [newClassName UTF8String], 0);
SEL selectorToOverride1 = @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:);
SEL selectorToOverride2 = @selector(application:didFailToRegisterForRemoteNotificationsWithError:);
// get the info on the method we're going to override
Method m1 = class_getInstanceMethod([jreporterNativePush class], selectorToOverride1);
Method m2 = class_getInstanceMethod([jreporterNativePush class], selectorToOverride2);
// add the method to the new class
class_addMethod(modDelegate, selectorToOverride1, (IMP)didRegisterForRemoteNotificationsWithDeviceToken, method_getTypeEncoding(m1));
class_addMethod(modDelegate, selectorToOverride2, (IMP)didFailToRegisterForRemoteNotificationsWithError, method_getTypeEncoding(m2));
// register the new class with the runtime
objc_registerClassPair(modDelegate);
}
// change the class of the object
object_setClass(delegate, modDelegate);
NSLog(@"completed crazy swap w/o bombing w00t");
///////// end of delegate injection / modification code
*numFunctionsToTest = 1;
FRENamedFunction* func = (FRENamedFunction*) malloc(sizeof(FRENamedFunction) * 1);
func[0].name = (const uint8_t*) "registerPush";
func[0].functionData = NULL;
func[0].function = ®isterPush;
*functionsToSet = func;
}
回答2:
the problem is, that you do not tell your application, which function to call, once it has registered with apns. in order to do that, you have to create and set a so-called delegate - read https://developer.apple.com/library/ios/#documentation/General/Concept ual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources. html#//apple_ref/doc/uid/TP40010810-CH11-SW1
you create the needed delegate by implementing the UIApplicationDelegate protocol. a protocol is quite similar to the concept of interfaces in java - read https://developer.apple.com/library/ios/#referencelibrary/GettingStart ed/Learning_Objective-C_A_Primer/_index.html
so you will basically create a SEPARATE class by specifying a header file and implementation and set an instance of this class as your uiapplicationdelegate. right now, you unnecessarily wrap a class around static code and add functions that have the same name as functions that should be called in the delegate, but you do not tell the application about your delegate. a good place to set your delegate would be the contextinitializer function of your nativeextension, where you call something like:
delegate = [[UINativePushAppDelegate alloc] init];
[[UIApplication sharedApplication] setDelegate:delegate]; // assuming there is a class, called uinativepushappdelegate
now the application will call the didRegisterForRemoteNotificationsWithDeviceToken method of this instance, once you register for apns.
来源:https://stackoverflow.com/questions/9204993/push-notification-ios-native-extension-for-adobe-air