Swift. Packages and Bundles. Resources
Here is a quick introduction, along with the full articles list
Before reading the text below, take a look at this
The current work is a continuation of discussions about libraries, frameworks, and executables
After reading the article, you may question: where are frameworks located in a device before loading into physical memory by the dynamic loader?
The article said that app and library code is transformed into the executable binary file, and that file represents your app. But the binary itself is just a set of instructions for a processor. And what about resources like images, nibs, or localized strings? Where are they?
Frameworks have their own resources, and moreover, they (frameworks) are not embedded into the resulting binary because of dynamic linkage. So, we have to take a framework at runtime and load it into RAM. What is the place we should go for it?
The simple answer is: any app is a package (or a particular type of bundle) that stores frameworks, separate dynamic libraries provided by Xcode, the result executable binary, files with app metadata, resources, etc. And what you see on your iPhone isn’t a binary but a directory with everything needed inside
So, let’s take a look at bundles, packages, and resources a bit closer to have a full picture of how it works
A bundle can be a package, but not any package can be a bundle because a package can have any folder structure inside, and bundles themselves have standardized structures
Xcode automatically creates bundle structure for the following targets: application, framework, plug-in. Shell scripts, static libraries, and separate dynamic libraries (not a framework) don’t use a bundle structure
Due to a bundle is just a folder, you can create it manually. No magic here. Create a folder, add correct folder extension depends on the type, meet all requirements of the structure, and that is it!
Bundle structures of frameworks, applications, and plug-ins can vary depending on macOS and iOS accordingly. In this article, we mainly refer to iOS
Let’s take a look at the structure of the most popular bundle types:
The structures of different bundle types are pretty similar
The *.lproj folders store images, sounds, strings, etc., that are used for chosen app language. It’s easy to use localized resources just out of the box. All you need is to put your resources in the correct lproj folder
Info.plist has bundle configuration information, e.g. CFBundleIdentifier — is a unique bundle identifier
In iOS, bundle resources are located in the top-level folder (in macOS, bundles store resources in the Resources folder). You can also create your own folder and put there any resources or files you want. The only requirement — the folder shouldn’t be called “Resources” :)
Just a few words about an Assets.car problem. Your project can store some resources in the Assets.xcassets folder. It’s a pretty comfortable way to keep images of different sizes. After building, the application bundle will contain a file with the .car extension. That file is a compiled version of the Assets.xcassets folder. Your images and other resources from the Assets.xcassets folder will be stored there
Let’s say you have a module that has its own Assets.xcassets folder, as a result — its own Assets.car file. If you want to build your module as a static library, you can catch a conflict that will say: “I have the Assets.car file from your module and the same file from the application. I don’t know what to do…” Until resolving the conflict, you won’t be able to build your app
The possible solution is to create a custom bundle package (plug-in) for each module and store resources there (like in the picture above). So, your modules will store their Assets.car files inside such packages
Here are several facts about module resources:
- If you build your module as a static library, its code will be embedded into the result executable binary, and its resources (images, plug-ins, strings, .car files, …) will be placed in the application bundle, in the top-level folder
- If you build your module as a framework, its resources will be placed in its own .framework bundle folder (as a result, no conflicts with the Assets.car file; yep, it is the second possible solution of the Assets.car problem — using frameworks over static libraries)
Again, an application bundle is that guy you interact on your iPhone with
And finally, here are several examples of how to work with bundles in code: