❇️ Feature flagging framework in Swift sauce

Overview

RealFlags

RealFlags makes it easy to configure feature flagsin your codebase.
It's designed for Swift and provides a simple and elegant abstraction layer over multiple providers you can query with your own priority.
It also comes with an handy UI tool to browse and alter values directly at runtime!

ImmobiliareLabs


Features | What You Get | Flags Browser & Editor | Tests | Documentation | Requirements | Installation | Powered Apps | Support & Contribute | License


Features Highlights

  • 💡 Simple & Elegant: Effectively describe and organize your own flags with a type-safe structure. It will abstract your implementation and consistently reduce the amount of code to manage your feature flags
  • 💡 Extensible: You can use one of the default data providers or create your own. We support Firebase Remote and Local configurations too
  • 💡 Complete: Feature Flags supports all primitive datatypes and complex objects: Int (and any numeric variant), String, Bool, Data, Date, URL, Dictionary, Array (values must conform to FlagProtocol), Optional Values and virtually any object conforms to Codable protocol!
  • 💡 Configurable: Enable, disable and customize features at runtime
  • 💡 Integrated UI Tool: the handy UI Tool allows you to customize and read flags at glance

What You Get

Our goal making RealFlags is to provide a type-safe abstract way to describe and query for feature flags.

The first step is to describe your collection of flags:

// Define the structure of your feature flags with type-safe properties!
struct UserFlags: FlagCollectionProtocol {
    @Flag(default: true, description: "Show social login options along native login form")
    var showSocialLogin: Bool
    
    @Flag(default: 0, description: "Maximum login attempts before blocking account")
    var maxLoginAttempts: Int
    
    @Flag(key: "rating_mode", default: "at_launch", description: "The behaviour to show the rating popup")
    var appReviewRating: String
}

This represent a type-safe description of some flags grouped in a collection. Each feature flags property is identified by the @Flag annotation. It's time load values for this collection; using a new FlagsLoader you will be able to load and query collection's values from one or more data provider:

// Allocate your own data providers
let localProvider = LocalProvider(localURL: fileURL)
let fbProvider = FirebaseRemoteProvider()

// Loader is the point for query values
let userFlagsLoader = FlagsLoader(UserFlags.self, // load flags definition
                                  providers: [fbProvider, localProvider]) // set providers

Now you can query values from userFlagsLoader by using the UserFlags structure (it suppports autocomplete and type-safe value thanks to @dynamicMemberLookup!).
Let me show it:

if userFlagsLoader.showSocialLogin { // query properties as type-safe with autocomplete!
    // do some stuff
}

Values are obtained respecting the order you have specified, in this case from Firebase Remote Config service then, when no value is found, into the local repository.
If no values are available the default value specified in @Flags annotation is returned.

This is just an overview of the library; if you want to know more follow the documentation below.

Flags Browser & Editor

RealFlags also comes with an handy tool you can use to browse and edit feature flags values directly in your client! It can be useful for testing purpose or allow product owners to enable/disable and verify features of the app.

Checkout the doc for more infos!

Tests

RealFlags includes an extensive collection of unit tests: you can found it into the Tests directory.

Documentation

The following documentation describe detailed usage of the library.

Requirements

RealFlags can be installed in any platform which supports Swift 5.4+ including Windows and Linux. On Apple platform the following configuration is required:

  • iOS 12+
  • Xcode 12.5+
  • Swift 5.4+

Installation

To use RealFlags in your project you can use Swift Package Manager (our primary choice) or CocoaPods.

Swift Package Manager

Aadd it as a dependency in a Swift Package, add it to your Package.swift:

dependencies: [
    .package(url: "https://github.com/immobiliare/RealFlags.git", from: "1.0.0")
]

And add it as a dependency of your target:

targets: [
    .target(name: "MyTarget", dependencies: [
        .product(name: "https://github.com/immobiliare/RealFlags.git", package: "RealFlags")
    ])
]

In Xcode 11+ you can also navigate to the File menu and choose Swift Packages -> Add Package Dependency..., then enter the repository URL and version details.

CocoaPods

RealFlags can be installed with CocoaPods by adding pod 'RealFlags' to your Podfile.

pod 'RealFlags'

Powered Apps

RealFlags was created by the amazing mobile team at ImmobiliareLabs, the Tech dept at Immobiliare.it, the first real estate site in Italy.
We are currently using RealFlags in all of our products.

If you are using RealFlags in your app drop us a message, we'll add below.

Indomio

Support

Made with ❤️ by ImmobiliareLabs and Contributors

We'd love for you to contribute to RealFlags!
If you have any questions on how to use RealFlags, bugs and enhancement please feel free to reach out by opening a GitHub Issue.

License

RealFlags is licensed under the MIT license.
See the LICENSE file for more information.

