All skills

Comprehensive macOS development guidance including Swift 6+, SwiftUI, SwiftData, architecture patterns, AppKit bridging, and macOS 26 Tahoe APIs. Use for macOS code review, best practices, UI review, or platform-specific features.

  • 40 files
  • 300.9 KB
  • Updated 3 months ago
  • GitHub

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

This session only. Nothing lands on disk.

coding-best-practicesarchitecture-principles.md

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

Architecture & Design Principles

SOLID principles, DRY, Clean Architecture, and design patterns for macOS development.

SOLID Principles

S - Single Responsibility Principle (SRP)

Definition: A class should have only one reason to change.

// ❌ BAD: Multiple responsibilities
class UserViewController: NSViewController {
    func loadUser() {
        // Network call
        let url = URL(string: "https://api.example.com/user")!
        URLSession.shared.dataTask(with: url) { data, _, _ in
            // Parsing
            let user = try? JSONDecoder().decode(User.self, from: data!)
            // Database saving
            try? self.saveToDatabase(user!)
            // UI update
            DispatchQueue.main.async {
                self.nameLabel.stringValue = user!.name
            }
        }.resume()
    }
}

// ✅ GOOD: Separated responsibilities
protocol UserRepository {
    func fetchUser(id: UUID) async throws -> User
}

class NetworkUserRepository: UserRepository {
    func fetchUser(id: UUID) async throws -> User {
        // Only handles network calls
    }
}

@MainActor
class UserViewModel: ObservableObject {
    @Published var user: User?
    private let repository: UserRepository

    init(repository: UserRepository) {
        self.repository = repository
    }

    func loadUser(id: UUID) async {
        do {
            user = try await repository.fetchUser(id: id)
        } catch {
            // Handle error
        }
    }
}

class UserViewController: NSViewController {
    private let viewModel: UserViewModel

    // Only handles UI updates
    func updateUI() {
        nameLabel.stringValue = viewModel.user?.name ?? ""
    }
}

O - Open/Closed Principle (OCP)

Definition: Software entities should be open for extension but closed for modification.

// ❌ BAD: Must modify class to add new export formats
class DocumentExporter {
    func export(_ document: Document, format: String) throws -> Data {
        switch format {
        case "pdf":
            return exportToPDF(document)
        case "html":
            return exportToHTML(document)
        default:
            throw ExportError.unsupportedFormat
        }
    }
}

// ✅ GOOD: Open for extension via protocols
protocol DocumentExportStrategy {
    func export(_ document: Document) throws -> Data
}

class PDFExportStrategy: DocumentExportStrategy {
    func export(_ document: Document) throws -> Data {
        // PDF export logic
    }
}

class HTMLExportStrategy: DocumentExportStrategy {
    func export(_ document: Document) throws -> Data {
        // HTML export logic
    }
}

class MarkdownExportStrategy: DocumentExportStrategy {
    func export(_ document: Document) throws -> Data {
        // Markdown export logic - new format added without modifying existing code
    }
}

class DocumentExporter {
    func export(_ document: Document, using strategy: DocumentExportStrategy) throws -> Data {
        try strategy.export(document)
    }
}

L - Liskov Substitution Principle (LSP)

Definition: Subtypes must be substitutable for their base types.

// ❌ BAD: Violates LSP - ReadOnlyDocument can't fulfill Document contract
class Document {
    var content: String

    func save() throws {
        // Save to disk
    }
}

class ReadOnlyDocument: Document {
    override func save() throws {
        throw DocumentError.readOnly  // Violates contract!
    }
}

// ✅ GOOD: Proper abstraction
protocol Readable {
    var content: String { get }
}

protocol Writable {
    var content: String { get set }
    func save() throws
}

class Document: Readable, Writable {
    var content: String

    func save() throws {
        // Save to disk
    }
}

class ReadOnlyDocument: Readable {
    let content: String  // Immutable, as expected

    init(content: String) {
        self.content = content
    }
}

// Now functions can require only what they need
func displayContent(_ document: Readable) {
    print(document.content)  // Works with both types
}

func editContent(_ document: Writable) {
    // Works only with writable documents
}

I - Interface Segregation Principle (ISP)

Definition: Clients should not be forced to depend on interfaces they don't use.

// ❌ BAD: Fat protocol forcing unnecessary implementations
protocol MediaPlayer {
    func play()
    func pause()
    func stop()
    func adjustVolume(_ volume: Int)
    func showSubtitles(_ show: Bool)
    func setPlaybackSpeed(_ speed: Double)
}

class AudioPlayer: MediaPlayer {
    func play() { /* ... */ }
    func pause() { /* ... */ }
    func stop() { /* ... */ }
    func adjustVolume(_ volume: Int) { /* ... */ }
    func showSubtitles(_ show: Bool) { /* Not applicable! */ }
    func setPlaybackSpeed(_ speed: Double) { /* Not applicable! */ }
}

// ✅ GOOD: Segregated interfaces
protocol Playable {
    func play()
    func pause()
    func stop()
}

