cocoapods-subspecs-organization

$npx mdskill add TheBushidoCollective/han/cocoapods-subspecs-organization

Modularize complex CocoaPods libraries by structuring them into independent, manageable subspecs.

  • Separates core functionality from optional features for cleaner dependency graphs.
  • Manipulates Podspec files using Read, Write, Edit, and Bash execution.
  • Applies established patterns for defining default and optional module inclusions.
  • Outputs structured Podspec definitions detailing module dependencies and source files.

SKILL.md

.github/skills/cocoapods-subspecs-organizationView on GitHub ↗
---
name: cocoapods-subspecs-organization
user-invocable: false
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

```ruby
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

```ruby
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

```ruby
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

```ruby
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

```ruby
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

```ruby
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

```ruby
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

```ruby
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

```ruby
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

```ruby
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

```ruby
# Installs default subspecs only
pod 'MyLibrary'
```

### Installing Specific Subspecs

```ruby
# 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

```ruby
# Install everything (not recommended - bloats dependency tree)
# No built-in way - user must list each subspec
```

## Nested Subspecs

```ruby
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

```ruby
# 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

```ruby
# 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

```ruby
# Over-granular
spec.subspec 'StringExtensions'
spec.subspec 'ArrayExtensions'
spec.subspec 'DictionaryExtensions'
# Better: Group as 'Extensions'
```

❌ Circular dependencies

```ruby
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

```ruby
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

```ruby
spec.subspec 'Extensions' do |ext|
  ext.source_files = 'Source/Extensions/**/*.swift'
end
```

✅ Use clear dependency hierarchy

```ruby
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

```ruby
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

```bash
# 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

More from TheBushidoCollective/han

SkillDescription
absinthe-resolversUse when implementing GraphQL resolvers with Absinthe. Covers resolver patterns, dataloader integration, batching, and error handling.
absinthe-schemaUse when designing GraphQL schemas with Absinthe. Covers type definitions, interfaces, unions, enums, and schema organization patterns.
absinthe-subscriptionsUse when implementing real-time GraphQL subscriptions with Absinthe. Covers Phoenix channels, PubSub, and subscription patterns.
act-docker-setupUse when configuring Docker environments for act, selecting runner images, managing container resources, or troubleshooting Docker-related issues with local GitHub Actions testing.
act-local-testingUse when testing GitHub Actions workflows locally with act. Covers act CLI usage, Docker configuration, debugging workflows, and troubleshooting common issues when running workflows on your local machine.
act-workflow-syntaxUse when creating or modifying GitHub Actions workflow files. Provides guidance on workflow syntax, triggers, jobs, steps, and expressions for creating valid GitHub Actions workflows that can be tested locally with act.
ameba-configurationUse when configuring Ameba rules and settings for Crystal projects including .ameba.yml setup, rule management, severity levels, and code quality enforcement.
ameba-custom-rulesUse when creating custom Ameba rules for Crystal code analysis including rule development, AST traversal, issue reporting, and rule testing.
ameba-integrationUse when integrating Ameba into development workflows including CI/CD pipelines, pre-commit hooks, GitHub Actions, and automated code review processes.
analyze-performanceAnalyze performance metrics and identify slow transactions in Sentry