Comments
  • Providers not respecting order defined

    Providers not respecting order defined

    Bug Report

    | Q | A | | ------------------- | ------ | | BC Break | no | | Version | 1.3.1 |

    Summary

    I'm setting providers: [FeatureFlagLocalProvider(), FirebaseRemoteProvider()]. for this property

    default: True localProvider: True firebase: False

    Current behavior

    The flag resolves as false, somehow deciding to choose firebase over the localProvider

    How to reproduce

    unsure, it seems like it should just work if you do [localProvider, firebaseProvider]

    Expected behavior

    the flag should resolve as True, respecting the local provider over the firebase provider

    opened by AndrewSB 3
  • Define resources explicitly for SPM package

    Define resources explicitly for SPM package

    I generated a Xcode project from the Swift Package and imported the generated Xcode into my workspace.

    I get the following error when compiling my workspace

    Screenshot 2022-04-10 at 3 19 56 PM

    My main use case is to be able to use this library together with tuist. The discussion about this use case is here

    opened by antranapp 2
  • Reset values for LocalProvider and per flag in every writable provider

    Reset values for LocalProvider and per flag in every writable provider

    Feature Request

    Allows reset of values inside a collection both via call and inside the browser of flags.
    This feature should be also implemented to reset a single flag.
    The scope is to reset any writable provider (ie. for LocalProvider it will remove the dictionary serialized file) and so on.

    | Q | A |------------ | ------ | New Feature | yes | BC Break | no

    enhancement 
    opened by malcommac 1
  • Give the opportunity to alter the default value of a Flag

    Give the opportunity to alter the default value of a Flag

    Feature Request

    Give the opportunity to alter the default value of a Flag.
    Sometimes you may need to alter the default value specified inside the property wrapper signature. This happens if you want to define a single collection file used in several targets of your project where one or more flags have different default values.
    In this case you can avoid to replicate the same collection blueprint but just change the default value.

    A new call setDefaultValue() is therefore provided to allows this change.

    | Q | A |------------ | ------ | New Feature | yes | BC Break | no

    enhancement 
    opened by malcommac 1
  • FlagProvider's flagValue(from:fallback) function ignores fallback setting

    FlagProvider's flagValue(from:fallback) function ignores fallback setting

    Feature Request

    By calling flagValue(from:fallback) function from any FlagLoader concrete implementation value obtained ignores the fallback parameter which means "return the default value if nothing found on any provider".
    Fallback is always returned when loader's reference is not available, while in case everything is setup correctly and no values were found the returned value is nil instead of defaultValue.

    | Q | A |------------ | ------ | New Feature | no | BC Break | yes

    bug 
    opened by malcommac 0
  • Release 1.2.1

    Release 1.2.1

    Added the following features:

    • #15 Give the opportunity to alter the default value of a Flag by using setDefaultValue() (example: loader.nested.$flagInt.setDefault(100, providers: [...]))
    • #16 Reset values for LocalProvider and per flag in every writable provider (using resetValue() for Flag and resetAllData for LocalProvider.
    • #15 Added more unit tests
    opened by malcommac 0
  • Make the Flags Browser more user friendly for general purpose users

    Make the Flags Browser more user friendly for general purpose users

    Feature Request

    While it's more complete, especially for debug purpose, the flags browser interface is far from being simple. Especially if you want to expose it to your non-tech managers the resulting experience is really poor.

    We'll work on the following issues:

    • [x] Simplify the initial flags collection list showing only the relevant informations (without describing the type of collection but exposing a metadata section event for FlagsCollection type).
    • [x] Allowing in-place toggle for the most common used data type: the boolean. When you open a flags collection you should be able to change the value of boolean flags directly from list. This should works only if flag has one or more writable providers (the toggle operation will affects all the writable data providers like the LocalProvider).
    • [x] Show relevant errors when set the value of a flag

    | Q | A |------------ | ------ | New Feature | yes | BC Break | no

    The following screenshots describe the expected result:

    Screenshot 2022-01-22 at 13 19 21 Screenshot 2022-01-22 at 13 19 16 enhancement 
    opened by malcommac 0
  • LocalProvider fail to throw error if something went wrong saving local flags

    LocalProvider fail to throw error if something went wrong saving local flags

    Bug Report

    LocalProvider class fails to throw errors when saving internal flags dictionary inside a local file making the error catching really difficult.

    | Q | A | | ------------------- | ------ | | BC Break | yes | | Version | 1.1.0 |

    How to reproduce

    Just allocate a new LocalProvider pointing to an URL of a directory instead of a file.

    Expected behavior

    public func setValue<Value>(_ value: Value?, forFlag key: FlagKeyPath) throws -> Bool where Value: FlagProtocol support throws but the saveToDisk() function does not throw, instead it uses optional throw via try?.

    Just remove it to fix the issue.

    bug 
    opened by malcommac 0
  • wrappedTypeFromOptionalType() function in FirebaseRemoteProvider works only with optional type, fails with non-optional

    wrappedTypeFromOptionalType() function in FirebaseRemoteProvider works only with optional type, fails with non-optional

    Bug Report

    FirebaseRemoteProvider's wrappedTypeFromOptionalType() function does not work correctly with non optional type and fails returning nil values even for available flags. This causes wrong behaviour by querying remote provider for set values.

    Fix

    Fix includes fallback value for wrappedTypeFromOptionalType which allows us to return the unwrapped value correctly:

    public func wrappedTypeFromOptionalType(_ type: Any.Type) -> Any.Type? {
        (type as? OptionalProtocol.Type)?.wrappedType ?? type
    }
    
    bug 
    opened by malcommac 0
  • Bundle for FlagsControllerBrowser's storyboard failed to instantiate via CocoaPods

    Bundle for FlagsControllerBrowser's storyboard failed to instantiate via CocoaPods

    Bug Report

    Bundle is currently loaded by getting Bundle(for: Self.self) in extension of Bundle inside the package.
    However Bundle's path is set to:

    <AppName>.app/Frameworks/RealFlags.framework/RealFlags

    but now the bundle is inside RealFlags.bundle.

    How to reproduce

    Using the package via CocoaPods and instantiate a new controller via the default create() of the controller.

    Fix

    Allows the bundle function to search inside the package itself if generated:

    extension Bundle {
        
        private static let internalBundle = Bundle(for: FlagsBrowserController.self)
        
        internal static var libraryBundle: Bundle {
            #if SWIFT_PACKAGE
            Bundle.module
            #else
            [
                Bundle(url: internalBundle.bundleURL.appendingPathComponent("RealFlags.bundle")),
                internalBundle
            ].lazy.compactMap({ $0 }).first ?? Bundle.main
            #endif
        }
        
    }
    
    bug 
    opened by malcommac 0
  • Support for Computed value in annotated feature flag

    Support for Computed value in annotated feature flag

    This PR add support for computed value for annotated @Flag properties.
    This feature allows you to define a closure function which may return a custom value for an annotated flag.
    If value returned from callback is not nil this is the value of the flag and no other checks are made.
    If the value returned is nil the flow continues by asking to any provider specified (in order) and eventually returning the fallback return value.

    This is an example:
    Suppose you have a feature flag called hasPublishButton: it should return true only when the app's language is set to it. This is how we should define this behaviour:

    public struct MiscFlags: FlagCollectionProtocol {
    
        // MARK: - Flags
    
        @Flag(default: false, computedValue: MiscFlags.computedPublishButton, description: "")
        var hasPublishButton: Bool
                
        // MARK: - Computed Properties Functions
    
        public init() { }
    
        private static func computedPublishButton() -> Bool? {
            Language.main.code == "it"
        }
    }
    
    documentation enhancement 
    opened by malcommac 0
  • Firebase 10 support - Fork Link

    Firebase 10 support - Fork Link

    What problem are you facing?

    Ive added a fork to support firebase 10.0. This was needed to resolve captcha issues with the simulator and firebase auth captcha

    https://github.com/tr736-reclip/RealFlags

    Other Information

    No response

    opened by tr736-reclip 0
  • Add iOS 11 support

    Add iOS 11 support

    @malcommac Thanks for the great package. My app is currently supporting iOS 11+

    I see it's quite trivial to change the codebase a bit to support iOS 11. I don't see any reason why we shouldn't do that.

    Hope I haven't overseen something here 😬

    opened by antranapp 0
  • A Sample App would be nice

    A Sample App would be nice

    Feature Request

    | Q | A |------------ | ------ | New Feature | yes | RFC | no | BC Break | no

    Summary

    First of all, thank you for the great library. It looks modern and easy to use.

    I'd suggest to add a simple demo app into this library, so that other can easily start play around with the library immediately without initial setup.

    documentation enhancement 
    opened by antranapp 1
  • Better presentation for optional data inside the flags browser

    Better presentation for optional data inside the flags browser

    Feature Request

    At this time all the optional data is showed using Optional(Value) string inside the browser. This is just the debug description form coming from the Swift runtime. We should optimize it by showing the data if available eventually with a small icon to inform about the optional nature.

    | Q | A |------------ | ------ | New Feature | yes

    enhancement 
    opened by malcommac 0
  • Support for Optimizely data provider

    Support for Optimizely data provider

    Feature Request

    http://optimizely.com as Firebase Remote Config allows you to configure A/B test and feature flags. It should be a great new data provider to add.

    | Q | A |------------ | ------ | New Feature | yes | RFC | no | BC Break | no

    enhancement 
    opened by malcommac 0
