I am trying to use the SystemConfiguration on mac OS to get a notification when a new network interface appears on the mac and a new IP address is assigned for it.
I set it up to watch for the system configuration key State:/Network/Interface and it works that I get a notification whenever a new network interface appears or disappears.
However I would like to get a notification whenever the IPv4 address is assigned on the new network interface (e.g. by DHCP). I know that the key State:/Network/Interface/en0/IPv4 is holding the IPv4 address for the en0 interface. But using regular expressions as depicted in the man page for all IPv4 addresses State:/Network/Interface/.*/IPv4 does not work for the new interface.
I have put together a small minimal code example on github, however one can also use the scutil command line tool.
Link to demo repository
main.c
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
/* Callback used if a configuration change on monitored keys was detected.
*/
void dynamicStoreCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void* __nullable info) {
CFIndex count = CFArrayGetCount(changedKeys);
for (CFIndex i=0; i<count; i++) {
NSLog(#"Key \"%#\" was changed", CFArrayGetValueAtIndex(changedKeys, i));
}
}
int main(int argc, const char * argv[]) {
NSArray *SCMonitoringInterfaceKeys = #[#"State:/Network/Interface.*"];
#autoreleasepool {
SCDynamicStoreRef dsr = SCDynamicStoreCreate(NULL, CFSTR("network_interface_detector"), &dynamicStoreCallback, NULL);
SCDynamicStoreSetNotificationKeys(dsr, CFBridgingRetain(SCMonitoringInterfaceKeys), NULL);
CFRunLoopAddSource(CFRunLoopGetCurrent(), SCDynamicStoreCreateRunLoopSource(NULL, dsr, 0), kCFRunLoopDefaultMode);
NSLog(#"Starting RunLoop...");
while([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
}
return 0;
}
With the help of some developer colleagues I found out what went wrong. The signature for the SCDynamicStoreSetNotificationKeys function is as follows:
Boolean SCDynamicStoreSetNotificationKeys (SCDynamicStoreRef store,
CFArrayRef __nullable keys,
CFArrayRef __nullable patterns
)
Meaning that I have to set the pattern separately from the keys which act as the root of the tree under which the pattern matching will occur. Here is the modified version of my main.m:
int main(int argc, const char * argv[]) {
NSArray *SCMonitoringInterfaceKeys = #[#"State:/Network/Interface"];
NSArray *patterns = #[#"en\\d*/IPv4"];
#autoreleasepool {
SCDynamicStoreRef dsr = SCDynamicStoreCreate(NULL, CFSTR("network_interface_detector"), &dynamicStoreCallback, NULL);
SCDynamicStoreSetNotificationKeys(dsr, CFBridgingRetain(SCMonitoringInterfaceKeys), CFBridgingRetain(patterns));
CFRunLoopAddSource(CFRunLoopGetCurrent(), SCDynamicStoreCreateRunLoopSource(NULL, dsr, 0), kCFRunLoopDefaultMode);
NSLog(#"Starting RunLoop...");
while([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
}
return 0;
}
I have included the solution into the solved branch of the repo.
I wrote an Helper Tool for an application based on http://dl.dropbox.com/u/463624/Elevator.zip, but setting the deployment target to 10.10 now I can see deprecation in lauch.h, this is the code:
#import <Foundation/Foundation.h>
#import <launch.h>
#import <syslog.h>
#import "HelperTool.h"
int main (int argc, const char * argv[])
{
#autoreleasepool {
syslog(LOG_NOTICE, "MyHelper launched (uid: %d, euid: %d, pid: %d)", getuid(), geteuid(), getpid());
launch_data_t req = launch_data_new_string(LAUNCH_KEY_CHECKIN);
launch_data_t resp = launch_msg(req);
launch_data_t machData = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_MACHSERVICES);
launch_data_t machPortData = launch_data_dict_lookup(machData, "com.me.MyHelper.mach");
mach_port_t mp = launch_data_get_machport(machPortData);
launch_data_free(req);
launch_data_free(resp);
NSMachPort *rp = [[NSMachPort alloc] initWithMachPort:mp];
NSConnection *c = [NSConnection connectionWithReceivePort:rp sendPort:nil];
//.....
}
return 0;
}
looking here: https://developer.apple.com/library/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/headers/System.html no big changes, but how to solve?
I have installed last all-in-one bundle GTK+ for windows 32 bit.
I have a problem with function gtk_label_set_text: it leaks memory when it is called recursively
There is an example code below. It leaks memory about 1Mb every 20 seconds
#include <gtk/gtk.h>
gboolean update_label(gpointer);
int main(int argc, char ** argv)
{
GtkWidget *window;
GtkWidget *label = NULL;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
label = gtk_label_new(NULL);
gtk_container_add(GTK_CONTAINER(window),label);
gtk_widget_show_all(window);
g_timeout_add(10,(GtkFunction)update_label,label);
gtk_main();
return 0;
}
gboolean update_label(gpointer data)
{
GtkWidget *label = data;
gchar tmpbuf[100];
sprintf(tmpbuf , "Random text %i\n",rand());
gtk_label_set_text(GTK_LABEL(label),tmpbuf);
return TRUE;
}
The code creates a windows with label and updates it every 10 ms.
Can someone help me? Is there something wrong in GTK+ library or in my code?
Thanks
This is most probably a duplicate Memory leak in GTK under Windows 7 in gtk_widget_queue_draw. What is the version of GTK you use ?
Here is an extremely simple CoreMIDI OS X application that sends MIDI data. The problem is that it doesn't work. It compiles fine, and runs. It reports no errors, and does not crash. The Source created becomes visible in MIDI Monitor. However, no MIDI data comes out.
Could somebody let me know what I'm doing wrong here?
#include <CoreMIDI/CoreMIDI.h>
int main(int argc, char *args[])
{
MIDIClientRef theMidiClient;
MIDIEndpointRef midiOut;
MIDIPortRef outPort;
char pktBuffer[1024];
MIDIPacketList* pktList = (MIDIPacketList*) pktBuffer;
MIDIPacket *pkt;
Byte midiDataToSend[] = {0x91, 0x3c, 0x40};
int i;
MIDIClientCreate(CFSTR("Magical MIDI"), NULL, NULL,
&theMidiClient);
MIDISourceCreate(theMidiClient, CFSTR("Magical MIDI Source"),
&midiOut);
MIDIOutputPortCreate(theMidiClient, CFSTR("Magical MIDI Out Port"),
&outPort);
pkt = MIDIPacketListInit(pktList);
pkt = MIDIPacketListAdd(pktList, 1024, pkt, 0, 3, midiDataToSend);
for (i = 0; i < 100; i++) {
if (pkt == NULL || MIDISend(outPort, midiOut, pktList)) {
printf("failed to send the midi.\n");
} else {
printf("sent!\n");
}
sleep(1);
}
return 0;
}
You're calling MIDISourceCreate to create a virtual MIDI source.
This means that your source will appear in other apps' MIDI setup UI, and that those apps can choose whether or not to listen to your source. Your MIDI will not get sent to any physical MIDI ports, unless some other app happens to channel it there. It also means that your app has no choice as to where the MIDI it's sending goes. I'm assuming that's what you want.
The documentation for MIDISourceCreate says:
After creating a virtual source, use MIDIReceived to transmit MIDI messages from your virtual source to any clients connected to the virtual source.
So, do two things:
Remove the code that creates the output port. You don't need it.
change MIDISend(outPort, midiOut, pktList) to: MIDIReceived(midiOut, pktlist).
That should solve your problem.
So what are output ports good for? If you wanted to direct your MIDI data to a specific destination -- maybe a physical MIDI port -- you would NOT create a virtual MIDI source. Instead:
Call MIDIOutputPortCreate() to make an output port
Use MIDIGetNumberOfDestinations() and MIDIGetDestination() to get the list of destinations and find the one you're interested in.
To send MIDI to one destination, call MIDISend(outputPort, destination, packetList).
I'm just leaving this here for my own reference. It's a full example based 100% on yours, but including the other side (receiving), my bad C code and the accepted answer's corrections (of course).
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
#define NSLogError(c,str) do{if (c) NSLog(#"Error (%#): %u:%#", str, (unsigned int)c,[NSError errorWithDomain:NSMachErrorDomain code:c userInfo:nil]); }while(false)
static void spit(Byte* values, int length, BOOL useHex) {
NSMutableString *thing = [#"" mutableCopy];
for (int i=0; i<length; i++) {
if (useHex)
[thing appendFormat:#"0x%X ", values[i]];
else
[thing appendFormat:#"%d ", values[i]];
}
NSLog(#"Length=%d %#", length, thing);
}
- (void) startSending {
MIDIEndpointRef midiOut;
char pktBuffer[1024];
MIDIPacketList* pktList = (MIDIPacketList*) pktBuffer;
MIDIPacket *pkt;
Byte midiDataToSend[] = {0x91, 0x3c, 0x40};
int i;
MIDISourceCreate(theMidiClient, CFSTR("Magical MIDI Source"),
&midiOut);
pkt = MIDIPacketListInit(pktList);
pkt = MIDIPacketListAdd(pktList, 1024, pkt, 0, 3, midiDataToSend);
for (i = 0; i < 100; i++) {
if (pkt == NULL || MIDIReceived(midiOut, pktList)) {
printf("failed to send the midi.\n");
} else {
printf("sent!\n");
}
sleep(1);
}
}
void ReadProc(const MIDIPacketList *packetList, void *readProcRefCon, void *srcConnRefCon)
{
const MIDIPacket *packet = &packetList->packet[0];
for (int i = 0; i < packetList->numPackets; i++)
{
NSData *data = [NSData dataWithBytes:packet->data length:packet->length];
spit((Byte*)data.bytes, data.length, YES);
packet = MIDIPacketNext(packet);
}
}
- (void) setupReceiver {
OSStatus s;
MIDIEndpointRef virtualInTemp;
NSString *inName = [NSString stringWithFormat:#"Magical MIDI Destination"];
s = MIDIDestinationCreate(theMidiClient, (__bridge CFStringRef)inName, ReadProc, (__bridge void *)self, &virtualInTemp);
NSLogError(s, #"Create virtual MIDI in");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
MIDIClientCreate(CFSTR("Magical MIDI"), NULL, NULL,
&theMidiClient);
[self setupReceiver];
[self startSending];
}
#end
A little detail that others are skipping: the time parameter of MIDIPacketListAdd is important for some musical apps.
Here is an example of how you can retrieve it:
#import <mach/mach_time.h>
MIDITimeStamp midiTime = mach_absolute_time();
Source: Apple Documentation
And then, applied to the other examples here:
pktBuffer[1024];
MIDIPacketList *pktList = (MIDIPacketList*)pktBuffer;
MIDIPacket *pktPtr = MIDIPacketListInit(pktList);
MIDITimeStamp midiTime = mach_absolute_time();
Byte midiDataToSend[] = {0x91, 0x3c, 0x40};
pktPtr = MIDIPacketListAdd(pktList, sizeof(pktList), pktPtr, midiTime, sizeof(midiDataToSend), midiDataToSend);
Consider your own midi client creating application may crash or the host sending midi can crash also. You can handle this easier with checking if an client/destination exists already then doing this by handling singleton allocations. When your Midi client is existing but not working then this is because you need to tell CoreMidi what your costume made client is capable of processing and what latency it will have specially when the host sending client is using timestamps a lot (aka ableton and other).
in your .h file
#import <CoreMIDI/CoreMIDI.h>
#import <CoreAudio/HostTime.h>
#interface YourVirtualMidiHandlerObject : NSObject
#property (assign, nonatomic) MIDIClientRef midi_client;
#property (nonatomic) MIDIEndpointRef outSrc;
#property (nonatomic) MIDIEndpointRef inSrc;
- (id)initWithVirtualSourceName:(NSString *)clientName;
#end
in your .m file
#interface YourVirtualMidiHandlerObject () {
MIDITimeStamp midiTime;
MIDIPacketList pktList;
}
#end
You would prepare initiation of your virtual client in the following way
also in your .m file
#implementation YourVirtualMidiHandlerObject
// this you can call in dealloc or manually
// else where when you stop working with your virtual client
-(void)teardown {
MIDIEndpointDispose(_inSrc);
MIDIEndpointDispose(_outSrc);
MIDIClientDispose(_midi_client);
}
- (id)initWithVirtualSourceName:(NSString *)clientName {
if (self = [super init]) {
OSStatus status = MIDIClientCreate((__bridge CFStringRef)clientName, (MIDINotifyProc)MidiNotifyProc, (__bridge void *)(self), &_midi_client);
BOOL isSourceLoaded = NO;
BOOL isDestinationLoaded = NO;
ItemCount sourceCount = MIDIGetNumberOfSources();
for (ItemCount i = 0; i < sourceCount; ++i) {
_outSrc = MIDIGetSource(i);
if ( _outSrc != 0 ) {
if ([[self getMidiDisplayName:_outSrc] isEqualToString:clientName] && !isSourceLoaded) {
isSourceLoaded = YES;
break; //stop looping thru sources if it is existing
}
}
}
ItemCount destinationCount = MIDIGetNumberOfDestinations();
for (ItemCount i = 0; i < destinationCount; ++i) {
_inSrc = MIDIGetDestination(i);
if (_inSrc != 0) {
if ([[self getMidiDisplayName:_inSrc] isEqualToString:clientName] && !isDestinationLoaded) {
isDestinationLoaded = YES;
break; //stop looping thru destinations if it is existing
}
}
}
if(!isSourceLoaded) {
//your costume source needs to tell CoreMidi what it is handling
MIDISourceCreate(_midi_client, (__bridge CFStringRef)clientName, &_outSrc);
MIDIObjectSetIntegerProperty(_outSrc, kMIDIPropertyMaxTransmitChannels, 16);
MIDIObjectSetIntegerProperty(_outSrc, kMIDIPropertyTransmitsProgramChanges, 1);
MIDIObjectSetIntegerProperty(_outSrc, kMIDIPropertyTransmitsNotes, 1);
// MIDIObjectSetIntegerProperty(_outSrc, kMIDIPropertyTransmitsClock, 1);
isSourceLoaded = YES;
}
if(!isDestinationLoaded) {
//your costume destination needs to tell CoreMidi what it is handling
MIDIDestinationCreate(_midi_client, (__bridge CFStringRef)clientName, midiRead, (__bridge void *)(self), &_inSrc);
MIDIObjectSetIntegerProperty(_inSrc, kMIDIPropertyAdvanceScheduleTimeMuSec, 1); // consider more 14ms in some cases
MIDIObjectSetIntegerProperty(_inSrc, kMIDIPropertyReceivesClock, 1);
MIDIObjectSetIntegerProperty(_inSrc, kMIDIPropertyReceivesNotes, 1);
MIDIObjectSetIntegerProperty(_inSrc, kMIDIPropertyReceivesProgramChanges, 1);
MIDIObjectSetIntegerProperty(_inSrc, kMIDIPropertyMaxReceiveChannels, 16);
// MIDIObjectSetIntegerProperty(_inSrc, kMIDIPropertyReceivesMTC, 1);
// MIDIObjectSetIntegerProperty(_inSrc, kMIDIPropertyReceivesBankSelectMSB, 1);
// MIDIObjectSetIntegerProperty(_inSrc, kMIDIPropertyReceivesBankSelectLSB, 1);
// MIDIObjectSetIntegerProperty(_inSrc, kMIDIPropertySupportsMMC, 1);
isDestinationLoaded = YES;
}
if (!isDestinationLoaded || !isSourceLoaded) {
if (status != noErr ) {
NSLog(#"Failed creation of virtual Midi client \"%#\", so disposing the client!",clientName);
MIDIClientDispose(_midi_client);
}
}
}
return self;
}
// Returns the display name of a given MIDIObjectRef as an NSString
-(NSString *)getMidiDisplayName:(MIDIObjectRef)obj {
CFStringRef name = nil;
if (noErr != MIDIObjectGetStringProperty(obj, kMIDIPropertyDisplayName, &name)) return nil;
return (__bridge NSString *)name;
}
For those of you trying to read tempo (midi transport) and set the propertys for the virtual destination in your creation process...
Don't forget timestamps are send with the packets but a packet can contain several commands of same type, even several clock commands. When constructing a clock counter to find bpm tempo you will have to consider counting at least 12 of them before calculating. When you go only with 3 of them you are actually measuring your own buffer read processing latency instead of the real timestamps.
Your reading procedure (callback) will handle timestamps if the midi sender fails to set those properly with...
void midiRead(const MIDIPacketList * pktlist, void * readProcRefCon, void * srcConnRefCon) {
const MIDIPacket *pkt = pktlist->packet;
for ( int index = 0; index < pktlist->numPackets; index++, pkt = MIDIPacketNext(pkt) ) {
MIDITimeStamp timestamp = pkt->timeStamp;
if ( !timestamp ) timestamp = mach_absolute_time();
if ( pkt->length == 0 ) continue;
const Byte *p = &pkt->data[0];
Byte functionalDataGroup = *p & 0xF0;
// Analyse the buffered bytes in functional groups is faster
// like 0xF will tell it is clock/transport midi stuff
// go in detail after this will shorten the processing
// and it is easier to read in code
switch (functionalDataGroup) {
case 0xF : {
// in here read the exact Clock command
// 0xF8 = clock
}
break;
case ... : {
// do other nice grouped stuff here, like reading notes
}
break;
default : break;
}
}
}
dont forget the client needs a callback where internal notifications are handled.
void MidiNotifyProc(const MIDINotification* message, void* refCon) {
// when creation of virtual client fails we despose the whole client
// meaning unless you need it you can ignore added/removed notifications
if (message->messageID != kMIDIMsgObjectAdded &&
message->messageID != kMIDIMsgObjectRemoved) return;
// reactions to other midi notications you gonna trigger here..
}
then you can send midi with...
-(void)sendMIDICC:(uint8_t)cc Value:(uint8_t)v ChZeroToFifteen:(uint8_t)ch {
MIDIPacket *packet = MIDIPacketListInit(&pktList);
midiTime = packet->timeStamp;
unsigned char ctrl[3] = { 0xB0 + ch, cc, v };
while (1) {
packet = MIDIPacketListAdd(&pktList, sizeof(pktList), packet, midiTime, sizeof(ctrl), ctrl);
if (packet != NULL) break;
// create an extra packet to fill when it failed before
packet = MIDIPacketListInit(&pktList);
}
// OSStatus check = // you dont need it if you don't check failing
MIDIReceived(_outSrc, &pktList);
}
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
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;
}
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.