Mobile Development 26 min read

Exploring iOS Componentization

This article introduces iOS componentization using CocoaPods, detailing the drawbacks of manual code copying, explaining the creation of local podspec libraries, configuring source files, resources, access levels, and integration steps, while also covering sub‑specs, resource bundles, and practical code examples for Swift projects.

Sohu Tech Products
Sohu Tech Products
Sohu Tech Products
Exploring iOS Componentization

Exploring iOS Componentization

Componentization of iOS projects has become a mature practice in the industry, yet many teams still copy source files manually between projects, leading to duplicated code, difficult maintenance, and poor debugging experience. This article presents a practical workflow for modularizing an iOS app using CocoaPods.

1. Background

Manual copying of FeatureModuleA and its dependency FeatureModuleB into new projects creates multiple independent copies. Updating the module requires editing every copy, which is error‑prone and hampers unified management.

2. Basic Principle

The chosen approach relies on cocoapods to create a local pod library that encapsulates the component. The component is stored in its own Git repository and integrated into consuming apps via a Podfile entry.

2.1 Creating a Component Library

Two common ways to generate the initial pod structure:

pod lib create – scaffolds a full template for a new component. lanfudong@MacBook-Pro ~ % pod lib create QRCodeReader (The command asks a few questions and creates a folder containing QRCodeReader.podspec , an Example project, and template source files.)

pod spec create – creates only an empty .podspec file for an existing module. lanfudong@MacBook-Pro ~ % pod spec create QRCodeReader

2.2 Editing the podspec

A typical .podspec looks like the following (key fields are highlighted):

Pod::Spec.new do |spec|
    spec.name          = "QRCodeReader"
    spec.version       = "0.1.0"
    spec.summary       = "A short description of QRCodeReader."
    spec.description   = <<-DESC
        TODO: Add long description of the pod here.
    DESC
    spec.license       = { :type => "MIT", :file => "LICENSE" }
    spec.author        = { "lanfudong" => "[email protected]" }
    spec.source        = { :git => "https://github.com/lanfudong/QRCodeReader.git", :tag => spec.version.to_s }
    spec.ios.deployment_target = "10.0"
    spec.source_files  = "QRCodeReader/Classes/**/*"
    spec.resource_bundle = { "QRCodeReader" => "Resources/**/*.{xcassets,json,plist}" }
    spec.xcconfig      = { 'DEFINES_MODULE' => 'YES' }
    spec.frameworks    = "AudioToolbox", "AVFoundation"
end

The spec defines the module name, version, summary, license, author, source location, deployment target, source files, resource bundle, build settings, and required frameworks.

3. Adjusting Access Levels

Swift defaults to internal visibility, which restricts symbols to the same module. After modularization, public APIs must be marked public or open so that consuming apps can import the component.

Access Levels open – highest level, usable and subclassable outside the module. public – usable outside the module but not subclassable. internal – default, visible only inside the module. fileprivate – visible within the same source file. private – visible only within the declaring scope.

4. Integrating the Component

Add the component to the app’s Podfile :

pod 'QRCodeReader', :path => '../QRCodeReader'

Run pod install . Xcode will show a Development Pods group containing the component.

5. Managing Resource Files

Resources (images, JSON, plist, etc.) should be placed in a Resources folder inside the component and referenced via resource_bundle to avoid name collisions.

Loading an image from the component’s bundle requires specifying the bundle explicitly:

// App main bundle root path
let mainPath = Bundle.main.resourcePath
// QRCodeReader.bundle relative path
let pathComponent = "/Frameworks/QRCodeReader.framework/QRCodeReader.bundle"
let bundle = Bundle(path: mainPath + pathComponent)
let image = UIImage(named: imageName, in: bundle, compatibleWith: nil)

A helper that works for both dynamic (framework) and static library integrations:

public func image(named: String, in bundleName: String) -> UIImage? {
    if let img = _dynamicImage(named: named, in: bundleName) { return img }
    if let img = _staticImage(named: named, in: bundleName) { return img }
    return UIImage(named: named) // fallback
}

private func _dynamicImage(named: String, in bundleName: String) -> UIImage? {
    let pathComponent = "/Frameworks/\(bundleName).framework/\(bundleName).bundle"
    return _image(named: named, with: pathComponent)
}

private func _staticImage(named: String, in bundleName: String) -> UIImage? {
    let pathComponent = "/\(bundleName).bundle"
    return _image(named: named, with: pathComponent)
}

private func _image(named: String, with pathComponent: String) -> UIImage? {
    guard let mainPath = Bundle.main.resourcePath else { return nil }
    let bundle = Bundle(path: mainPath + pathComponent)
    return UIImage(named: named, in: bundle, compatibleWith: nil)
}

6. Sub‑components (subspecs)

When a library contains several small modules, they can be expressed as subspec s inside the same podspec. Example:

spec.subspec 'QRCodeReader' do |reader|
    reader.source_files = 'QRCodeReader/**/*.{swift,m,c,h}'
end

spec.subspec 'ImageViewer' do |viewer|
    viewer.source_files = 'ImageViewer/**/*.{swift,m,c,h}'
end

spec.subspec 'ImagePicker' do |picker|
    picker.source_files = 'ImagePicker/**/*.{swift,m,c,h}'
end

Consumers can integrate the whole library or select individual subspecs:

# Integrate all subspecs
pod 'FocusUtility', :path => '../FocusUtility'

# Integrate only QRCodeReader
pod 'FocusUtility/QRCodeReader', :path => '../FocusUtility'

7. Conclusion

The guide demonstrates a minimal yet functional iOS componentization workflow: decoupling code, configuring a local CocoaPods library, handling access levels, managing resources, and optionally defining subspecs. While the example is simple, real‑world projects may require more sophisticated dependency management, versioning, and automation.

iOSModularizationCocoaPodsSwiftcomponentizationPodSpec
Sohu Tech Products
Written by

Sohu Tech Products

A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.