Skip to content

kramlex/ObservedKeyPathsMacros

ObservedKeyPathsMacros

A Swift macro that automatically generates observedKeyPaths and observation readers for SwiftUI and Observation framework integration.

Features

  • πŸ” Automatically detects observable properties using @ObservationTracked or falls back to all non-ignored stored properties
  • πŸ“± Generates observedKeyPaths array for type-safe key path access
  • πŸ‘€ Creates observation readers for efficient change tracking
  • πŸ”„ Integrates with Combine and ObservableObject for backward compatibility
  • ⚑ Lightweight and performant with no runtime overhead

Requirements

  • iOS 17.0+ / macOS 10.15+
  • Swift 5.10+
  • Xcode 15.0+

Installation

Swift Package Manager

Add the following dependency to your Package.swift:

dependencies: [
    .package(url: "https://github.com/kramlex/ObservedKeyPathsMacros.git", from: "1.0.0")
]

Or add it directly in Xcode:

  1. File β†’ Add Package Dependencies
  2. Enter: https://github.com/kramlex/ObservedKeyPathsMacros.git
  3. Click Add Package

Usage

Basic Usage

Simply add the @GenerateObservedKeyPaths macro to your Observable class:

import ObservedKeyPaths
import Observation

@Observable
@GenerateObservedKeyPaths
class UserProfile {
    var name: String = ""
    var email: String = ""
    var age: Int = 0
    
    // The macro will automatically generate:
    // - observedKeyPaths: [\UserProfile.name, \UserProfile.email, \UserProfile.age]
    // - _observationReaders for efficient tracking
    // - objectWillChange publisher for Combine compatibility
}

With Explicit Tracking

You can explicitly mark which properties to track:

@Observable
@GenerateObservedKeyPaths
class Settings {
    @ObservationTracked var theme: String = "light"
    @ObservationTracked var fontSize: Int = 14
    
    var internalId: String = "123" // This won't be tracked
    
    // Only theme and fontSize will be included in observedKeyPaths
}

Ignoring Properties

Use @ObservationIgnored to exclude properties from tracking:

@Observable
@GenerateObservedKeyPaths
class DataModel {
    var importantData: String = ""
    
    @ObservationIgnored var cachedData: String = "" // This won't be tracked
    @ObservationIgnored var debugInfo: String = "" // This won't be tracked
}

Combine Integration

The macro automatically provides Combine compatibility:

@Observable
@GenerateObservedKeyPaths
class Counter {
    var count: Int = 0
    
    func increment() {
        count += 1
    }
}

let counter = Counter()
let cancellable = counter.objectWillChange
    .sink { _ in
        print("Counter changed!")
    }

counter.increment() // Will trigger the publisher

How It Works

The @GenerateObservedKeyPaths macro:

  1. Analyzes your class/struct to find observable properties
  2. Generates observedKeyPaths - an array of PartialKeyPath<Self> for type-safe access
  3. Creates observation readers - functions that access each property to trigger observation
  4. Provides Combine compatibility - integrates with existing ObservableObject patterns

Generated Code

For a class like:

@Observable
@GenerateObservedKeyPaths
class Example {
    var value: String = ""
    var count: Int = 0
}

The macro generates:

extension Example: ObservationType {}

static let observedKeyPaths: [PartialKeyPath<Example>] = [\Example.value, \Example.count]
static let _observationReaders: [(Example) -> Void] = [{ _ = $0.value as Any }, { _ = $0.count as Any }]

@ObservationIgnored
private lazy var _holder: ObservableObjectPublisherHolder = {
    ObservableObjectPublisherHolder(changesPublisherUsingReaders())
}()

@ObservationIgnored
public lazy var objectWillChange: ObservableObjectPublisher = {
    _holder.objectWillChange
}()

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Built with SwiftSyntax
  • Inspired by the Swift Observation framework
  • Designed for seamless SwiftUI integration

About

No description, website, or topics provided.

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages

 
 
 

Contributors

Languages