Reputation: 4313
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:
Upvotes: 3
Views: 3668
Reputation: 31045
+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)
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
Upvotes: 7
Reputation: 23298
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.
Upvotes: 2