3

Following the explanation here:

https://github.com/nst/iOS-Runtime-Headers

I am trying to obtain the class of the TUPhoneLogger in the bundle TelephonyUtilities.framework. However, the debugger always show "error: unknown class".

I've 2 different methods:

First method:

NSBundle* b = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/TelephonyUtilities.framework"];
BOOL success = [b load];
NSLog(@"%@", [b definedClasses_dd]);

Note: I've created a @interface NSBundle (DDAdditions) extension:

- (NSArray *)definedClasses_dd {
NSMutableArray *array = [NSMutableArray array];    
int numberOfClasses = objc_getClassList(NULL, 0);    
Class *classes = calloc(sizeof(Class), numberOfClasses);
numberOfClasses = objc_getClassList(classes, numberOfClasses);
for (int i = 0; i < numberOfClasses; ++i) {
    Class c = classes[i];
    if ([NSBundle bundleForClass:c] == self) {
        [array addObject:c];
        const char* nameOfClass = class_getName(c);
        NSString* classString = [NSString stringWithUTF8String:nameOfClass];
        if([classString isEqualToString:@"TUPhoneLogger"]) {
            NSLog(@"Found it! TUPhoneLogger");
            id test= [[c alloc] init];
            NSLog(@"test: %@", test);
        }
    }
}
free(classes);

Second method:

NSBundle* b = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/TelephonyUtilities.framework"];
Class telephonyClass = [b classNamed:@"TUPhoneLogger"];
id test= [[telephonyClass alloc] init];
NSLog(@"%@", test);

In the debugger:

enter image description here

Victor Ronin
  • 22,758
  • 18
  • 92
  • 184
ikevin8me
  • 4,253
  • 5
  • 44
  • 84
  • 1
    you haven't called `[b load]` in your second method. – Simon Mar 13 '13 at 07:37
  • Also, just repeating my comment from below ... this framework won't load on a simulator, only on a real (iPhone?) device ... and not sure if an iPad/iPod Touch would have this framework either. – Nate Mar 16 '13 at 08:16

2 Answers2

7

+1 to Victor, as I think it's simpler to just include the Framework as a Build Phase library in your project. Private frameworks are found under the PrivateFrameworks SDK subdirectory, but otherwise, it works similarly as with Public frameworks (with differences described in Victor's answer).

I will just offer one other technique that works, if you do want dynamic loading:

#include <dlfcn.h>
#import <objc/runtime.h>

and then

void* handle = dlopen("/System/Library/PrivateFrameworks/TelephonyUtilities.framework/TelephonyUtilities", RTLD_NOW);
Class c = NSClassFromString(@"TUPhoneLogger");
id instance = [[c alloc] init];
dlclose(handle);

I suppose a benefit of dlopen() is that you don't have to remember to call load, which got you in your second example. A downside is that you should call dlclose() afterwards.

Note: the slight difference in path for dlopen() vs NSBundle bundleWithPath: (file vs. dir)

Note++: this code won't work in the simulator, as the simulator probably is missing that framework (no real phone functionality)


Update

In iOS 9.3, Apple removed Private Frameworks from the SDK. So, since then, it's actually typically the case that you'll need to use this dynamic technique, if the Framework is not one of the public iOS frameworks. See this answer for more

Community
  • 1
  • 1
Nate
  • 31,017
  • 13
  • 83
  • 207
  • Hi, I tried using your method and it is still the same unknown class error. I tried both of what you wrote and also removed the tailing "TelephonyUtilities" at the end of the path. (I also did included dlfcn.h) – ikevin8me Mar 16 '13 at 05:24
  • 1
    @ikevinjp, It **must have** the trailing "TelephonyUtilities" at the end of the path. That's why I made the **Note:** at the end of my post. `NSBundle` wants the `.framework` *folder*, but `dlopen()` requires the *file* inside the .framework folder. – Nate Mar 16 '13 at 07:52
  • 1
    If you run this code on a simulator, yes, it will fail, as the simulator is missing many, many things that a real iPhone has ... apparently including the TelephonyUtilities framework. However, if you're running on an iPad, or iPod Touch, maybe that framework's missing there, as well. – Nate Mar 16 '13 at 08:13
2

There is a third method (which I prefer)

a) You link to this framework statically (meaning, you add it to your target)

b) You define necessary class (TUPhoneLogger ) in .h class. You can get it by using class-dump(-z)

c) You include this .h file

d) You just use private class the same way as you use public class.

Small additional explanation

There is no "magic" about private frameworks and private API. The only different that they aren't documented and included in .h files.

Step b) and c) creates .h classes with them and as result they can be used exactly the same way as usual public API.

Victor Ronin
  • 22,758
  • 18
  • 92
  • 184