{"id":16901914,"url":"https://github.com/andybons/abvariants","last_synced_at":"2026-05-16T21:36:25.056Z","repository":{"id":24942851,"uuid":"28360434","full_name":"andybons/ABVariants","owner":"andybons","description":"Experiments/Mods system for iOS and OS X","archived":false,"fork":false,"pushed_at":"2015-03-04T21:32:19.000Z","size":320,"stargazers_count":0,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-15T00:35:53.951Z","etag":null,"topics":["ab-testing","feature-flags","objective-c"],"latest_commit_sha":null,"homepage":"","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andybons.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-22T21:33:30.000Z","updated_at":"2022-07-01T08:17:36.000Z","dependencies_parsed_at":"2022-08-23T08:51:08.157Z","dependency_job_id":null,"html_url":"https://github.com/andybons/ABVariants","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andybons%2FABVariants","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andybons%2FABVariants/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andybons%2FABVariants/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andybons%2FABVariants/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andybons","download_url":"https://codeload.github.com/andybons/ABVariants/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244627460,"owners_count":20483804,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ab-testing","feature-flags","objective-c"],"created_at":"2024-10-13T18:02:29.378Z","updated_at":"2026-05-16T21:36:24.993Z","avatar_url":"https://github.com/andybons.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"Variants [![Build Status](https://travis-ci.org/andybons/ABVariants.svg?branch=master)](https://travis-ci.org/andybons/ABVariants)\n========\nExperiments/Mods system for iOS and OS X\n\nCan be used for:\n+ A/B testing\n+ Experimental features\n+ Trusted tester groups\n+ Gradual feature rollouts\n\nThis README details the Cocoa implementation of Variants. For general background, see [the general README](https://github.com/Medium/variants/).\n\n## Detailed Design\n\nFlag and Variant definitions can be defined in a JSON file that can be loaded by an `ABRegistry` object that manages the sanity and evaluation of each condition.\n\nExample\n```json\n{\n  \"flag_defs\": [\n    {\n      \"flag\": \"ab_test\",\n      \"base_value\": false\n    }\n  ],\n  \"variants\": [\n    {\n      \"id\": \"FeatureABTest\",\n      \"conditions\": [\n        {\n          \"type\": \"RANDOM\",\n          \"value\": 0.5\n        }\n      ],\n      \"mods\": [\n        {\n          \"flag\": \"ab_test\",\n          \"value\": true\n        }\n      ]\n    }\n  ]\n}\n```\n\nIn the above example, a flag called \"ab_test\" is defined, and behavior surrounding how that flag will be evaluated is defined by the variant definition below it. If the condition defined by the variant is met, then the associated mods will be realized (the flag \"ab_test\" will evaluate to true). The variant is using the built-in RANDOM condition type that will evaluate its result by checking whether a random number between 0.0 and 1.0 is less than or equal to the given value (0.5 in this case). So, in practice, a call to `[registry flagValueWithName:@\"ab_test\"]` will return an `NSNumber` with a `boolValue` of `YES` 50% of the time.\n\nBut say you don't want to use the built-in condition types...\n\nAnother example\n```json\n{\n  \"flag_defs\": [\n    {\n      \"flag\": \"enable_new_hotness_feature\",\n      \"base_value\": false\n    }\n  ],\n  \"variants\": [\n    {\n      \"id\": \"EnableNewHotnessFeature\",\n      \"conditions\": [\n        {\n          \"type\": \"CUSTOM\",\n          \"values\": [\n            \"andybons\",\n            \"pupius\",\n            \"guitardave24\"\n          ]\n        }\n      ],\n      \"mods\": [\n        {\n          \"flag\": \"enable_new_hotness_feature\",\n          \"value\": true\n        }\n      ]\n    }\n  ]\n}\n```\n\nNow, there is no built-in condition type called CUSTOM, so when the above config is loaded, the passed `NSError` will be populated. We need to define how a CUSTOM condition should be evaluated _before_ the above config is loaded.\n\n```objective-c\n[self.registry registerConditionTypeWithID:@\"CUSTOM\"\n    specBlock:^ABConditionEvaluator(id\u003cNSCopying\u003e value) {\n        return ^BOOL(id\u003cNSCopying\u003e context) {\n            return [((NSDictionary *)context)[@\"password\"] isEqualToString:(NSString *)value];\n        };\n    } error:\u0026error];\n```\n\nThe above code evaluates the CUSTOM condition by checking to see if the value of the \"username\" key in the passed in context object is present in the values passed when the variant is constructed. Here are a couple examples of getting the flag value:\n\n```objective-c\nNSNumber hasAccess = [[ABRegistry defaultRegistry] flagValueWithName:@\"enable_new_hotness_feature\"\n                                                             context:@{@\"username\": @\"andybons\"}];\n// hasAccess.boolValue == YES\n\nNSNumber hasAccess = [[ABRegistry defaultRegistry] flagValueWithName:@\"enable_new_hotness_feature\"\n                                                             context:@{@\"username\": @\"tessr\"}];\n// hasAccess.boolValue == NO\n```\n\nTake a look at the unit tests for a working example.\n\n# Using Variants\n\n## CocoaPods\n\n```shell\npod install Variants\n```\n\n## Import\n```objective-c\n#import \u003cVariants/Variants.h\u003e\n```\n\n## Author\n\n[Andrew Bonventre](https://github.com/andybons)\n\nVariants was originally created by [David Byttow](https://github.com/guitardave24) for [Medium](https://github.com/Medium/variants)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandybons%2Fabvariants","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandybons%2Fabvariants","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandybons%2Fabvariants/lists"}