| name | cocoapods-subspecs-organization |
| description | Use when organizing complex CocoaPods libraries into subspecs. Covers modular architecture, dependency management between subspecs, and default subspecs patterns for better code organization and optional features. |
| allowed-tools | Read, Write, Edit, Bash, Grep, Glob |
CocoaPods - Subspecs Organization
Organize complex libraries into modular subspecs for better maintainability and optional features.
What Are Subspecs?
Subspecs allow you to split a pod into logical modules that can be installed independently or as a group.
Benefits
- Modularity: Separate core functionality from optional features
- Selective Installation: Users install only what they need
- Reduced Dependencies: Optional features don't force unnecessary dependencies
- Better Organization: Clear separation of concerns
Basic Subspec Pattern
Pod::Spec.new do |spec|
spec.name = 'MyLibrary'
spec.version = '1.0.0'
# Main spec has no source files - all in subspecs
spec.default_subspecs = 'Core'
# Core subspec - installed by default
spec.subspec 'Core' do |core|
core.source_files = 'Source/Core/**/*.swift'
core.frameworks = 'Foundation'
end
# Optional feature subspec
spec.subspec 'Networking' do |networking|
networking.source_files = 'Source/Networking/**/*.swift'
networking.dependency 'MyLibrary/Core' # Depends on Core
networking.dependency 'Alamofire', '~> 5.0'
end
# Another optional feature
spec.subspec 'UI' do |ui|
ui.source_files = 'Source/UI/**/*.swift'
ui.dependency 'MyLibrary/Core'
ui.ios.frameworks = 'UIKit'
ui.osx.frameworks = 'AppKit'
end
end
Dependency Patterns
Subspec Dependencies
Pod::Spec.new do |spec|
spec.name = 'MySDK'
# Foundation layer
spec.subspec 'Core' do |core|
core.source_files = 'Source/Core/**/*.swift'
end
# Networking depends on Core
spec.subspec 'Networking' do |net|
net.source_files = 'Source/Networking/**/*.swift'
net.dependency 'MySDK/Core'
net.dependency 'Alamofire', '~> 5.0'
end
# Analytics depends on Core and Networking
spec.subspec 'Analytics' do |analytics|
analytics.source_files = 'Source/Analytics/**/*.swift'
analytics.dependency 'MySDK/Core'
analytics.dependency 'MySDK/Networking'
end
end
External Dependencies Per Subspec
spec.subspec 'SQLite' do |sqlite|
sqlite.source_files = 'Source/SQLite/**/*.swift'
sqlite.dependency 'MyLibrary/Core'
sqlite.dependency 'SQLite.swift', '~> 0.14'
sqlite.libraries = 'sqlite3'
end
spec.subspec 'Realm' do |realm|
realm.source_files = 'Source/Realm/**/*.swift'
realm.dependency 'MyLibrary/Core'
realm.dependency 'RealmSwift', '~> 10.0'
end
Default Subspecs
Single Default Subspec
Pod::Spec.new do |spec|
spec.name = 'MyLibrary'
# When users do: pod 'MyLibrary'
# Only Core is installed
spec.default_subspecs = 'Core'
spec.subspec 'Core' do |core|
core.source_files = 'Source/Core/**/*.swift'
end
spec.subspec 'Extensions' do |ext|
ext.source_files = 'Source/Extensions/**/*.swift'
ext.dependency 'MyLibrary/Core'
end
end
Multiple Default Subspecs
Pod::Spec.new do |spec|
spec.name = 'MySDK'
# When users do: pod 'MySDK'
# Both Core and Networking are installed
spec.default_subspecs = 'Core', 'Networking'
spec.subspec 'Core' do |core|
core.source_files = 'Source/Core/**/*.swift'
end
spec.subspec 'Networking' do |net|
net.source_files = 'Source/Networking/**/*.swift'
net.dependency 'MySDK/Core'
end
spec.subspec 'Analytics' do |analytics|
analytics.source_files = 'Source/Analytics/**/*.swift'
analytics.dependency 'MySDK/Core'
# Optional - not installed by default
end
end
Platform-Specific Subspecs
Pod::Spec.new do |spec|
spec.name = 'CrossPlatformLib'
# Shared core
spec.subspec 'Core' do |core|
core.source_files = 'Source/Core/**/*.swift'
core.frameworks = 'Foundation'
end
# iOS-only subspec
spec.subspec 'iOS' do |ios|
ios.source_files = 'Source/iOS/**/*.swift'
ios.dependency 'CrossPlatformLib/Core'
ios.ios.deployment_target = '13.0'
ios.ios.frameworks = 'UIKit'
end
# macOS-only subspec
spec.subspec 'macOS' do |macos|
macos.source_files = 'Source/macOS/**/*.swift'
macos.dependency 'CrossPlatformLib/Core'
macos.osx.deployment_target = '10.15'
macos.osx.frameworks = 'AppKit'
end
end
Resource Bundles in Subspecs
spec.subspec 'UI' do |ui|
ui.source_files = 'Source/UI/**/*.swift'
# Each subspec can have its own resource bundle
ui.resource_bundles = {
'MyLibrary_UI' => [
'Resources/UI/**/*.{png,jpg,xcassets}',
'Resources/UI/**/*.{storyboard,xib}'
]
}
ui.dependency 'MyLibrary/Core'
end
spec.subspec 'Themes' do |themes|
themes.source_files = 'Source/Themes/**/*.swift'
themes.resource_bundles = {
'MyLibrary_Themes' => ['Resources/Themes/**/*']
}
themes.dependency 'MyLibrary/UI'
end
Common Subspec Patterns
Core + Optional Features
Pod::Spec.new do |spec|
spec.name = 'MyFramework'
spec.default_subspecs = 'Core'
# Required core functionality
spec.subspec 'Core' do |core|
core.source_files = 'Source/Core/**/*.swift'
end
# Optional: JSON serialization
spec.subspec 'JSON' do |json|
json.source_files = 'Source/JSON/**/*.swift'
json.dependency 'MyFramework/Core'
json.dependency 'SwiftyJSON', '~> 5.0'
end
# Optional: XML support
spec.subspec 'XML' do |xml|
xml.source_files = 'Source/XML/**/*.swift'
xml.dependency 'MyFramework/Core'
end
# Optional: Networking
spec.subspec 'Networking' do |net|
net.source_files = 'Source/Networking/**/*.swift'
net.dependency 'MyFramework/Core'
net.dependency 'Alamofire', '~> 5.0'
end
end
Layered Architecture
Pod::Spec.new do |spec|
spec.name = 'MySDK'
# Layer 1: Foundation
spec.subspec 'Foundation' do |foundation|
foundation.source_files = 'Source/Foundation/**/*.swift'
end
# Layer 2: Data (depends on Foundation)
spec.subspec 'Data' do |data|
data.source_files = 'Source/Data/**/*.swift'
data.dependency 'MySDK/Foundation'
end
# Layer 3: Domain (depends on Data)
spec.subspec 'Domain' do |domain|
domain.source_files = 'Source/Domain/**/*.swift'
domain.dependency 'MySDK/Data'
end
# Layer 4: Presentation (depends on Domain)
spec.subspec 'Presentation' do |presentation|
presentation.source_files = 'Source/Presentation/**/*.swift'
presentation.dependency 'MySDK/Domain'
presentation.ios.frameworks = 'UIKit'
end
end
Protocol + Implementations
Pod::Spec.new do |spec|
spec.name = 'MyStorage'
# Protocol definitions
spec.subspec 'Core' do |core|
core.source_files = 'Source/Core/**/*.swift'
end
# UserDefaults implementation
spec.subspec 'UserDefaults' do |ud|
ud.source_files = 'Source/UserDefaults/**/*.swift'
ud.dependency 'MyStorage/Core'
end
# Keychain implementation
spec.subspec 'Keychain' do |keychain|
keychain.source_files = 'Source/Keychain/**/*.swift'
keychain.dependency 'MyStorage/Core'
keychain.dependency 'KeychainAccess', '~> 4.0'
end
# SQLite implementation
spec.subspec 'SQLite' do |sqlite|
sqlite.source_files = 'Source/SQLite/**/*.swift'
sqlite.dependency 'MyStorage/Core'
sqlite.libraries = 'sqlite3'
end
end
User Installation Patterns
Installing Default Subspecs
# Installs default subspecs only
pod 'MyLibrary'
Installing Specific Subspecs
# Install only Core
pod 'MyLibrary/Core'
# Install Core and Networking
pod 'MyLibrary/Core'
pod 'MyLibrary/Networking'
# Or more concisely
pod 'MyLibrary', :subspecs => ['Core', 'Networking']
Installing All Subspecs
# Install everything (not recommended - bloats dependency tree)
# No built-in way - user must list each subspec
Nested Subspecs
spec.subspec 'Networking' do |net|
# Nested subspec: Networking/REST
net.subspec 'REST' do |rest|
rest.source_files = 'Source/Networking/REST/**/*.swift'
rest.dependency 'MyLibrary/Core'
end
# Nested subspec: Networking/GraphQL
net.subspec 'GraphQL' do |graphql|
graphql.source_files = 'Source/Networking/GraphQL/**/*.swift'
graphql.dependency 'MyLibrary/Core'
graphql.dependency 'Apollo', '~> 1.0'
end
end
# Users install with:
# pod 'MyLibrary/Networking/REST'
# pod 'MyLibrary/Networking/GraphQL'
Best Practices
Directory Structure
MyLibrary/
├── MyLibrary.podspec
├── Source/
│ ├── Core/ # Core subspec
│ ├── Networking/ # Networking subspec
│ ├── UI/ # UI subspec
│ └── Analytics/ # Analytics subspec
├── Resources/
│ ├── Core/
│ ├── UI/
│ └── Analytics/
└── Tests/
├── CoreTests/
├── NetworkingTests/
└── UITests/
Naming Conventions
# Use clear, descriptive names
spec.subspec 'Networking' # Good
spec.subspec 'Net' # Too abbreviated
# Group related functionality
spec.subspec 'UI'
spec.subspec 'UIComponents'
spec.subspec 'UIExtensions'
# Platform suffixes when needed
spec.subspec 'iOS'
spec.subspec 'macOS'
Dependency Guidelines
# Keep dependency chains shallow
spec.subspec 'A' do |a|
a.dependency 'MyLib/Core' # 1 level - Good
end
spec.subspec 'B' do |b|
b.dependency 'MyLib/A' # 2 levels - OK
end
spec.subspec 'C' do |c|
c.dependency 'MyLib/B' # 3 levels - Consider flattening
end
Anti-Patterns
Don't
❌ Create too many small subspecs
# Over-granular
spec.subspec 'StringExtensions'
spec.subspec 'ArrayExtensions'
spec.subspec 'DictionaryExtensions'
# Better: Group as 'Extensions'
❌ Circular dependencies
spec.subspec 'A' do |a|
a.dependency 'MyLib/B'
end
spec.subspec 'B' do |b|
b.dependency 'MyLib/A' # CIRCULAR - Will fail
end
❌ Duplicate source files
spec.subspec 'Core' do |core|
core.source_files = 'Source/**/*.swift' # Includes everything
end
spec.subspec 'Utils' do |utils|
utils.source_files = 'Source/Utils/**/*.swift' # DUPLICATE
end
Do
✅ Group related functionality
spec.subspec 'Extensions' do |ext|
ext.source_files = 'Source/Extensions/**/*.swift'
end
✅ Use clear dependency hierarchy
spec.subspec 'A' do |a|
a.dependency 'MyLib/Core'
end
spec.subspec 'B' do |b|
b.dependency 'MyLib/Core' # Both depend on Core - Good
end
✅ Keep source files separate
spec.subspec 'Core' do |core|
core.source_files = 'Source/Core/**/*.swift'
end
spec.subspec 'Utils' do |utils|
utils.source_files = 'Source/Utils/**/*.swift'
end
Testing Subspecs
# Lint specific subspec
pod lib lint --include-podspecs=*.podspec
# Test specific subspec in example project
cd Example
pod install
# Then build/run in Xcode
Related Skills
- cocoapods-podspec-fundamentals
- cocoapods-test-specs
- cocoapods-publishing-workflow