Skip to content

Lightweight and type-safe reactive signal system for modern Swift apps. Built with concurrency, Combine interop, and real-time UX in mind

License

Notifications You must be signed in to change notification settings

ranjanakarsh/Signal

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 

Repository files navigation

Signal.swift

A lightweight, thread-safe, reactive state management library for Swift — built with Swift Concurrency, Combine compatibility, and high-end scalability in mind.


Swift iOS License Platform


Motivation

Signal.swift was born out of the need for a minimal yet powerful reactive layer suitable for large-scale iOS applications — like social networks or dating apps — without the overhead of RxSwift or Combine-only constraints.

Designed by @ranjanakarsh, it blends:

  • Actor-based safety with Swift Concurrency
  • Multicast emissions with weak references
  • Type erasure, Combine bridging, and value replay
  • Debouncing, throttling, and event-state models
  • Debug tracing and performance logging

Features

  • Actor-isolated signals for race-free data propagation
  • Auto-cleanup of deallocated subscribers
  • Built entirely with actor isolation for thread safety
  • async/await-based subsription model
  • emit, complete, fail for lifecycle awareness
  • Type-safe events: .next, .completed, .failed
  • Value replay with configurable buffer size (ValueSignal)
  • Throttled and debounced emissions
  • Combine-compatible .publisher
  • Token-based unsubscribe system
  • Lightweight — no third-party dependencies required (except swift-atomics)

Installation

Swift Package Manager (Recommended)

.package(url: "https://github.com/ranjanakarsh/Signal.swift.git", from: "1.0.0")

Then import:

import Signal

Quick Start

Basic Usage

let signal = Signal<String>()

let token = await signal.subscribe(owner: self) { value in
    print("Received:", value)
}

signal.emit("Hello world!")

Note: all subscribe(...) methods are now async to support actor isolation and concurrency safety. Use await when subscribing to signals.

With Completion and Failure

await signal.subscribeEvent(owner: self) { event in
    switch event {
    case .next(let value): print("Received:", value)
    case .completed: print("Signal completed")
    case .failed(let error): print("Error:", error)
    }
}

signal.emit("final value")
signal.complete()

Value Replay

let valueSignal = ValueSignal<Int>(replayCount: 2)
valueSignal.emit(1)
valueSignal.emit(2)

await valueSignal.subscribe(owner: self) { value in
    print("Got replayed value:", value)
}

Combine Integration

let publisher = await signal.publisher
let cancellable = publisher.sink { value in
    print("Combine received:", value)
}

Architecture

Signal Types

Type Description
Signal<T> Actor-isolated broadcast channel, async-safe subscriptions
ValueSignal<T> Cached value signal with replayCount, built on Signal
AnySignal<T> Type-erased wrapper supporting async subscribe
SignalResult<T, E> Convenience enum for result-based signaling

Debugging

Enable debug tracking:

signal.setDebugOptions(Signal.DebugOptions(
    name: "AuthSignal",
    loggingEnabled: true
))

Support for os_signpost tracing is included.

Design philosophy

  • Favor Swift-native concurrency over legacy locks
  • Maintain decoupled logic with high composability
  • Provide low-cost reactivity for UI and real-time systems
  • Keep APIs simple and intuitive, but flexible

Roadmap

  • Signal-to-async/await stream bridging
  • SwiftUI property wrappers
  • ReplaySubject-style hot signals
  • DevTools visual debugging inspector
  • SignalGroup for bulk coordination

Contributing

Want to help? Contributions are welcome!

  • Open an issue or submit a feature request
  • Fort the repo and create a PR
  • Write tests for new features

Please ensure your changes are covered with tests and follow Swift style guides.

Inspiration

This project draws conceptual inspiration form:

  • Combine
  • RxSwift
  • The Composable Architecture (TCA)
  • SignalKit

License

MIT @ Ranjan Akarsh

Releases

No releases published

Packages

No packages published

Languages