Placing content in a bundle
Place bundle content in the correct location based on its type.
Overview
A bundle is a directory with a standardized hierarchical structure that typically contains executable code and the resources used by that code. Bundles fulfill many different roles: apps, app extensions, frameworks, and plug-ins are all bundles. Bundles can also contain other bundles; for example, an app may contain an app extension.
A bundle has a set of standard locations to hold content. The correct location to place content within a bundle depends on the content type. For example, you must place an app extension in the location reserved for plug-ins and a storyboard in the location reserved for resources. Not all locations are appropriate on all platforms and, similarly, not all platforms support all content types.
Xcode understands the bundle structure and, if you use Xcode to build your bundle, it places content correctly based on its type. If you don’t use Xcode to build your software product, use the information below to place your bundled content in the right location. Even if you use Xcode, you might find this information useful as you check the final structure of your product.
Place content based on type and platform
Bundled content includes the bundle’s Info.plist, code content and resources:
The
Info.plistis a property list file stored at a location that identifies a directory hierarchy as a bundle. For a list ofInfo.plistkeys, see Information Property List.Code content is either executable code, like a helper tool, or another bundle that contains executable code, like an app extension. In this context, executable code means a Mach-O image. It doesn’t include things like shell scripts, Python scripts, and AppleScripts (unless you save the AppleScript as an application). Although you execute a script, it has no place to hold a code signature, so you treat it as a resource.
Resources are everything that’s not code.
When adding content to a bundle, place it according to the rules in the following table:
If the location ends with a slash (
/), place the item within that directory. Otherwise, place a single item of that type at that location.A location of
/indicates the root of the bundle.The macOS platform covers both Mac and Mac Catalyst apps.
Content type | Platform | Location |
|---|---|---|
| macOS |
|
macOS framework |
| |
iOS, watchOS, tvOS, visionOS |
| |
main executable | macOS |
|
macOS framework |
| |
iOS, watchOS, tvOS, visionOS |
| |
resource | macOS |
|
macOS framework |
| |
iOS, watchOS, tvOS, visionOS |
| |
privacy manifest | macOS |
|
macOS framework |
| |
iOS, watchOS, tvOS, visionOS |
| |
framework, dynamic library | macOS |
|
macOS framework |
| |
iOS, tvOS, visionOS |
| |
watchOS | See Placing Content In A Bundle section below. | |
app extension | macOS |
|
iOS, watchOS, tvOS |
| |
plug-in | macOS |
|
macOS framework |
| |
iOS, watchOS, tvOS, visionOS |
| |
provisioning profile | macOS |
|
iOS, watchOS, tvOS, visionOS |
| |
help app, helper tool | macOS |
|
| ||
macOS framework |
| |
XPC Service | macOS |
|
Automator action | macOS |
|
QuickLook generator | macOS |
|
privileged helper tool | macOS |
|
Service Management login item | macOS |
|
Spotlight importer | macOS |
|
system extension | macOS |
|
App Clip | iOS |
|
watchOS app | iOS |
|
iOS, watchOS, and tvOS support third-party frameworks but don’t support third-party standalone dynamic libraries, which are those outside a framework bundle, typically with the .dylib filename extension. The only exception to this rule is the Swift system libraries provided by Xcode.
iOS and tvOS support frameworks and Swift system libraries at the topmost app level; a nested bundle, like an app extension, can’t include a framework.
For more information about embedding frameworks and Swift system libraries on watchOS, see Handle frameworks and Swift system libraries on watchOS.
For information on localizing resources, see Localized Resources in Bundles.
Handle frameworks and Swift system libraries on watchOS
A watchOS app consists of an iOS app that contains a watchOS app, that then contains a WatchKit extension. This nesting is present even for watch-only apps. When embedding frameworks and Swift system libraries in a watchOS app, you need to:
Place iOS frameworks and Swift system libraries in the iOS app’s
Frameworksdirectory. For example, for an iOS app called MyApp, place iOS frameworks and Swift system libraries inMyApp.app/Frameworks/.Place the watchOS Swift system libraries in the WatchKit app’s
Frameworksdirectory, for example,MyApp.app/Watch/MyApp WatchKit App.app/Frameworks/.Place watchOS frameworks in the WatchKit extension’s
Frameworksdirectory, for example,MyApp.app/Watch/MyApp WatchKit App.app/PlugIns/MyApp WatchKit Extension.appex/Frameworks/.
Support a single framework version on macOS
A macOS framework is a bundle that uses a unique format. The framework’s root contains a Versions directory that holds one or more versions of the framework, each of which has its own bundle-like structure. On ancestor platforms to macOS, multiple versions of a framework can coexist within this versioned bundle. On macOS, however, best practice is to use a single version named A.
If you build your macOS framework in Xcode, it generates the correct structure automatically. If you’re working outside of Xcode, follow these rules to structure your framework for maximum compatibility:
Create a single
Versionsdirectory at the framework’s root.Within that, create a directory name
A.Populate
Versions/Awith your framework content.Create a symlink within
VersionscalledCurrentthat targetsA.Create a symlink in the framework’s root that target’s the framework’s executable through
Versions/Current. For example, if you’re creating the CoreWaffleVarnishing framework, create a symlink calledCoreWaffleVarnishingthat targets the framework’s executable atVersions/Current/CoreWaffleVarnishing.Create a symlink in the framework’s root that target’s the framework’s
Resourcesdirectory throughVersions/Current.Optionally, create other symlinks in the root that target similarly named items through
Versions/Current.
The final structure looks like this:
CoreWaffleVarnishing.framework/
CoreWaffleVarnishing -> Versions/Current/CoreWaffleVarnishing
Resources -> Versions/Current/Resources
Versions/
Current -> A
A/
CoreWaffleVarnishing
Resources/
Info.plist
… other resources …Place code content directly in its location
Each code location must contain a flat list of code content. If you have a lot of code content, you might be tempted to group it in nested directories, for example:
WaffleVarnisher.app/
Contents/
…
PlugIns/
Waffles/
Belgian.plugin
Buttermilk.plugin
BananaCaramel.plugin
…
Varnishes/
Gloss.plugin
Satin.plugin
Matte.plugin
…
Don’t group your nested code in this way. Although it might work in some situations, it might fail later in hard-to-debug ways.
If you ignore this recommendation and use this structure, don’t use a dot (.) in your directory names, such as Waffles and Varnishes in the example above. The code-signing machinery assumes that any directory with a name that contains a dot is a bundle, and then fails when signing that directory because it’s not a well-formed bundle.
Codeless bundles
A codeless bundle has no executable code. For example, some apps use a codeless bundle as part of their plug-in support. A codeless bundle can hold a code signature, so you can sign it either as code or a resource, depending on the circumstances:
If you distribute a codeless bundle independently, sign it as code.
If you embed a codeless bundle within another bundle, sign it as code if the embedding location typically holds code, for example,
Contents/PlugIns. Otherwise, sign the codeless bundle as a resource.