All skills
rshankras avatar

/architecture-patterns

@1361023

Deep dive into software architecture for macOS. Covers SOLID principles, design patterns, and modular code organization. Use when designing app architecture or refactoring.

Use this Skill: https://skilld.dev/gh/rshankras/claude-code-apple-skills/architecture-patterns

This session only. Nothing lands on disk.

modular-design.md

≈2.2k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Modular Design

Swift Package Manager organization, feature modules, and code organization strategies for macOS applications.

Project Organization Strategies

Strategy 1: Group by Feature (Recommended for Most Apps)

MyApp/
├── Features/
│   ├── Documents/
│   │   ├── DocumentListView.swift
│   │   ├── DocumentDetailView.swift
│   │   ├── DocumentViewModel.swift
│   │   └── DocumentModel.swift
│   ├── Settings/
│   │   ├── SettingsView.swift
│   │   ├── GeneralSettingsTab.swift
│   │   ├── AppearanceSettingsTab.swift
│   │   └── SettingsViewModel.swift
│   └── Search/
│       ├── SearchView.swift
│       ├── SearchResultRow.swift
│       └── SearchViewModel.swift
├── Core/
│   ├── Models/
│   │   └── SharedModels.swift
│   ├── Services/
│   │   ├── PersistenceService.swift
│   │   └── NetworkService.swift
│   └── Extensions/
│       └── Date+Formatting.swift
├── App/
│   ├── MyApp.swift
│   ├── ContentView.swift
│   └── AppState.swift
└── Resources/
    └── Assets.xcassets

Pros: Related files together, easy to find things, scales well Cons: Shared dependencies can create circular references

Strategy 2: Group by Layer

MyApp/
├── Views/
│   ├── DocumentListView.swift
│   ├── DocumentDetailView.swift
│   ├── SettingsView.swift
│   └── SearchView.swift
├── ViewModels/
│   ├── DocumentViewModel.swift
│   ├── SettingsViewModel.swift
│   └── SearchViewModel.swift
├── Models/
│   ├── Document.swift
│   ├── Settings.swift
│   └── SearchResult.swift
├── Services/
│   ├── DocumentRepository.swift
│   └── SearchService.swift
└── App/
    └── MyApp.swift

Pros: Clear separation of concerns, familiar to MVVM developers Cons: Related files scattered across folders, doesn't scale as well

Recommendation

  • Small apps (< 15 files): Group by layer — simpler, less folder nesting
  • Medium+ apps (15+ files): Group by feature — better discoverability and modularity

Swift Package Manager Modularization

For large apps, extract code into local Swift packages for build isolation, clear API boundaries, and faster incremental builds.

Package Structure

MyApp/
├── MyApp.xcodeproj
├── MyApp/                          # App target (thin shell)
│   ├── MyApp.swift
│   └── ContentView.swift
└── Packages/
    ├── Core/                       # Shared models, protocols, utilities
    │   ├── Package.swift
    │   └── Sources/Core/
    │       ├── Models/
    │       ├── Protocols/
    │       └── Extensions/
    ├── DocumentFeature/            # Document management feature
    │   ├── Package.swift
    │   └── Sources/DocumentFeature/
    │       ├── DocumentListView.swift
    │       ├── DocumentDetailView.swift
    │       └── DocumentViewModel.swift
    ├── Persistence/                # Data layer
    │   ├── Package.swift
    │   └── Sources/Persistence/
    │       ├── SwiftDataModels/
    │       └── Repositories/
    └── Networking/                  # Network layer
        ├── Package.swift
        └── Sources/Networking/
            ├── APIClient.swift
            └── Endpoints/

Package.swift for a Feature Module

// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "DocumentFeature",
    platforms: [.macOS(.v14)],
    products: [
        .library(name: "DocumentFeature", targets: ["DocumentFeature"]),
    ],
    dependencies: [
        .package(path: "../Core"),
        .package(path: "../Persistence"),
    ],
    targets: [
        .target(
            name: "DocumentFeature",
            dependencies: ["Core", "Persistence"]
        ),
        .testTarget(
            name: "DocumentFeatureTests",
            dependencies: ["DocumentFeature"]
        ),
    ]
)

Dependency Graph Rules

  1. Service layers (Persistence, Networking) depend on Core only
  2. Feature modules depend on Core and relevant service layers
  3. App target depends on all feature modules and wires them together
