Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/claration/Impactor/llms.txt

Use this file to discover all available pages before exploring further.

The Bundle struct provides utilities for parsing, inspecting, and modifying iOS application bundles (.app directories).

Bundle struct

pub struct Bundle {
    bundle_dir: PathBuf,
    bundle_type: BundleType,
    info_plist_path: PathBuf,
}
Represents an iOS app bundle with its Info.plist and associated resources.

Creating a bundle

pub fn new<P: Into<PathBuf>>(bundle_path: P) -> Result<Self, Error>
Creates a Bundle instance from a path. The path must contain a valid Info.plist file.
use plume_utils::Bundle;
use std::path::PathBuf;

let bundle = Bundle::new("/path/to/MyApp.app")?;
let bundle = Bundle::new(PathBuf::from("/Applications/App.app"))?;

Bundle types

pub enum BundleType {
    App,
    AppExtension,
    Framework,
    Dylib,
    Unknown,
}
Determined from the file extension:
  • .appBundleType::App
  • .appexBundleType::AppExtension
  • .frameworkBundleType::Framework
  • .dylibBundleType::Dylib
should_have_entitlements
fn
pub fn should_have_entitlements(&self) -> bool
Returns true for App and AppExtension types
should_be_signed
fn
pub fn should_be_signed(&self) -> bool
Returns true for all types except Unknown

Accessors

bundle_dir
fn
pub fn bundle_dir(&self) -> &PathBuf
Returns the bundle’s directory path
bundle_type
fn
pub fn bundle_type(&self) -> &BundleType
Returns the bundle type

Discovering nested bundles

collect_nested_bundles
fn
pub fn collect_nested_bundles(&self) -> Result<Vec<Bundle>, Error>
Recursively finds all nested bundles (app extensions, frameworks, dylibs)
collect_bundles_sorted
fn
pub fn collect_bundles_sorted(&self) -> Result<Vec<Bundle>, Error>
Returns all bundles sorted by depth (deepest first). Used for signing in the correct order.
let bundle = Bundle::new("/path/to/App.app")?;
let all_bundles = bundle.collect_bundles_sorted()?;

for b in all_bundles {
    println!("Bundle: {:?} - {:?}", b.bundle_dir(), b.bundle_type());
}

Info.plist methods

These methods read and modify the bundle’s Info.plist file.

Reading values

Implements PlistInfoTrait:
get_name
fn
pub fn get_name(&self) -> Option<String>
Returns CFBundleDisplayName or CFBundleName
get_executable
fn
pub fn get_executable(&self) -> Option<String>
Returns CFBundleExecutable
get_bundle_identifier
fn
pub fn get_bundle_identifier(&self) -> Option<String>
Returns CFBundleIdentifier
get_bundle_name
fn
pub fn get_bundle_name(&self) -> Option<String>
Returns CFBundleName
get_version
fn
pub fn get_version(&self) -> Option<String>
Returns CFBundleShortVersionString
get_build_version
fn
pub fn get_build_version(&self) -> Option<String>
Returns CFBundleVersion

Writing values

set_info_plist_key
fn
pub fn set_info_plist_key<V: Into<Value>>(
    &self,
    key: &str,
    value: V
) -> Result<(), Error>
Sets an arbitrary key in Info.plist
set_name
fn
pub fn set_name(&self, new_name: &str) -> Result<(), Error>
Sets both CFBundleDisplayName and CFBundleName
set_version
fn
pub fn set_version(&self, new_version: &str) -> Result<(), Error>
Sets both CFBundleShortVersionString and CFBundleVersion
set_bundle_identifier
fn
pub fn set_bundle_identifier(&self, new_identifier: &str) -> Result<(), Error>
Sets CFBundleIdentifier
set_matching_identifier
fn
pub fn set_matching_identifier(
    &self,
    old_identifier: &str,
    new_identifier: &str,
) -> Result<(), Error>
Replaces all occurrences of old_identifier with new_identifier in:
  • CFBundleIdentifier
  • WKCompanionAppBundleIdentifier
  • NSExtension → NSExtensionAttributes → WKAppBundleIdentifier

Example: Reading bundle info

use plume_utils::{Bundle, PlistInfoTrait};

let bundle = Bundle::new("/path/to/App.app")?;

if let Some(name) = bundle.get_name() {
    println!("App name: {}", name);
}

if let Some(id) = bundle.get_bundle_identifier() {
    println!("Bundle ID: {}", id);
}

if let Some(version) = bundle.get_version() {
    println!("Version: {}", version);
}

if let Some(executable) = bundle.get_executable() {
    println!("Executable: {}", executable);
}

Example: Modifying bundle

use plume_utils::Bundle;

let bundle = Bundle::new("/path/to/App.app")?;

// Change app name
bundle.set_name("My Custom App")?;

// Change version
bundle.set_version("2.0.0")?;

// Change bundle identifier
bundle.set_bundle_identifier("com.example.newid")?;

// Set custom key
bundle.set_info_plist_key("UIFileSharingEnabled", true)?;
bundle.set_info_plist_key("MinimumOSVersion", "14.0")?;

Example: Updating identifiers across extensions

let bundle = Bundle::new("/path/to/App.app")?;
let old_id = bundle.get_bundle_identifier().unwrap();
let new_id = format!("{}.customteam", old_id);

// Update main app and all nested bundles
let all_bundles = bundle.collect_bundles_sorted()?;
for nested_bundle in all_bundles {
    nested_bundle.set_matching_identifier(&old_id, &new_id)?;
}

Example: Processing all bundles

use plume_utils::{Bundle, BundleType};

let bundle = Bundle::new("/path/to/App.app")?;
let bundles = bundle.collect_bundles_sorted()?;

for b in &bundles {
    match b.bundle_type() {
        BundleType::App => {
            println!("Main app: {:?}", b.bundle_dir());
        }
        BundleType::AppExtension => {
            println!("Extension: {:?}", b.bundle_dir());
        }
        BundleType::Framework => {
            println!("Framework: {:?}", b.bundle_dir());
        }
        BundleType::Dylib => {
            println!("Dylib: {:?}", b.bundle_dir());
        }
        BundleType::Unknown => {}
    }
}

Advanced: Working with nested structures

let bundle = Bundle::new("/path/to/App.app")?;
let bundles = bundle.collect_nested_bundles()?;

// Find watch extension
let watch_ext = bundles.iter().find(|b| {
    b.bundle_dir()
        .file_name()
        .and_then(|n| n.to_str())
        .map(|s| s.contains("Watch"))
        .unwrap_or(false)
});

if let Some(watch) = watch_ext {
    println!("Found watch extension: {:?}", watch.bundle_dir());
}

Build docs developers (and LLMs) love