Releases(1.4.1)
Owner
Immobiliare Labs
It took sharp skills in technology to establish Immobiliare.it as the leadering online service provider for the real estate market.
Immobiliare Labs
Returns true for all possible feature flags within the Twitter Mac app!

twitterinject Returns true for all possible feature flags within the Twitter Mac app! On Apple platforms, the default feature flags are present within

Spotlight 9 May 4, 2022
DGPreview - Make UIKit project enable preview feature of SwiftUI

DGPreview Make UIKit project enable preview feature of SwiftUI Requirements iOS

donggyu 5 Feb 14, 2022
Zip - A Swift framework for zipping and unzipping files. Simple and quick to use. Built on top of minizip.

Zip A Swift framework for zipping and unzipping files. Simple and quick to use. Built on top of minizip. Usage Import Zip at the top of the Swift file

Roy Marmelstein 2.3k Jan 3, 2023
💻 A fast and flexible O(n) difference algorithm framework for Swift collection.

A fast and flexible O(n) difference algorithm framework for Swift collection. The algorithm is optimized based on the Paul Heckel's algorithm. Made wi

Ryo Aoyama 3.3k Jan 4, 2023
RandomKit is a Swift framework that makes random data generation simple and easy.

RandomKit is a Swift framework that makes random data generation simple and easy. Build Status Installation Compatibility Swift Package Manager CocoaP

