14


I'm looking for a way to check if a framework exists and/or if it's classes are defined, before importing and using that framework. Specifically, the framework is Assets Library.

Currently, I'm able to do this with the Core Data framework, since that framework has a file called CoreDataDefines.h which provides a preprocessor directive _COREDATADEFINES_H. This allows me to simply check for that defintion like so:

#ifdef _COREDATADEFINES_H
#import <CoreData/CoreData.h>

// do something with Core Data

#else

// do something without using Core Data

#endif


Unfortunately, the Assets Library does not provide a clear definitions header file so I'm looking for a way to write my own #define statement that can check for the framework's existence before importing it, much like I have done for Core Data above.

I have tried this:

#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
// import assets library if defined !
#define ASSETSLIBRARY_DEFINE    (NSClassFromString(@"ALAsset") != nil)
#if ASSETSLIBRARY_DEFINE
#import <AssetsLibrary/AssetsLibrary.h>
#endif
#endif

... but no luck.
The compiler tells me that the "Token is not a valid binary operator in a preprocessor subexpression."


Any help is always much appreciated.

imnk
  • 4,342
  • 3
  • 29
  • 31
  • 1
    Have you considered using [CMake](http://cmake.org/)? It can check for existing Mac OS X Frameworks. I don't know how about finding iOS Frameworks, but as a last resort you can write your own cmake module for finding it. – Maciej Sep 07 '11 at 11:54
  • Something is very very wrong with this. – Mike Weller Sep 07 '11 at 12:08
  • Thanks for the info @Maciek but I think CMake is not what I'm looking for. Basically I just want a preprocessor directive that checks if a header file exists in the project bundle's frameworks. – imnk Sep 07 '11 at 15:22
  • 1
    Where you able to find a solution? – Thizzer Jan 11 '12 at 16:07

3 Answers3

4

If you know what class should be imported with the framework you can check if it is loaded:

BOOL isFrameworkLoaded = (NSClassFromString(@"MyClassNameFromTheFramework") != nil);
Davyd Geyl
  • 4,578
  • 1
  • 28
  • 35
1

I have a few tricks for this kind of thing… Though what follows may not EXACTLY solve the problems you detailed, this may well aide in finding your ultimate solution… The first "strategy" can be called upon in a "Script Build Phase". This can be used in many ways, but in this example it TESTS that the Framework is valid, (according to otool, and then does some "post-processing" accordingly.. and then tests it again.. and so on..

function test { "$@";   status=$?;
    if [ $status -ne 0 ]; then
       echo "error with $1"; fi 
    return $status
}
  PRODUCT_PATH="${BUILT_PRODUCTS_DIR}/${WRAPPER_NAME}"
        BINARY="$PRODUCT_PATH/${PRODUCT_NAME}"
           MAC="/Library/Frameworks/"
SYSTEM_PRODUCT="$MAC/${WRAPPER_NAME}"

test otool -l "$BINARY" #/Library/Frameworks/AtoZ.framework/AtoZ
if [ $status -ne 0 ]; then
   sudo rm -r "$SYSTEM_PRODUCT"
   sudo cp -r "$PRODUCT_PATH" "$SYSTEM_PRODUCT"
test otool -l "$BINARY" 
if [ $status == 0 ]; then
   cd "${PRODUCT_PATH}"
   ln -sF Versions/Current/Frameworks Frameworks
fi

The next hand-me-down in this wisdom free-for-all is the sexy, and aptly named NSBundle method.. preflightAndReturnError

NSBundle *b = [NSBundle bundleWithPath:path];
NSError *e  = nil;
BOOL okdok  = [b preflightAndReturnError:&e];
if  (okdok)  
     okdok  = [b load];
if (!okdok) { [self jumpOffABridge:nil]; goto hell; }

Chances are though… you'll be.. OK DOK, by the time load finishes up.

Alex Gray
  • 16,007
  • 9
  • 96
  • 118
1

What you're doing here is very wrong. The _COREDATADEFINES_H define you see in CoreDataDefines.h is known as the sentinal value, an old C technique to avoid multiple inclusions of the same header file.

You should certainly not be using this in your own code, and the presence of the sentinal only tells you that the header has already been included somewhere else. If the sentinal is not defined, it simply means the header has not been included, not that the framework itself is nonexistant.

I'm not sure exactly what you are trying to do, but it looks like you want to use macros to decide between code that uses the framework, and code that doesn't use the framework. If you want to do this at compile time, your only choice is to define your own macros and set them up in your target with some compiler options. For example, to enable code that uses the assets library you might define this in the "Other C Flags" build setting:

-DUSE_ASSETS_FRAMEWORK

And then use this in your code:

#ifdef USE_ASSETS_FRAMEWORK
    #import <AssetsLibrary/AssetsLibrary.h>
    // code that uses assets framework
#else
    // code that does not use assets framework
#endif

If you want to be able to detect at runtime whether the app is linked against the framework and that the framework exists on the current iOS version, you should use the standard approach that Apple recommends which is to test the existance of any classes or functions you need:

if (NSClassFromString(@"ALAsset")) {
    // ALAsset is available }
} else {
    // ALAsset not available
}
Mike Weller
  • 45,401
  • 15
  • 131
  • 151
  • 2
    Thanks for the info Mike. I'm trying to create a cross-project framework that can automatically leave out code when a certain framework has not been imported. This is what I mean by 'not existing' ... i.e. not existing in the current project where my framework is being used. So, I'd rather not have to set macros in each project if possible. That's what I'm trying to avoid. The runtime suggestion I have considered also, but it produces compile time errors and stops me from running my code when the Assets Library framework has not been detected. – imnk Sep 07 '11 at 15:17