Skip to content

Bundling Resources

Introduction

Many platforms provide native ways to bundle application resources like images or other binary data. The way these are embedded into the build process and the specific capabilities of these mechanisms vary quite a bit though.

As an example let's compare the iOS and Androids resource system.

iOS

Example folder structure:

iOS resource folder structure

iOS .app bundles contain a Resources folder that is meant to host the applications binary data. Inside you can create subfolders with the extension .lproj that can host different versions of a file for different languages ( e.g. de.lrpoj or en-US.lproj). The special base.lproj is used the specify the default such that files not found in a specific language bundle will be looked up in it. The directory structure inside these can be defined by the user arbitrarily.

Android

Example folder structure:

Android resource folder structure

Android on the other hand follows a different approach. It defines a rigid structure per type of resource. An image, for instance, needs to be in a folder called res/drawables while a json file might be stored in res/raw. To specify the language ( and other selections like e.g. the dpi etc. ) you add this information to the folder name: res/drawables-hidpi-en-US.

Users cannot create subfolders inside these folders. Trying to do so may result in compile errors, and the files are simply not accessible.

Android has another mechanism for distributing files with the application called assets. The asset folders structure can be defined by users and it can house any type of data. However, a downside is that its content cannot be automatically localized.

Boden representation of resources

Boden abstracts all these intricacies into a platform agnostic json file:

{
    "name" : "main",
    "root-path" : "./Resources",
    "resources" : [
        {
            "language" : "",
            "android-qualifiers" : "",
            "images" : [ ],
            "raw" : [ ]
        },
        {
            "language" : "de",
            "android-qualifiers" : "",
            "images" : [ ],
            "raw" : [ ]
        }
    ],
    "assets" : [ ]
}
{
    "name" : "main",
    "root-path" : "./Resources",
    "resources" : [
        {
            "language" : "",
            "android-qualifiers" : "",
            "images" : [
                {
                    "bundle_path" : "images/image.png",
                    "resolutions" : {
                        "0.75x" : "",
                        "1.0x" : "image.png",
                        "1.5x" : "",
                        "2.0x" : "image@2x.png",
                        "3.0x" : "image@3x.png",
                        "4.0x" : ""
                    }
                }
            ],
            "raw" : [
                {
                    "bundle_path" : "images/animation.json",
                    "file": "flag.json"
                }
            ]
        },
        {
            "language" : "de",
            "android-qualifiers" : "",
            "images" : [
                {
                    "bundle_path" : "images/image.png",
                    "resolutions" : {
                        "0.75x" : "",
                        "1.0x" : "de/image.png",
                        "1.5x" : "",
                        "2.0x" : "de/image@2x.png",
                        "3.0x" : "de/image@3x.png",
                        "4.0x" : ""
                    }
                }
            ],
            "raw" : [
                {
                    "bundle_path" : "images/animation.json",
                    "file": "de/flag.json"
                }
            ]
        }
    ],
    "assets" : [
        {
            "bundle_path" : "images/asset_image.png",
            "file": "image.png"
        }
    ]
}

To include a resource.json file into the targets build process use the following code in your CMake file:

roger_add( "TARGET_NAME" OUTPUT "PATH_TO_JSON")
add_executable( "TARGET_NAME" main.cpp ${OUTPUT})
roger_add(
    navigationviewdemo
    NAVIGATIONVIEWDEMO_RESOURCES
    ${CMAKE_CURRENT_LIST_DIR}/../DemoAssets/demo_resources.json)
# ...
add_universal_executable(
    navigationviewdemo 
    TIDY SOURCES 
    ${NAVIGATIONVIEWDEMO_FILES} 
    ${NAVIGATIONVIEWDEMO_ASSETS} 
    ${NAVIGATIONVIEWDEMO_RESOURCES})

Using resources

To reference resource files inside your code use one of the following schemes:

Scheme Meaning
image:// An image specified in the "images" section
resource:// An arbitrary file specified in the "raw" section
asset:// An arbitrary file specified in the "assets" section

All Boden classes will understand how to interpret these paths. Borrowing the resource file from the previous sections json example:

using namespace bdn::ui;

auto image = std::make_shared<ImageView>();
image->url = "image://main/images/image.png";

To get a platform specific representation of the path use the Application::uriToBundledFileUri function:

// On Android this will return "file:///android_asset/test/index.html"
bdn::App()->uriToBundledFileUri("asset://main/test/index.html") 

Automatic flattening

As discussed previously, Android does not support subdirectories in its resource folders. Therefore the roger utility will flatten any directory structure by replacing all / with _. Additionally, when accessing resources, the extension is ignored by android. Therefore roger will append the extension twice, but replace the . with an _ as well:

Original  Flattened (Android)
images/image.png images_image_png.png