47

Does anyone know how you detect from within your code if you're running inside an App Extension?

I have an app which shares classes between an app and an extension. The app code uses [UIApplication sharedApplication] but this isn't available from within an extension, so it won't compile saying:

'sharedApplication' is unavailable: not available iOS (App Extension)

So I need a way to detect that I'm in the extension and use an alternative to sharedApplication if that's the case.

pkamb
  • 33,281
  • 23
  • 160
  • 191
Mark Bridges
  • 8,228
  • 4
  • 50
  • 65

7 Answers7

57

You can use a preprocessor macro:

In the project settings use the dropdown in the topbar to select your extension target: enter image description here

Then:

  1. Click Build Settings
  2. Find (or search) Preprocessor Macros under Apple LLVM 6.0 - Preprocessing
  3. Add TARGET_IS_EXTENSION or any other name of your choice in both the debug and release sections.

Then in your code:

#ifndef TARGET_IS_EXTENSION // if it's not defined
    // Do your calls to UIApplication
#endif
Andrew
  • 15,357
  • 6
  • 66
  • 101
  • Thanks, this approach works but it's actually the other way round I need to detect come to think of it. If not the Extension, use UIApplication. Anyway I solved it with a flag in the app targets build setting identifying it as the app as opposed to the extension. – Mark Bridges Aug 02 '14 at 10:29
  • @MarkBridges Sorry about that. I fixed that in my answer for future readers. – Andrew Aug 02 '14 at 13:58
  • 16
    This solution won't work for code in a framework. We currently check the name of the application bundle to see if the code is running in an extension but I would love to find a better way. – roustem Sep 19 '14 at 19:09
  • can you please describe your code i am also facing same problem what flag or code that i need to add then the app will pick based on app or extension. Remember i created shared framework those code should execute based on the bundle id. – loganathan Oct 08 '14 at 10:48
  • @loganathan [@siuying's answer](http://stackoverflow.com/a/26008395/2446155) for that. – Andrew Oct 08 '14 at 10:51
  • but if you are going with the programmatic approach will work but it will give your the compilation error right?. For ex:- how you will write the code to pick the uiapplication class in native app and not in extension app. Because for me it shows the compilation error when i do match with bundle id – loganathan Oct 08 '14 at 11:30
  • 1
    My code flow doesn't seem to get into the ifdef at all. Any idea why? – Shwethascar Apr 06 '16 at 21:38
46

As Apple's documentation says:

When you build an extension based on an Xcode template, you get an extension bundle that ends in .appex.

So, we can use the following code:

if ([[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"]) {
    // this is an app extension
}

// Swift version
if Bundle.main.bundlePath.hasSuffix(".appex") {
    // this is an app extension
}
some_id
  • 29,466
  • 62
  • 182
  • 304
Ruslan Skorb
  • 491
  • 4
  • 5
  • 1
    This should be the selected answer, it is far preferable to depending on a preprocessor macro. – MobileVet Dec 02 '17 at 21:36
  • Agreed, this is cleaner. – some_id Aug 07 '20 at 19:00
  • 4
    Not really cleaner, it's indirect and could change in the future if that extension changes, plus conditional compilation will allow you to exclude resources or methods that cannot compile in your extension. – Wil Gieseler Aug 31 '20 at 03:15
23

The preprocessor macro will work mostly, but will not work in shared library (e.g. cocoapods or shared frameworks).

Alternatively you can use following code.

@implementation ExtensionHelpers

+(BOOL) isAppExtension
{
    return [[[NSBundle mainBundle] executablePath] containsString:@".appex/"];
}

@end

This work by checking the bundle executablePath, as only App Extension has extension ".appex".

siuying
  • 1,449
  • 12
  • 15
  • 14
    I still get a compile error when compiling the shared library since it has the flag for "Require Only App-Extension-Safe API" to set to YES. How do you avoid compilation error while still using this code you wrote above? – cohen72 Feb 23 '15 at 13:59
  • 1
    I don't recommend using -[NSString containsString:] in production for two reasons: 1. It's not published API, i.e. official help page does not mentions it (which is likely a bug, but anyway); 2. It will crash on iOS7. Use -[NSString rangeOfString:] instead. – Alexander Vasenin Jun 08 '15 at 21:22
  • @AlexanderVasenin 2: It's documented in NSString.h and clearly marked as available on iOS 8+. – Maciej Swic Aug 31 '15 at 08:12
12

Swift 5

let bundleUrl: URL = Bundle.main.bundleURL
let bundlePathExtension: String = bundleUrl.pathExtension
let isAppex: Bool = bundlePathExtension == "appex"

// `true` when invoked inside the `Extension process`
// `false` when invoked inside the `Main process`
neoneye
  • 50,398
  • 25
  • 166
  • 151
9

The solution proposed by @Andrew did not work for me.

What worked was:

  1. Select extension target
  2. Build Settings
  3. Search "Custom Flags"
  4. Add a new Flag (example: "EXTENSION") to Debug and Release

enter image description here

Now the code:

#if EXTENSION
// IF is extension (SIRI) we do stuff
#else
// ELSE is main app do more stuff
#endif
Tiago Mendes
  • 4,572
  • 1
  • 29
  • 35
3

You can add a preprocessor macro on the extension target and then check with a #ifdef inside of your class.

Marcelo
  • 9,916
  • 3
  • 43
  • 52
0

For my shared library I created a separate target who's app extensions flag is set to yes, and used preprocessor macro's within the build settings for that specific target.

cohen72
  • 2,830
  • 29
  • 44