Skip to content

stefanprojchev/ForgeObservers

Repository files navigation

ForgeObservers

Reactive system observers for iOS — connectivity, lifecycle, keyboard, and more.

Swift 6.3+ iOS 18+ License Release

📖 Full documentation →


ForgeObservers exposes the most common iOS system events as AsyncStream values behind clean protocols. Every observer is testable via an injectable NotificationCenter — no UIApplication.shared required, no @testable import tricks.

Observers

Observer Protocol Emits
ConnectivityObserver ConnectivityObserving ConnectivityStatus — network path, interface, expensive/constrained flags
AppLifecycleObserver AppLifecycleObserving AppLifecycleState.active, .inactive, .background
KeyboardObserver KeyboardObserving KeyboardState — visibility, height, animation duration
AppearanceObserver AppearanceObserving AppAppearance — light/dark mode
LocaleObserver LocaleObserving AppLocale — language + region code
ProtectedDataObserver ProtectedDataObserving ProtectedDataState — available/unavailable, with waitUntilAvailable()
NotificationPermissionObserver NotificationPermissionObserving NotificationPermissionStatus

Features

  • AsyncStream-first — subscribe with for await in a .task modifier or inside an actor
  • Protocol-oriented — each observer has a protocol, making mocking trivial
  • Testable by design — notification-based observers accept an injectable NotificationCenter so tests can pump fake notifications
  • .assign(to:on:) helper — bind any AsyncSequence directly to a property on an object
  • Zero Combine dependency — pure Swift Concurrency

Requirements

  • iOS 18+
  • Swift 6.3+ (Xcode 26 or later)

Installation

Xcode

  1. File → Add Package Dependencies…
  2. Paste https://github.com/stefanprojchev/ForgeObservers.git
  3. Set rule to Up to Next Major from 1.0.0

Package.swift

dependencies: [
    .package(url: "https://github.com/stefanprojchev/ForgeObservers.git", from: "1.0.0")
],
targets: [
    .target(
        name: "YourApp",
        dependencies: ["ForgeObservers"]
    )
]

Quick Start

Connectivity

import ForgeObservers

let connectivity = ConnectivityObserver()

// Sync read of the current status
if connectivity.status.isConnected {
    await fetchLatestData()
}

// Subscribe to changes
Task {
    for await status in connectivity.statusStream {
        print("Connected: \(status.isConnected), via: \(status.interface)")
    }
}

App Lifecycle in SwiftUI

import SwiftUI
import ForgeObservers

struct ContentView: View {
    let lifecycle: AppLifecycleObserving

    var body: some View {
        FeedView()
            .task {
                for await state in lifecycle.stateStream {
                    switch state {
                    case .active:     resumeTimers()
                    case .background: await saveState()
                    case .inactive:   break
                    }
                }
            }
    }
}

Testing with injected NotificationCenter

import Testing
import UIKit
@testable import ForgeObservers

@Test
func lifecycleReactsToBackground() async throws {
    // Inject a fresh NotificationCenter — no interference from the real one
    let center = NotificationCenter()
    let observer = AppLifecycleObserver(notificationCenter: center)

    center.post(name: UIApplication.didEnterBackgroundNotification, object: nil)
    try await Task.sleep(for: .milliseconds(50))

    #expect(observer.state == .background)
}

AsyncSequence helpers

Bind any stream directly to a property:

@Observable
final class AppViewModel {
    var isOffline = false

    func start(connectivity: ConnectivityObserving) async {
        await connectivity.statusStream
            .map { !$0.isConnected }
            .assign(to: \.isOffline, on: self)
    }
}

Documentation

The Forge Family

ForgeObservers is part of the Forge family of Swift packages for iOS.

Package Description
ForgeCore Thread-safe primitives for iOS Swift packages.
ForgeInject Dependency injection with constructor and property wrapper support.
ForgeObservers Reactive system observers — connectivity, lifecycle, keyboard, and more.
ForgeStorage Type-safe key-value, file, and Keychain storage.
ForgeOrchestrator Orchestrate app flows — startup gates, data pipelines, and continuous monitors.
ForgePush Push notification management — permissions, tokens, and routing.
ForgeLocation Location triggers — geofencing, significant changes, and visits.
ForgeBackgroundTasks Background task scheduling and dispatch.

License

ForgeObservers is released under the MIT License. See LICENSE.

About

Reactive system observers for iOS — connectivity, lifecycle, keyboard, and more.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages