iOS App Package Size Optimization: Detecting Unused Code, Resources, and Binary Optimizations
This article presents practical methods for reducing iOS app bundle size by detecting and removing unused Objective‑C/Swift classes, redundant resources, and applying binary optimizations such as segment migration and link‑time optimization, illustrated with real‑world experience from 58.com.
Background
With the rapid evolution of mobile apps, package size has become a critical metric affecting download conversion rates. This article shares 58.com’s experience in shrinking app bundles by addressing unused OC/Swift classes, redundant resources, and applying binary optimizations such as segment migration and LTO.
1.1 Pain Points of Package Size Governance
Tools for detecting unused code and resources are fragmented and costly to use; open‑source tools often lack proper support for OC/Swift mixed binaries, and understanding Mach‑O structures adds further complexity.
1.2 Technical Characteristics
The tool works by dragging a built .app into the UI, then parsing Mach‑O sections (__TEXT, __DATA, symbol tables) to extract Swift type information, class names, and function call ranges. It also disassembles instructions to locate important calls such as Swift AccessFunction addresses.
2.1 Package Size Detection
The .app consists of the main binary, Assets.car , nib files, and other resources. Binary size is dominated by the main executable and dynamic libraries. Detection includes:
Unused OC/Swift classes
Segment migration
Link‑time optimization (LTO)
2.2 Unused Class Detection
2.2.1 OC Unused Class Detection
Commonly, a difference between the classlist (all classes) and classrefs (referenced classes) yields unused classes, but dynamic calls and +load methods must be added to classrefs by scanning __DATA,__cfstring , __objc_nlclslist , and __objc_nlcatlist sections.
2.2.2 Swift Unused Class Detection in Mixed Environments
Swift classes are represented by swift_class_t (inherits from objc_class ). The same classlist/classrefs diff works after extending classrefs with dynamically discovered Swift class references.
struct swift_class_t : objc_class {
uint32_t flags;
uint32_t instanceAddressOffset;
uint32_t instanceSize;
uint16_t instanceAlignMask;
uint16_t reserved;
uint32_t classSize;
uint32_t classAddressOffset;
void *description;
void *baseAddress();
}2.2.3 Swift Unused Class Detection Details
Swift classes expose an AccessFunction that must be called before any method. By collecting all AccessFunction addresses from __TEXT,__swift5_types and scanning disassembled bl instructions, the tool determines which Swift classes are actually used.
struct SwiftClassType {
uint32_t Flag;
uint32_t Parent;
int32_t Name;
int32_t AccessFunction;
int32_t FieldDescriptor;
int32_t SuperclassType;
uint32_t MetadataNegativeSizeInWords;
uint32_t MetadataPositiveSizeInWords;
uint32_t NumImmediateMembers;
uint32_t NumFields;
uint32_t FieldOffsetVectorOffset;
uint32_t Offset;
uint32_t NumMethods;
// VTableList …
}2.3 Unused Resource Detection
The tool extracts all strings from binaries (including Mach‑O and nib files) to match against resource filenames. Asset catalogs are unpacked with cartool to list contained files. Image resources are matched with possible aliases (e.g., cover , cover.png , [email protected] ). Bundle resources are also considered, and custom alias patterns can be supplied via a plist.
2.4 Other Optimizations
2.4.1 Segment Migration
Moving the __text segment out of __TEXT into a custom segment (e.g., __WB_TEXT ) reduces the portion encrypted by Apple, decreasing final IPA size. The following linker flags achieve this:
-Wl,-rename_section,__TEXT,__cstring,__RODATA,__cstring
-Wl,-rename_section,__TEXT,__objc_methname,__RODATA,__objc_methname
-Wl,-rename_section,__TEXT,__objc_classname,__RODATA,__objc_classname
-Wl,-rename_section,__TEXT,__objc_methtype,__RODATA,__objc_methtype
-Wl,-rename_section,__TEXT,__gcc_except_tab,__RODATA,__gcc_except_tab
-Wl,-rename_section,__TEXT,__const,__RODATA,__const
-Wl,-rename_section,__TEXT,__text,__WB_TEXT,__text
-Wl,-rename_section,__TEXT,__stubs,__WB_TEXT,__stubs
-Wl,-segprot,__WB_TEXT,rx,rx2.4.2 Link‑Time Optimization (LTO)
LTO performs whole‑program optimization at link time, merging bitcode modules and eliminating dead code. Two modes exist: Full LTO (single large module) and Thin LTO (parallel modules). Enabling LTO in Xcode (LLVM_LTO = YES) and configuring pods via post_install ensures the optimization is applied.
post_install do |installer|
installer.sandbox.target_support_files_root.glob('**/*.xcconfig').each do |xcconfig_file|
config = Xcodeproj::Config.new(xcconfig_file)
config.attributes['LLVM_LTO'] = 'YES'
config.other_linker_flags[:simple] << "-Wl,-mllvm,--enable-machine-outliner=always,-mllvm,--machine-outliner-reruns=1"
config.save_as(xcconfig_file)
end
endSummary of Evaluation Results
A table compares three solution schemes, showing reductions in time complexity, memory peak, and execution time—from 10¹⁶ operations and 6 GB memory (1.5 h) in Scheme 1 to 10⁹ operations and 1 GB memory (20 s) in Scheme 3.
Tool Usage
Users drag a compiled .app into the UI, click “Diagnose”, and receive detailed reports on unused classes, resource waste, segment migration status, and LTO configuration. Visual charts display binary size distribution, and interactive lists allow inspection of specific files.
Conclusion
Continuous bundle‑size monitoring is essential for user acquisition. The described techniques—unused class detection, resource analysis, segment migration, and LTO—have been applied across 58.com’s iOS projects, forming a standard practice for the company’s development teams.
For more details, see the open‑source project WBBlades .
58 Tech
Official tech channel of 58, a platform for tech innovation, sharing, and communication.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.