Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

1 Commit

Repository files navigation

ForgeAccess

Subscription-aware feature gating for the Forge package family.

Swift 6.3+iOS 18+macOS 15+LicenseRelease


ForgeAccess is the feature-gating library in the Forge family of iOS packages. Generic over your own Subscription and Feature enums — bring your own payment SDK; the library never assumes one.

Features

  • Generic over host-defined Subscription and Feature enums.
  • Pluggable subscription source — adapt RevenueCat, StoreKit 2, your backend, or a static value via SubscriptionStatusProviding.
  • Two override channels (feature-level + subscription-level) with built-in persisted, in-memory, and time-frame providers.
  • Detailed AccessProviderEvaluation for debug UIs and analytics ("why was this feature blocked?").
  • Ready-made AccessControlView debug screen (iOS) — list every subscription and feature, force overrides, drill into evaluation details.

Requirements

  • iOS 18+
  • macOS 15+ (debug screens are iOS-only)
  • Swift 6.3+ (Xcode 26 or later)

Installation

Xcode

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

Package.swift

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

Quick Start

import ForgeAccess
enumSubscription:String,SubscriptionProtocol{case pro, gold
vardescription:String{ rawValue.capitalized }}enumFeature:String,FeatureProtocol{case advancedExport, basicSearch, cloudSync
typealiasSubscription=AppSubscriptionvarname:String{ rawValue }varavailableInSubscriptions:Set<Subscription>{switchself{case.advancedExport:[.pro,.gold]case.cloudSync:[.gold]case.basicSearch:[] // free; default policy decides
}}vardescription:String{ name }}structMyStatusAdapter:SubscriptionStatusProviding{func cachedActive()->Set<Subscription>{ /* read from cache */ []}func refreshActive()asyncthrows->Set<Subscription>{ /* refresh */ []}}letaccess=ForgeAccess<Subscription,Feature>(configuration:.init(
status:MyStatusAdapter(),
defaultPolicy:.deny
))if access.isEnabled(.advancedExport){
// …
}

Subscription status

The host app supplies subscriptions via SubscriptionStatusProviding:

  • cachedActive() — synchronous snapshot used for in-view feature gates. Must not block.
  • refreshActive() — async refresh from the source of truth.

ForgeAccess never assumes a payment SDK — write a thin adapter over RevenueCat, StoreKit 2, your backend, or a hard-coded value. For previews and tests, use StaticSubscriptionStatusProvider(active: [.pro]).

Overrides

Two channels, each evaluated in provider order — the first .on/.off wins:

ChannelAffectsBuilt-in providers
SubscriptionThe effective active subscription setPersistedSubscriptionOverrideStore, InMemorySubscriptionOverrideStore, TimeFrameSubscriptionOverrideProvider
FeatureThe feature decision directly (short-circuits subscription check)PersistedFeatureOverrideStore, InMemoryFeatureOverrideStore, TimeFrameFeatureOverrideProvider

The persisted store for each channel is wired in by default and evaluated first. Add more providers via additionalFeatureOverrides / additionalSubscriptionOverrides.

Custom feature override provider

structRemoteConfigFeatureOverrides:FeatureOverrideProviderProtocol{letname="Remote Config"func isFeatureOverridden(_ feature:Feature)->OverrideState{
// .on / .off / .unchanged
.unchanged
}}letaccess=ForgeAccess<Subscription,Feature>(configuration:.init(
status:MyStatusAdapter(),
additionalFeatureOverrides:[AnyFeatureOverrideProvider(RemoteConfigFeatureOverrides())]))

Time-frame override

Useful for trials, launch promos, or sunset windows:

lettrial= TimeFrameSubscriptionOverrideProvider<Subscription>{ sub inguard sub ==.pro else{returnnil}return.init(state:.on, start: signupDate, end: signupDate.addingTimeInterval(14*86400))}letaccess=ForgeAccess<Subscription,Feature>(configuration:.init(
status:MyStatusAdapter(),
additionalSubscriptionOverrides:[AnySubscriptionOverrideProvider(trial)]))

Debug UI (iOS)

AccessControlView lists every subscription and feature, surfaces real vs. effective state, and lets the user force overrides via the persisted stores. Wrap it in a NavigationStack and present from a dev menu:

.sheet(isPresented: $showDevMenu){NavigationStack{AccessControlView(access: access)}}

Tapping a feature opens FeatureEvaluationDetailsView — a full evaluation trace including which override provider decided.

Evaluation details

access.evaluate(feature) returns AccessProviderEvaluation with:

  • finalDecision
  • decidedByOverride + decidingOverride
  • activeSubscriptions, requiredSubscriptions
  • overrideStates — every provider's decision in evaluation order

Useful for analytics or in-app debug surfaces.

Architecture

ForgeAccess<Subscription, Feature>
├─ ActiveSubscriptionProvider ← cached/fresh, applies subscription overrides
│ └─ SubscriptionStatusProviding ← host-supplied (RevenueCat / StoreKit / server)
└─ AccessProvider ← evaluates feature, applies feature overrides

Feature overrides short-circuit the subscription check. If every feature-override provider returns .unchanged, the package falls back to the subscription rules and defaultPolicy.

The Forge Family

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

PackageDescription
ForgeCoreThread-safe primitives for iOS Swift packages.
ForgeInjectDependency injection with constructor and property wrapper support.
ForgeObserversReactive system observers — connectivity, lifecycle, keyboard, and more.
ForgeStorageType-safe key-value, file, and Keychain storage.
ForgeDBType-safe repository pattern and GRDB-backed SQLite persistence.
ForgeOrchestratorOrchestrate app flows — startup gates, data pipelines, and continuous monitors.
ForgePushPush notification management — permissions, tokens, and routing.
ForgeLocationLocation triggers — geofencing, significant changes, and visits.
ForgeBackgroundTasksBackground task scheduling and dispatch.
ForgeNetworkingTyped, async/await-first HTTP networking with auth, retry, and background transfers.
ForgeLogStructured logging with pluggable providers and a built-in inspector UI.
ForgeAccessSubscription-aware feature gating with override channels and debug UI.

License

ForgeAccess is released under the MIT License. See LICENSE.

About

Subscription-aware feature gating for iOS with override channels and a debug UI.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages