Are Private Frameworks Supported on iOS

What are Private FrameWorks and how will we use them?

Private frameworks are frameworks which you are not allowed to use. They are not expected to be used outside of Apple, hence "Private". They are often lower-level libraries which may "break" the system if not used correctly. (But the frameworks have to exist because Apple's apps and public frameworks do use the private frameworks.)

Since the private frameworks are not for public use, Apple doesn't need to give any headers or documentations away.

If you're writing for AppStore, you should not use private frameworks (unless you're from Apple). The system will immediately reject your app even before the review process begins.


(On the other hand, for jailbroken platforms you're free to use any frameworks. Even so, the APIs of private frameworks is unstable, that you should avoid them if possible.

There is an ongoing effort to document these private frameworks and API in http://iphonedevwiki.net/index.php/Main_Page.)

Handling private frameworks in Xcode ≥ 7.3

You can solve this problem by linking to the private framework dynamically, instead of the more common way of linking at build time. At build time, the BluetoothManager.framework would need to exist on your development Mac for the linker to be able to use it. With dynamic linking, you defer the process until runtime. On the device, iOS 9.3 still has that framework present (and the other ones, too, of course).

Here is how you can modify your project on Github:

1) In Xcode's Project Navigator, under the Frameworks, remove the reference to BluetoothManager.framework. It was probably showing in red (not found) anyway.

2) Under the project Build Settings, you have the old private framework directory explicitly listed as a framework search path. Remove that. Search for "PrivateFrameworks" in the build settings if you have trouble finding it.

3) Make sure to add the actual headers you need, so the compiler understands these private classes. I believe you can get current headers here for example. Even if the frameworks are removed from the Mac SDKs, I believe this person has used a tool like Runtime Browser on the device to generate the header files. In your case, add BluetoothManager.h and BluetoothDevice.h headers to the Xcode project.

3a) Note: the generated headers sometimes don't compile. I had to comment out a couple struct typedefs in the above Runtime Browser headers in order to get the project to build. Hattip @Alan_s below.

4) Change your imports from:

#import <BluetoothManager/BluetoothManager.h>

to

#import "BluetoothManager.h"

5) Where you use the private class, you're going to need to first open up the framework dynamically. To do this, use (in MDBluetoothManager.m):

#import <dlfcn.h>

static void *libHandle;

// A CONVENIENCE FUNCTION FOR INSTANTIATING THIS CLASS DYNAMICALLY
+ (BluetoothManager*) bluetoothManagerSharedInstance {
Class bm = NSClassFromString(@"BluetoothManager");
return [bm sharedInstance];
}

+ (MDBluetoothManager*)sharedInstance
{
static MDBluetoothManager* bluetoothManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// ADDED CODE BELOW
libHandle = dlopen("/System/Library/PrivateFrameworks/BluetoothManager.framework/BluetoothManager", RTLD_NOW);
BluetoothManager* bm = [MDBluetoothManager bluetoothManagerSharedInstance];
// ADDED CODE ABOVE
bluetoothManager = [[MDBluetoothManager alloc] init];
});
return bluetoothManager;
}

I placed the call to dlopen in your singleton method, but you could put it elsewhere. It just needs to be called before any code uses the private API classes.

I added a convenience method [MDBluetoothManager bluetoothManagerSharedInstance] because you'll be calling that repeatedly. I'm sure you could find alternate implementations, of course. The important detail is that this new method dynamically instantiates the private class using NSClassFromString().

6) Everywhere you were directly calling [BluetoothManager sharedInstance], replace it with the new [MDBluetoothManager bluetoothManagerSharedInstance] call.

I tested this with Xcode 7.3 / iOS 9.3 SDK and your project runs fine on my iPhone.

Update

Since there seems to be some confusion, this same technique (and exact code) still works in iOS 10.0-11.1 (as of this writing).

Also, another option to force loading of a framework is to use [NSBundle bundleWithPath:] instead of dlopen(). Notice the slight difference in paths, though:

handle = dlopen("/System/Library/PrivateFrameworks/BluetoothManager.framework/BluetoothManager", RTLD_NOW);
NSBundle *bt = [NSBundle bundleWithPath: @"/System/Library/PrivateFrameworks/BluetoothManager.framework"];

Are private frameworks supported on iOS?

It appears that custom frameworks are now supported by Xcode 6:

iOS developers can now create dynamic frameworks. Frameworks are a
collection of code and resources to encapsulate functionality that is
valuable across multiple projects. Frameworks work perfectly with
extensions, sharing logic that can be used by both the main
application, and the bundled extensions.

Xcode 7.3 missing Private Frameworks

According to the Xcode 7.3 release notes:

The Apple private frameworks have been removed from the iOS, watchOS, and tvOS SDKs. If your application fails to link, make sure that you are not using any private frameworks. The use of private frameworks is an unsupported configuration and applications that use non-public APIs will be rejected by the App Store - see App Store Guideline 2.5. (22330301)

You have two options:

  1. Remove your dependency on these private frameworks.
  2. Copy the private frameworks from a previous version of Xcode and link with them. There is no guarantee they will continue to work on devices running iOS 9.3 and above.

How can I know if I'm using private frameworks?

You can use appscanner https://github.com/ChimpStudios/App-Scanner

From developer description

App Scanner is a preflight submission check list for iOS developers.
It searches code for private API usage. It is not perfect.

Other ways are listed here How does Apple know you are using private API?

iOS Private Frameworks

What BJ Homer said is correct: private frameworks are for Apple's use only, so there's no reason why they would ship the headers.

That said, if you want to walk on the wild side, and you don't want to submit your app to the app store, you can try using class-dump to create headers based on the library file in the framework. This won't generate the real headers that Apple uses - you just get the method signatures. You don't get any constants or C functions, and you certainly don't get any informative comments. Any private frameworks you do use will probably change in the next release of iOS, breaking your app.

Is ICU a private framework on iOS?

All frameworks not described in Apple reference and invisible in header files can be considered as 'Private' (hence not usable in an AppStore application).

All framework/method references present in headers but not in Apple reference documentation are considered 'Undocumented', and should not be used in AppStore apps (but sometimes, it does the trick...)

Link Private Apple Framework with CocoaPods

Quite some time passed, but since I was looking for the same thing and ended up here there might be other hits, too.
So, the answer is adding the following to your spec file:

  s.xcconfig     =  { "FRAMEWORK_SEARCH_PATHS" => "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks" }


Related Topics



Leave a reply



Submit