21

In Xcode, I can set a framework to "Optional" instead of "Required", which then means the framework is weak linked.

Does that mean the framework is only included in the bundle when it is imported somewhere?

I want to weak-link a few debugging frameworks which use private API, and I do not want them to appear in the App Store build.

rmaddy
  • 298,130
  • 40
  • 468
  • 517
openfrog
  • 37,829
  • 61
  • 213
  • 364

3 Answers3

19

Important note: This answer was written before iOS 8 was announced. While the technical details still apply to system frameworks, it is now possible to build your own, dynamically linked frameworks that ship within your app bundle. There are restrictions, e.g., only an app and its extensions can link to the same instance of an embedded framework, but the fact remains that custom, dynamically linked framework are possible since iOS 8. If you want to learn more, refer to this guide (Using an Embedded Framework to Share Code) and WWDC 2014 session 416, Building Modern Frameworks.

Original Answer: None of the (platform) frameworks is really "included in the bundle". Instead, your app has a reference ("link") to a framework once you add it to the "Link Binary with Library" build phase. The frameworks are pre-installed on the devices. When you run an app, all the app's framework references are resolved by the dynamic linker (on the device), which means the framework code is loaded so your app can use it.

Some frameworks may not be available on all the devices you intend to support, e.g., PassKit was introduced in iOS 6. If you run an app that links against PassKit on an iOS 5 device, it crashes right after launch, because the dynamic linker cannot find the framework on the device. However, if you weak-link PassKit, the dynamic linker will set all the framework's symbols to nil, if the framework could not be found. This prevents the app from crashing and you can check for the symbols' availability at runtime, e.g.:

if ([PKPass class]) {
  // Class is available - use it
  PKPass *pass = [[PKPass alloc] init];
}

[PKPass class] is safe to use on all devices/systems since the PKPass class symbol will be nil on older systems, and messaging nil is not a problem in Objective-C.

More on Weak-Linking: Apple Documentation

To really answer your question:

Does that mean the framework is only included in the bundle when it is imported somewhere?

No. The framework will always be linked from the app. Only when the framework is not found on the actual device your app is running on, then the framework will not be loaded.

One solution would be to have separate targets for Debug and App Store Builds. An alternative is to not use the built-in "Link Binary with Library" build phase from Xcode, but to link the Debug frameworks via linker options. These can be specified for each configuration (Debug/Release/...) separately, like so:

Adding framework via linker flags

If you'd want to weak-link it, use -weak_framework PassKit (PassKit, of course, being just an example here... insert the name of your framework) instead. If your Debug framework is not in one of the default framework directories, you might have to provide a full path or modify the Frameworks Search Path. Plus, you should probably use macros to make sure none of the code using the debug framework(s) makes it to the App Store build.

Edit: Another option since Xcode 5 is to use @import <FrameworkName>;. This way, you can leave your "Link Binary..." phase empty and trigger the linking of frameworks in code. You can then use macros such as DEBUG to make sure some frameworks aren't used for App Store builds. There's an excellent answer regarding @import.

Community
  • 1
  • 1
hagi
  • 10,474
  • 3
  • 31
  • 44
  • OP's question indicates that he uses a non-Apple framework. So the framework must be added to the bundle. – mkalmes Jun 05 '13 at 11:45
  • I'm not sure whether there are frameworks that you can, but must not dynamically link because they are private? If the framework is linked statically, weak-linking doesn't apply/make any sense after all. – hagi Jun 05 '13 at 11:54
  • Apple doesn't provide an official way to build a framework, but it isn't impossible. Google provides a framework for their [Google+ iOS SDK](https://developers.google.com/+/downloads/). OP's question title and description don't match very well. I assume he is more interested in omitting the debugging framework, than in "weak-linking". – mkalmes Jun 05 '13 at 12:12
  • I agree... the weak-linking keyword kind of led me in the wrong direction here, because I was assuming it's about dynamically linked frameworks. I'm aware of custom frameworks, but those, too, are statically linked. It's basically a static library plus resources. – hagi Jun 05 '13 at 12:26
  • This is an excellent answer, to a question that has been answered badly in many other places. I'd +10 you if I could. – HenryRootTwo Feb 19 '15 at 02:50
3

I encountered weak linking when I was using iAds. The problem was if I strong link iAds framework and run the app on a device with SDK which did not supported iAds then it will simply crash. Weak linking allowed to avoid the crashes. I still believe that even with weak linking you still have to check in code if the framework is available or not.

azamsharp
  • 18,391
  • 34
  • 139
  • 218
  • Ok, so weak-linking does still include the debugging framework that uses private API, and thus Apple would still reject the app. – openfrog Jun 04 '13 at 15:53
  • private apis are no no with apple. Chance of your app getting caught and rejected because of that are high. azamsharp answer is correct. I ran into this issue just a few days ago. – Sam B Jun 04 '13 at 16:04
2

Does that mean the framework is only included in the bundle when it is imported somewhere?

This depends on how you configured your schemes or targets.

You could use one scheme only for debugging and include your optional framework only there. Use another scheme without the optional framework for release.

Scheme Example

UPDATE

To do this, base your new scheme on a project configuration and set OTHER_LDFLAGS as described in hagi's answer.

Project Configurations and Schemes

Community
  • 1
  • 1
mkalmes
  • 134
  • 1
  • 10
  • A scheme, or do I need to create an entire new target to safely exclude the framework? – openfrog Jun 04 '13 at 16:46
  • And how can I achieve this with just a scheme? The linked frameworks section is bound to the target / project? – openfrog Jun 04 '13 at 16:46
  • You can base a scheme on a Project Configuration and use @hagi's explanation to set `OTHER_LDFLAGS` accordingly. – mkalmes Jun 05 '13 at 11:42