protocol VolumeControllable {
    func adjustVolume(_ volume: Int)
}

protocol SubtitleSupporting {
    func showSubtitles(_ show: Bool)
}

protocol PlaybackSpeedControllable {
    func setPlaybackSpeed(_ speed: Double)
}

class AudioPlayer: Playable, VolumeControllable {
    func play() { /* ... */ }
    func pause() { /* ... */ }
    func stop() { /* ... */ }
    func adjustVolume(_ volume: Int) { /* ... */ }
    // No forced subtitle implementation!
}

class VideoPlayer: Playable, VolumeControllable, SubtitleSupporting, PlaybackSpeedControllable {
    // Implements all relevant protocols
}

D - Dependency Inversion Principle (DIP)

Definition: High-level modules should not depend on low-level modules. Both should depend on abstractions.

// ❌ BAD: High-level class depends on concrete implementation
class ArticleViewController: NSViewController {
    private let database = CoreDataManager()  // Concrete dependency!

    func loadArticles() {
        let articles = database.fetchArticles()
        // Update UI
    }
}

// ✅ GOOD: Depend on abstraction
protocol ArticleRepository {
    func fetchArticles() async throws -> [Article]
    func save(_ article: Article) async throws
}

class CoreDataArticleRepository: ArticleRepository {
    func fetchArticles() async throws -> [Article] {
        // Core Data implementation
    }

    func save(_ article: Article) async throws {
        // Core Data implementation
    }
}

class SwiftDataArticleRepository: ArticleRepository {
    func fetchArticles() async throws -> [Article] {
        // SwiftData implementation
    }

    func save(_ article: Article) async throws {
        // SwiftData implementation
    }
}

@MainActor
class ArticleViewModel: ObservableObject {
    @Published var articles: [Article] = []
    private let repository: ArticleRepository  // Depends on abstraction!

    init(repository: ArticleRepository) {
        self.repository = repository
    }

    func loadArticles() async {
        do {
            articles = try await repository.fetchArticles()
        } catch {
            // Handle error
        }
    }
}

// Easy to swap implementations or mock for testing
let viewModel = ArticleViewModel(repository: SwiftDataArticleRepository())
let testViewModel = ArticleViewModel(repository: MockArticleRepository())