Nikolai Vazquez 1.5k Dec 29, 2022
A Swift micro-framework to easily deal with weak references to self inside closures

WeakableSelf Context Closures are one of Swift must-have features, and Swift developers are aware of how tricky they can be when they capture the refe

Vincent Pradeilles 72 Sep 1, 2022
An extensible monitoring framework written in Swift

XestiMonitors Overview Reference Documentation Requirements Installation CocoaPods Carthage Swift Package Manager Usage Core Location Monitors Core Mo

J. G. Pusey 271 Oct 5, 2022
Framework for easily parsing your JSON data directly to Swift object.

Server sends the all JSON data in black and white format i.e. its all strings & we make hard efforts to typecast them into their respective datatypes

Mukesh 11 Oct 17, 2022
Swift 3 framework for accessing data in Event Registry

Swift 3 framework for accessing data in Event Registry

Pavel Pantus 8 Nov 1, 2016
Cereal is a serialization framework built for Swift

Cereal is a serialization framework built for Swift. Its intended as a substitution for NSCoding to allow advanced Swift features. With NSCoding, you cannot encode or decode a Swift struct, enum, or generic class. Cereal solves this issue through deferred decoding with generics, and a protocol that doesn't depend on NSObjectProtocol.

Weebly 369 Sep 28, 2022
xxHash framework in Swift.

xxHash-Swift xxHash framework in Swift. Original xxHash algorithm created by Yann Collet. Currently only suppport XXH3-64. It currently points to the

Shuaihu 2 Oct 18, 2022
Vaccine is a framework that aims to make your apps immune to recompile-disease.

Vaccine Description Vaccine is a framework that aims to make your apps immune to recompile-disease. Vaccine provides a straightforward way to make you

Christoffer Winterkvist 298 Dec 23, 2022
Merges a given number of PDF files into one file using the PDFKit framework

Titanium iOS PDF Merge Merges a given number of PDF files into one file using the PDFKit framework Requirements iOS 11+ Titanium SDK 9+ API's Methods

Hans Knöchel 6 Jan 26, 2022
A set of utilities (vmcli + vmctl) for macOS Virtualization.framework

VMCLI A set of utilities to help you manage VMs with Virtualization.framework Installation Prerequisites macOS Big Sur (11+) XCode.app installed # mak

Yifan Gu 771 Dec 24, 2022
SwiftyUpdateKit is a framework for iOS and macOS.

SwiftyUpdateKit is a framework for iOS and macOS. This framework supports for a user to update your app when new app version is released on the App Store.

Hituzi Ando 4 Aug 24, 2022
Another Virtualization.framework demo project, with focus to iBoot (WIP)

Virtual iBoot Fun This is just another Virtualization.framework sample project (WIP), but with focus on iBoot (iOS/macOS/tvOS/etc. bootloader) For a m

john 119 Dec 7, 2022
This iOS framework allows settings to be in-app in addition to or instead of being in the Settings app.

InAppSettingsKit InAppSettingsKit (IASK) is an open source framework to easily add in-app settings to your iOS or Catalyst apps. Normally iOS apps use

Ortwin Gentz, FutureTap 3.1k Jan 2, 2023
A framework to provide logic designed to prompt users at the ideal moment for a review of your app/software

ReviewKit ReviewKit is a Swift package/framework that provides logic designed to prompt users at the ideal moment for a review of your app. At a basic

Simon Mitchell 25 Jun 7, 2022