pull notification locally on jailbroken device

前端 未结 3 1318
-上瘾入骨i
-上瘾入骨i 2020-12-05 20:29

Since the iOS framework doesn\'t allow local notifications to execute code before they are posted, I\'m looking for a way to achieve it on a jailbroken device.

3条回答
  •  孤街浪徒
    2020-12-05 21:12

    ... Or post the notification any other way?

    Yes. You can make this work with a background (launch) daemon that triggers a notification (not necessarily a UILocalNotification). When the notification shows the user an alert, your daemon can then decide to open a normal UI application (or not).

    Build a Launch Daemon.

    This is the best tutorial I've found. The launch daemon starts when the phone boots, and runs all the time as a non-graphical background process. From there, you can schedule your check for updates. (I have a HelloDaemon class which does all its work in the run: method):

    int main(int argc, char *argv[]) {
        @autoreleasepool {
            HelloDaemon* daemon = [[HelloDaemon alloc] init];
            
            // start a timer so that the process does not exit.
            NSTimer* timer = [[NSTimer alloc] initWithFireDate: [NSDate date]
                                                      interval: 1.0
                                                        target: daemon
                                                      selector: @selector(run:)
                                                      userInfo: nil
                                                       repeats: NO];
            
            NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
            [runLoop addTimer: timer forMode: NSDefaultRunLoopMode];
            [runLoop run];
        }    
        return 0;
    }
    

    Daemons can use NSTimer normally, so schedule another timer (within run:) to check for updates to download whenever you want.

    Notify User from Daemon

    If the daemon decides that the user should be notified, then you can either:

    1) open the full UI application.

    #include 
    #define SBSERVPATH "/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices"
    
    -(void) openApp {
      
        // the SpringboardServices.framework private framework can launch apps,
        //  so we open it dynamically and find SBSLaunchApplicationWithIdentifier()
        void* sbServices = dlopen(SBSERVPATH, RTLD_LAZY);
        int (*SBSLaunchApplicationWithIdentifier)(CFStringRef identifier, Boolean suspended) = dlsym(sbServices, "SBSLaunchApplicationWithIdentifier");
        int result = SBSLaunchApplicationWithIdentifier(CFSTR("com.mycompany.AppName"), false);
        dlclose(sbServices);
    }
    

    This code requires the com.apple.springboard.launchapplications entitlement for your daemon to use it successfully. See here for adding an entitlement. You'd need an entitlements.xml file for your daemon executable, like this:

    
    
        
            com.apple.springboard.launchapplications
            
        
    
    

    2) show a simple alert window from your daemon, notifying the user of the event, and prompting them to open the UI app

    #include "CFUserNotification.h"
    
    -(void) showAlert {
      
        NSMutableDictionary* dict = [NSMutableDictionary dictionary];
        [dict setObject: @"Alert!" forKey: (__bridge NSString*)kCFUserNotificationAlertHeaderKey];
        [dict setObject: @"Updates Ready!" forKey: (__bridge NSString*)kCFUserNotificationAlertMessageKey];
        [dict setObject: @"View" forKey:(__bridge NSString*)kCFUserNotificationDefaultButtonTitleKey];
        [dict setObject: @"Cancel" forKey:(__bridge NSString*)kCFUserNotificationAlternateButtonTitleKey];
        
        SInt32 error = 0;
        CFUserNotificationRef alert =
        CFUserNotificationCreate(NULL, 0, kCFUserNotificationPlainAlertLevel, &error, (__bridge CFDictionaryRef)dict);
        
        CFOptionFlags response;
        // we block, waiting for a response, for up to 10 seconds
        if((error) || (CFUserNotificationReceiveResponse(alert, 10, &response))) {
            NSLog(@"alert error or no user response after 10 seconds");
        } else if((response & 0x3) == kCFUserNotificationAlternateResponse) {
            // user clicked on Cancel ... just do nothing
            NSLog(@"cancel");
        } else if((response & 0x3) == kCFUserNotificationDefaultResponse) {
            // user clicked on View ... so, open the UI App
            NSLog(@"view");
            [self openApp];
        }
        CFRelease(alert);
    }
    

    You'll need a CFUserNotification.h header to use the code the way I did above. You can find one by googling, or see one here. This older wiki document also shows some good information for using CFUserNotification from iOS apps.

    The answer I linked to from KennyTM above also shows how you can make your alert popup show, even if the device is locked.

提交回复
热议问题