DRY Principle (Don't Repeat Yourself)

Definition: Every piece of knowledge must have a single, unambiguous representation.

Code Duplication

// ❌ BAD: Repeated validation logic
func createUser(name: String, email: String) throws {
    if name.isEmpty {
        throw ValidationError.emptyName
    }
    if !email.contains("@") {
        throw ValidationError.invalidEmail
    }
    // Create user
}

func updateUser(name: String, email: String) throws {
    if name.isEmpty {
        throw ValidationError.emptyName
    }
    if !email.contains("@") {
        throw ValidationError.invalidEmail
    }
    // Update user
}

// ✅ GOOD: Extract common validation
struct UserValidator {
    static func validate(name: String, email: String) throws {
        guard !name.isEmpty else {
            throw ValidationError.emptyName
        }
        guard email.contains("@") else {
            throw ValidationError.invalidEmail
        }
    }
}

func createUser(name: String, email: String) throws {
    try UserValidator.validate(name: name, email: email)
    // Create user
}

func updateUser(name: String, email: String) throws {
    try UserValidator.validate(name: name, email: email)
    // Update user
}

SwiftUI View Duplication

// ❌ BAD: Repeated UI components
struct ProfileView: View {
    var body: some View {
        VStack {
            HStack {
                Image(systemName: "person")
                Text("John Doe")
                    .font(.headline)
            }
            .padding()
            .background(Color.gray.opacity(0.2))
            .cornerRadius(8)

            HStack {
                Image(systemName: "envelope")
                Text("john@example.com")
                    .font(.headline)
            }
            .padding()
            .background(Color.gray.opacity(0.2))
            .cornerRadius(8)
        }
    }
}

// ✅ GOOD: Reusable component
struct InfoRow: View {
    let icon: String
    let text: String

    var body: some View {
        HStack {
            Image(systemName: icon)
            Text(text)
                .font(.headline)
        }
        .padding()
        .background(Color.gray.opacity(0.2))
        .cornerRadius(8)
    }
}

struct ProfileView: View {
    var body: some View {
        VStack {
            InfoRow(icon: "person", text: "John Doe")
            InfoRow(icon: "envelope", text: "john@example.com")
        }
    }
}

Configuration Duplication

// ❌ BAD: Hardcoded values everywhere
class NetworkManager {
    func fetchUsers() async throws -> [User] {
        let url = URL(string: "https://api.example.com/users")!
        // ...
    }

    func fetchPosts() async throws -> [Post] {
        let url = URL(string: "https://api.example.com/posts")!
        // ...
    }
}

// ✅ GOOD: Centralized configuration
enum APIEndpoint {
    case users
    case posts
    case articles

    var url: URL {
        let baseURL = "https://api.example.com"
        switch self {
        case .users: return URL(string: "\(baseURL)/users")!
        case .posts: return URL(string: "\(baseURL)/posts")!
        case .articles: return URL(string: "\(baseURL)/articles")!
        }
    }
}

class NetworkManager {
    func fetch<T: Decodable>(from endpoint: APIEndpoint) async throws -> T {
        let url = endpoint.url
        // Single fetch implementation
    }

    func fetchUsers() async throws -> [User] {
        try await fetch(from: .users)
    }

    func fetchPosts() async throws -> [Post] {
        try await fetch(from: .posts)
    }
}

Clean Architecture

Layer Separation

// Domain Layer - Business logic, no framework dependencies
struct Article {
    let id: UUID
    let title: String
    let content: String
    let author: Author
}

protocol ArticleRepository {
    func fetch(id: UUID) async throws -> Article
    func save(_ article: Article) async throws
}

class ArticleUseCase {
    private let repository: ArticleRepository

    init(repository: ArticleRepository) {
        self.repository = repository
    }

    func publishArticle(_ article: Article) async throws {
        // Business logic
        guard article.content.count > 100 else {
            throw ValidationError.contentTooShort
        }
        try await repository.save(article)
    }
}

// Data Layer - Framework-specific implementations
import SwiftData

@Model
class ArticleEntity {
    @Attribute(.unique) var id: UUID
    var title: String
    var content: String
    // SwiftData specific
}

class SwiftDataArticleRepository: ArticleRepository {
    private let modelContext: ModelContext

    func fetch(id: UUID) async throws -> Article {
        // Convert ArticleEntity to Article
    }

    func save(_ article: Article) async throws {
        // Convert Article to ArticleEntity and save
    }
}

// Presentation Layer - SwiftUI/AppKit
@MainActor
class ArticleViewModel: ObservableObject {
    @Published var article: Article?
    private let useCase: ArticleUseCase

    init(useCase: ArticleUseCase) {
        self.useCase = useCase
    }

    func publish() async {
        guard let article else { return }
        do {
            try await useCase.publishArticle(article)
        } catch {
            // Handle error
        }
    }
}

Dependency Injection

Constructor Injection (Preferred)

// ✅ GOOD: Dependencies injected via initializer
class ArticleService {
    private let repository: ArticleRepository
    private let logger: Logger
    private let validator: ArticleValidator

    init(
        repository: ArticleRepository,
        logger: Logger,
        validator: ArticleValidator
    ) {
        self.repository = repository
        self.logger = logger
        self.validator = validator
    }
}

Property Injection (Use Sparingly)

// Use for SwiftUI Environment
struct ArticleListView: View {
    @Environment(\.articleRepository) var repository

    var body: some View {
        // Use repository
    }
}

// Define environment key
private struct ArticleRepositoryKey: EnvironmentKey {
    static let defaultValue: ArticleRepository = MockArticleRepository()
}

extension EnvironmentValues {
    var articleRepository: ArticleRepository {
        get { self[ArticleRepositoryKey.self] }
        set { self[ArticleRepositoryKey.self] = newValue }
    }
}

Service Locator (Avoid When Possible)

// ⚠️ Use sparingly - hides dependencies
class ServiceLocator {
    static let shared = ServiceLocator()

    private var services: [String: Any] = [:]

    func register<T>(_ service: T, for type: T.Type) {
        let key = String(describing: type)
        services[key] = service
    }

    func resolve<T>(_ type: T.Type) -> T? {
        let key = String(describing: type)
        return services[key] as? T
    }
}

Composition Over Inheritance

// ❌ BAD: Deep inheritance hierarchy
class Vehicle {
    func start() { }
}

class Car: Vehicle {
    func honk() { }
}

class ElectricCar: Car {
    func charge() { }
}

class TeslaModelS: ElectricCar {
    // Too deep!
}

// ✅ GOOD: Composition with protocols
protocol Startable {
    func start()
}

protocol Honkable {
    func honk()
}

protocol Chargeable {
    func charge()
}

struct ElectricCar: Startable, Honkable, Chargeable {
    private let engine: ElectricEngine
    private let horn: Horn
    private let battery: Battery

    func start() {
        engine.start()
    }

    func honk() {
        horn.makeSound()
    }

    func charge() {
        battery.charge()
    }
}

Architecture Patterns Checklist

  • SOLID: Each class has single responsibility
  • SOLID: Code open for extension, closed for modification
  • SOLID: Subtypes properly substitutable
  • SOLID: Interfaces are segregated and focused
  • SOLID: Depend on abstractions, not concretions
  • DRY: No duplicated logic or configuration
  • Clean Architecture: Clear layer separation
  • DI: Dependencies injected, not created internally
  • Composition: Prefer composition over inheritance
  • Testability: Easy to mock and test

Resources

Source: SKILL.md on GitHub

No third-party reports yet.

Signed by skilld at 32566aa. 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 Network
last_verified
2026-07-16
review_by
2027-06-22
os_version
iOS 27 / macOS 27
All 4 allowed tools
ReadGlobGrepWebFetch

README badge

README badge for rshankras/claude-code-apple-skills/macos