pull notification locally on jailbroken device

ios, jailbreak, notifications

Solution

... 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 <dlfcn.h>
#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:

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>com.apple.springboard.launchapplications</key>
        <true/>
    </dict>
</plist>

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.

Problem

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. - Is there built in functionally on a jailbroken device to schedule code execution with no need for the user to interact? - The code should download updates and determine if the user should receive a notification. - I don't want to use push notifications, which requires an external server to push them to the user. Update Well, I've managed to create a daemon which launches on start-up and keeps itself running. However, posting notifications requires the `UIApplication` object. According to the documentation this singleton is created by the `UIApplicationMain()` method which, for a regular application is called by `main()`. Since I want the notification be posted by a daemon, the singleton is nil. Can I create an instance of `UIApplication`? Or post the notification any other way? I've tried calling `UIApplicationMain()` and then posting the notification in the app delegate, as well as killing the application, but this shows a black screen for a moment; I guess its launching the application. Moreover, it causes the daemon to crash when app launching is impossible (when the phone is yet to fully boot). Here is a sketch of the code ``` int main(){ if(launchedBySpringBoard || launchedBynotification) UIApplicationMain(...); else if(launchedByDaeamon) StartRunLoop(); } void triggerdByRunLoopEveryXhours(){ downloadData(); if(isNewData()) postNotification(); } ```

Original source

Related problems