amadour
amadour

Reputation: 1670

How to programmatically send an action in an iOS extension, since `sharedApplication` is unavailable?

I'm implementing an iOS action extension, using code shared with the main app. Some of it uses [[UIApplication sharedApplication] sendAction:to:from:event] to send actions to the first responder. Since sharedApplication is unavailable in an extension, this code won't compile.

I've tred one workaround, which is instanciating a UIControl, and sending an action from it using -[UIControl sendAction:to:forEvent:]. This works but has a big drawback (in addition to feeling very hacky): it's not possible to control the sender (this will be the UIControl instance), which in my case is necessary.

Another workaround I'm considering is retrieving the UIApplication object wrapping the extension by observing UIApplication notifications and getting the object property from them (I've confirmed that some of these notifications are still sent in the extension.) But even if this did work would it have any chance to be approved by Apple, since it would just be cheating the sharedApplication limitation? Has anybody experienced with this technique in a live app?

Thanks!

Upvotes: 2

Views: 1127

Answers (1)

Brian Nickel
Brian Nickel

Reputation: 27550

It looks like there isn't a direct way to do this. Looking at a disassembled UIKit, there are surprisingly few methods that call -[UIApplication sendAction:to:from:forEvent:], and all of those explicitly provide a sender.

The good news is that -[UIControl sendAction:to:forEvent:] is just a thin wrapper around -[UIApplication sendAction:to:from:forEvent:]:

void -[UIControl sendAction:to:forEvent:](void * self, void * _cmd, void * arg2, void * arg3, void * arg4) {
    rbx = [arg3 retain];
    [*_UIApp sendAction:arg2 toTarget:rbx fromSender:self forEvent:arg4];
    rdi = rbx;
    [rdi release];
    return;
}

If you're willing to risk that Apple might change the fundamental behavior of control interactions to completely do away with UIApplication in extensions, it would probably be relatively safe to call sendAction:to:from:forEvent: on a found application instance. The way I would retrieve it is view.window.nextResponder, since this is always a UIApplication if the window is not nil:

[(id)view.window.nextResponder sendAction:@selector(foo:) to:nil from:nil event:nil]

Apple shouldn't reject this since you aren't using disallowed API's and you aren't doing anything UIControl isn't already doing.

If you don't want to take the risk, what you're doing is probably the best approach, creating a dummy control (if needed) and using it to send the action.

Upvotes: 2

Related Questions