0

Typically in iOS you can load constants at runtime using CFBundleGetDataPointerForName. However, I'm finding that CFBundleGetDataPointerForName always returns NULL pointers for constants defined in iOS-8 style frameworks, like the ones discussed in WWDC 2014 Session 416: Building Modern Frameworks.

To replicate, I'm just creating a framework with the Xcode framework template, adding a build script to make a universal binary, and dragging the created framework into another project has the framework project. Then I try to load a constant from it like so:

void * versionNumberConstant = CFBundleGetDataPointerForName(CFBundleGetMainBundle(), CFSTR("TestFramework2VersionNumber"));
NSLog(@"Version number was %@",versionNumberConstant ? @"PRESENT" : @"NULL"); // Prints NULL

For replication purposes, this repo has the framework project, and this repo tries to load a constant from that framework using CFBundleGetDataPointerForName. I can also replicate this issue with frameworks other developers have made, such as AdMob's SDK.

Just in case its an issue with the pointer not being in CFBundleGetMainBundle(), I've also looped through every CFBundleRef and can't load the constant from any of them:

CFArrayRef array = CFBundleGetAllBundles();
for (int i = 0; i < CFArrayGetCount(array); i++) {
    CFBundleRef bundleRef = (CFBundleRef)CFArrayGetValueAtIndex(array, i);
    void * versionPointer = CFBundleGetDataPointerForName(bundleRef, CFSTR("TestFramework2VersionNumber"));
    NSLog(@"Version pointer is %@",versionPointer ? @"PRESENT" : @"NULL");
}

Does anyone know why this is, or if there's some build setting I can change to fix it?

Version info:

  • Xcode: Version 6.2 6C131e
  • iOS: 8.2 (12D508)
  • Device: iPhone 6 / iPhone 6 Simulator
Community
  • 1
  • 1
MaxGabriel
  • 7,617
  • 4
  • 35
  • 82

1 Answers1

0

Ok, the issue was that I needed to load the specific bundle that that framework was using, like so:

NSURL *testFramework2URL = [[[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:@"Frameworks" isDirectory:YES] URLByAppendingPathComponent:@"TestFramework2.framework"];

CFBundleRef bundleRef = CFBundleCreate(NULL, (__bridge CFURLRef)testFramework2URL);
NSLog(@"Bundle Ref = %@",bundleRef);

double * manualVersion = CFBundleGetDataPointerForName(bundleRef, CFSTR("TestFramework2VersionNumber"));
NSLog(@"Manual version = %@",manualVersion ? @"present" : @"NULL"); // Manual version = present

double version = *manualVersion;
NSLog(@"Version = %g",version); // Version = 1

I can also achieve this using CFBundleGetAllBundles(), despite what I said in the question (I must have made a mistake when originally using the CFBundleGetAllBundles() function or something.

MaxGabriel
  • 7,617
  • 4
  • 35
  • 82