SwiftUI Integration
The TogglySwiftUI package provides SwiftUI-native components for feature flag management.
Installation
Add TogglySwiftUI to your target:
.product(name: "TogglySwiftUI", package: "Toggly.FeatureManagement")
Property Wrappers
@FeatureFlag
The @FeatureFlag property wrapper provides reactive access to a single feature flag.
import TogglySwiftUI
struct MyView: View {
@FeatureFlag("new-feature") var isNewFeatureEnabled
@FeatureFlag("beta-mode", defaultValue: true) var isBetaMode
var body: some View {
VStack {
if isNewFeatureEnabled {
NewFeatureView()
}
if isBetaMode {
BetaBadge()
}
}
}
}
The view automatically re-renders when the feature flag changes.
@FeatureGate
The @FeatureGate property wrapper evaluates multiple feature flags with AND/OR logic.
import TogglySwiftUI
struct PremiumView: View {
// All features must be enabled
@FeatureGate(["premium-tier", "new-features"], requirement: .all)
var hasPremiumFeatures
// At least one feature must be enabled
@FeatureGate(["discount-banner", "promo-code"], requirement: .any)
var showPromotion
var body: some View {
VStack {
if hasPremiumFeatures {
PremiumContent()
}
if showPromotion {
PromotionBanner()
}
}
}
}
Negation
Use the negate parameter to invert the result:
// Show when feature is OFF
@FeatureGate(["maintenance-mode"], negate: true) var isNotInMaintenance
View Modifiers
.featureFlag()
Show a view only when a feature is enabled:
struct ContentView: View {
var body: some View {
VStack {
// Only visible when "new-header" is enabled
HeaderView()
.featureFlag("new-header")
MainContent()
// Only visible when "new-footer" is enabled
FooterView()
.featureFlag("new-footer")
}
}
}
.featureFlag(..., negate: true)
Show a view only when a feature is disabled:
struct ContentView: View {
var body: some View {
VStack {
// Legacy banner shown when "new-banner" is OFF
LegacyBanner()
.featureFlag("new-banner", negate: true)
// New banner shown when "new-banner" is ON
NewBanner()
.featureFlag("new-banner")
}
}
}
Paired .featureFlag() modifiers
Prefer separate modifiers (or FeatureView + negate) for on and off paths:
struct ContentView: View {
var body: some View {
NewCheckoutFlow()
.featureFlag("new-checkout")
LegacyCheckoutFlow()
.featureFlag("new-checkout", negate: true)
}
}
FeatureView("new-checkout") {
NewCheckoutFlow()
}
FeatureView("new-checkout", negate: true) {
LegacyCheckoutFlow()
}
FeatureView
The FeatureView component provides declarative conditional rendering. Set negate: true to render when the feature is off — the same idea as .NET <feature negate>.
Basic Usage
import TogglySwiftUI
struct ContentView: View {
var body: some View {
VStack {
// Only renders when enabled
FeatureView("welcome-banner") {
WelcomeBanner()
}
// On and off paths
FeatureView("dark-mode") {
DarkModeSettings()
}
FeatureView("dark-mode", negate: true) {
Text("Dark mode coming soon!")
}
}
}
}
FeatureGateView
For multiple feature flag evaluation:
struct ContentView: View {
var body: some View {
// All features must be enabled
FeatureGateView(["premium", "new-ui"], requirement: .all) {
PremiumNewUI()
}
FeatureGateView(["premium", "new-ui"], requirement: .all, negate: true) {
StandardUI()
}
// Any feature must be enabled
FeatureGateView(["promo-a", "promo-b"], requirement: .any) {
PromotionBanner()
}
}
}
Environment Integration
TogglyProvider
Wrap your app with TogglyProvider to make the service available throughout the view hierarchy:
import TogglySwiftUI
@main
struct MyApp: App {
init() {
Toggly.configure(config: TogglyConfig(
appKey: "your-app-key",
environment: "Production"
))
}
var body: some Scene {
WindowGroup {
TogglyProvider {
ContentView()
}
}
}
}
Custom Service Instance
Pass a specific service instance using the environment:
struct ContentView: View {
let customService = TogglyService(config: TogglyConfig(
featureDefaults: ["test-feature": true]
))
var body: some View {
ChildView()
.togglyService(customService)
}
}
Accessing the Service
Access the Toggly service from the environment:
struct MyView: View {
@Environment(\.togglyService) var togglyService
var body: some View {
Button("Refresh") {
Task {
await togglyService?.refresh()
}
}
}
}
Using with Specific Service
All property wrappers and views accept an optional service parameter:
struct TestView: View {
let testService: TogglyService
// Use specific service instead of shared instance
@FeatureFlag("test-feature", service: testService) var isEnabled
var body: some View {
FeatureView("another-feature", service: testService) {
TestContent()
}
}
}
Entity context
Pass the entity per view, not on setIdentity. See Entity & page context. Clients do not register schemas.
FeatureView("OrderBadge", context: order, contextKind: "Order") {
BadgeView()
}
Missing entity context fails closed.
Best Practices
1. Initialize Early
Initialize Toggly in your app's init() or AppDelegate:
@main
struct MyApp: App {
init() {
Toggly.configure(config: TogglyConfig(appKey: "your-key"))
Task { await Toggly.shared.initialize() }
}
}
2. Use Feature Defaults
Provide defaults for a smooth offline experience:
TogglyConfig(
appKey: "your-key",
featureDefaults: [
"critical-feature": true,
"experimental-feature": false
]
)
3. Handle Loading States
Use showFeatureDuringEvaluation to control loading behavior:
TogglyConfig(
appKey: "your-key",
showFeatureDuringEvaluation: false // Hide content until loaded
)
4. Organize Feature Keys
Use constants for feature keys:
enum Features {
static let newDashboard = "new-dashboard"
static let darkMode = "dark-mode"
static let betaFeatures = "beta-features"
}
struct MyView: View {
@FeatureFlag(Features.newDashboard) var isNewDashboard
}