FeatureFlag is a Swift package that provides an easy and efficient way to manage feature flags in your application using macros introduced in Swift 5.9. This library allows developers to define feature flags as enums, persist their state with UserDefaults, and observe changes using Combine publishers.
- Declarative feature flag management using macros.
- Automatic integration with
UserDefaultsfor state persistence. - Combine-based
AnyPublisherto observe feature flag changes. - Swift 5.9 macro support for clean, boilerplate-free code generation.
- Swift: 5.9+
- iOS: 13.0+ / macOS: 10.15+ / tvOS: 13.0+ / watchOS: 6.0+
- Xcode: 15.0+
Add the following to your Package.swift:
.package(url: "https://github.com/Keitaro0226/FeatureFlagMacro.git", from: "0.1.3")Or add the repository URL directly to Xcode:
- Go to File > Add Packages.
- Enter
https://github.com/Keitaro0226/FeatureFlagMacro.git. - Follow the prompts to add the package to your project.
import Combine
import FeatureFlagMacroAnnotate your enum with @FeatureFlag to automatically generate the necessary boilerplate for fetching, setting, and observing feature flags:
@FeatureFlag
@MainActor
public enum FeatureFlag: String, Codable {
case enabled
case disabled
public static var defaultValue: Self { .disabled }
}// Set the feature flag
FeatureFlag.set(.enabled)
// Fetch the current feature flag
let currentFlag = FeatureFlag.fetch()
print(currentFlag) // Output: enabledYou can observe changes to the feature flag using Combine's AnyPublisher:
import Combine
@MainActor
final class RootScreenViewModel: ObservableObject {
@Published var featureFlag: FeatureFlag = .disabled
private var cancellables = Set<AnyCancellable>()
public init() {
bindFeatureFlagPublisher()
}
private func bindFeatureFlagPublisher() {
FeatureFlag.publisher
.assign(to: &$featureFlag)
}
}You can now reactively update your UI based on the current feature flag:
import SwiftUI
struct ContentView: View {
@StateObject private var viewModel = RootScreenViewModel()
var body: some View {
VStack {
Text("Feature is \(viewModel.featureFlag.rawValue)")
}
}
}When you annotate an enum with @FeatureFlagMacro, the following code is automatically generated:
fetch()to retrieve the current feature flag value fromUserDefaults.set(_:)to update the feature flag value and publish updates.- A Combine publisher
publisherto observe feature flag changes in real-time.
Example of generated code:
private static let subject = PassthroughSubject<FeatureFlag, Never>()
public static var publisher: AnyPublisher<FeatureFlag, Never> {
subject.eraseToAnyPublisher()
}
public static func fetch() -> FeatureFlag {
let value = UserDefaults.standard.string(forKey: "featureflag")
return FeatureFlag(rawValue: value ?? FeatureFlag.defaultValue.rawValue) ?? .defaultValue
}
public static func set(_ value: FeatureFlag) {
UserDefaults.standard.set(value.rawValue, forKey: "featureflag")
subject.send(value)
}FeatureFlag comes with unit tests for macro expansion and helper extensions.
Run the tests using Xcode or Swift Package Manager:
swift testassertMacroExpansion(
"""
@FeatureFlag
@MainActor
enum FeatureFlag: String, Codable {
case enabled
case disabled
public static var defaultValue: Self { .disabled }
}
""",
expandedSource: """
@MainActor
enum FeatureFlag: String, Codable {
case enabled
case disabled
public static var defaultValue: Self { .disabled }
private static let subject = PassthroughSubject<FeatureFlag, Never>()
public static var publisher: AnyPublisher<FeatureFlag, Never> {
subject.eraseToAnyPublisher()
}
public static func fetch() -> FeatureFlag {
let value = UserDefaults.standard.string(forKey: "featureflag")
return FeatureFlag(rawValue: value ?? FeatureFlag.defaultValue.rawValue) ?? .defaultValue
}
public static func set(_ value: FeatureFlag) {
UserDefaults.standard.set(value.rawValue, forKey: "featureflag")
subject.send(value)
}
}
""",
macros: testMacros
)Contributions are welcome! If you'd like to improve FeatureFlag, please:
- Fork the repository.
- Create a new branch.
- Make your changes.
- Open a pull request.
Please include tests for any new functionality.
FeatureFlag is available under the MIT License. See the LICENSE file for more details.
This library leverages the power of Swift 5.9 macros to simplify feature flag management in Swift applications.