App → DocumentFeature → Persistence → Core
    → SettingsFeature → Core
    → SearchFeature → Networking → Core

Access Control for Module Boundaries

Use Swift's access levels to enforce clean module APIs:

// In Persistence module

// Public: API surface used by other modules
public protocol TaskRepository: Sendable {
    func fetchAll() async throws -> [Task]
    func save(_ task: Task) async throws
}

// Public: consumers need to create this
public struct SwiftDataTaskRepository: TaskRepository {
    public init(modelContext: ModelContext) { ... }
    public func fetchAll() async throws -> [Task] { ... }
    public func save(_ task: Task) async throws { ... }
}

// Internal: implementation detail, not visible outside module
struct CacheManager {
    func invalidate() { ... }
}

// Package: visible to other targets in same package, not outside
package struct MigrationHelper {
    package func migrate(from oldSchema: Schema) { ... }
}

Access Level Summary

Level Visible To
private Enclosing declaration only
fileprivate Same source file
internal (default) Same module/target
package Same package
public Any importing module
open Any module (can subclass/override)

DRY: Reducing Duplication

Extract Shared Views

// Reusable components in Core or a shared UI module
struct EmptyStateView: View {
    let title: String
    let systemImage: String
    let description: String

    var body: some View {
        ContentUnavailableView(title, systemImage: systemImage, description: Text(description))
    }
}

struct LoadingOverlay: View {
    let isLoading: Bool
    var body: some View {
        if isLoading {
            ProgressView()
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(.ultraThinMaterial)
        }
    }
}

Extract Common Logic into Extensions

// Instead of duplicating date formatting across features
extension Date {
    var relativeDisplay: String {
        let formatter = RelativeDateTimeFormatter()
        formatter.unitsStyle = .abbreviated
        return formatter.localizedString(for: self, relativeTo: .now)
    }

    var shortDisplay: String {
        formatted(date: .abbreviated, time: .shortened)
    }
}

Protocol with Default Implementations

protocol Searchable {
    var searchableText: String { get }
}

extension Array where Element: Searchable {
    func search(query: String) -> [Element] {
        guard !query.isEmpty else { return self }
        let lowered = query.lowercased()
        return filter { $0.searchableText.lowercased().contains(lowered) }
    }
}

// Any model can opt-in
struct Document: Searchable {
    let title: String
    let content: String
    var searchableText: String { "\(title) \(content)" }
}

struct Contact: Searchable {
    let name: String
    let email: String
    var searchableText: String { "\(name) \(email)" }
}

When to Modularize

Signal Action
Build times getting slow Extract stable code into packages
Multiple developers working on same files Split into feature modules
Want to share code between app and extension Extract into shared package
Tests require the full app to compile Extract testable code into packages
Hard to find files Reorganize by feature

Testing Across Modules

Each package has its own test target with fast, isolated tests:

// In DocumentFeatureTests/
@testable import DocumentFeature
import Core

struct MockTaskRepository: TaskRepository {
    var tasks: [Task] = []
    func fetchAll() async throws -> [Task] { tasks }
    func save(_ task: Task) async throws { }
}

@Test func testFilteredTasks() async {
    let repo = MockTaskRepository(tasks: [
        Task(id: UUID(), title: "Active", isCompleted: false),
        Task(id: UUID(), title: "Done", isCompleted: true),
    ])
    let viewModel = TaskListViewModel(repository: repo)
    await viewModel.loadTasks()

    viewModel.filterOption = .active
    #expect(viewModel.filteredTasks.count == 1)
    #expect(viewModel.filteredTasks[0].title == "Active")
}

Source: SKILL.md on GitHub

No alerts2mo3 checks · Risk SAFE
  • Gen Agent Trust Hub2mo

    The skill is a comprehensive guide for macOS software architecture using Swift. It is generally safe but possesses a standard attack surface for indirect prompt injection as it analyzes external code using file-reading tools without explicit boundary instructions.

  • Socket2mo

    No alerts

  • Snyk2mo

    Risk: LOW · No issues

Signed by skilld at 1361023. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 3 months ago
What it can do
Reads files
last_verified
2026-07-16
review_by
2027-06-22
os_version
iOS 27 / macOS 27
All 3 allowed tools
ReadGlobGrep

README badge

README badge for rshankras/claude-code-apple-skills/architecture-patterns