├── VENExperimentsManagerTests ├── en.lproj │ └── InfoPlist.strings ├── VENTestExperiments.h ├── VENExperimentsManagerTests-Info.plist ├── testExperiments.plist └── VENExperimentsManagerTests.m ├── Sample App ├── ExperimentsManagerSampleApp │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── VENAppDelegate.h │ ├── main.m │ ├── ExperimentsManagerSampleApp-Prefix.pch │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── VENAppDelegate.m │ ├── ExperimentsManagerSampleApp-Info.plist │ ├── experiments.plist │ └── Base.lproj │ │ └── Main.storyboard └── ExperimentsManagerSampleApp.xcodeproj │ └── project.pbxproj ├── .travis.yml ├── VENExperimentsManager ├── VENExperimentsManager-Prefix.pch ├── SettingsViewController │ ├── VENExperimentsSettingsTVC.h │ ├── VENExperimentTableViewCell.h │ ├── VENExperimentTableViewCell.m │ ├── VENExperimentsSettingsTVC.m │ └── VENExperimentTableViewCell.xib ├── VENExperiment.h ├── VENExperimentsManager.h ├── VENExperiment.m └── VENExperimentsManager.m ├── .gitignore ├── LICENSE ├── VENExperimentsManager.podspec ├── VENExperimentsManager.xcodeproj ├── xcshareddata │ └── xcschemes │ │ └── VENExperimentsManager.xcscheme └── project.pbxproj └── README.md /VENExperimentsManagerTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Sample App/ExperimentsManagerSampleApp/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Sample App/ExperimentsManagerSampleApp/VENAppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface VENAppDelegate : UIResponder 4 | 5 | @property (strong, nonatomic) UIWindow *window; 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | before_install: 3 | - gem i cocoapods --no-ri --no-rdoc 4 | - brew uninstall xctool; brew install xctool --HEAD; 5 | xcode_project: VENExperimentsManager.xcodeproj 6 | xcode_scheme: VENExperimentsManager 7 | xcode_sdk: iphonesimulator 8 | -------------------------------------------------------------------------------- /VENExperimentsManager/VENExperimentsManager-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /Sample App/ExperimentsManagerSampleApp/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "VENAppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) 6 | { 7 | @autoreleasepool { 8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([VENAppDelegate class])); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /VENExperimentsManager/SettingsViewController/VENExperimentsSettingsTVC.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface VENExperimentsSettingsTVC : UITableViewController 4 | 5 | @property (nonatomic, strong) NSMutableArray *stableExperiments; 6 | @property (nonatomic, strong) NSMutableArray *unstableExperiments; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /Sample App/ExperimentsManagerSampleApp/ExperimentsManagerSampleApp-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Sample App/ExperimentsManagerSampleApp/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "40x40", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "60x60", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Sample App/ExperimentsManagerSampleApp/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /VENExperimentsManager/SettingsViewController/VENExperimentTableViewCell.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "VENExperimentsManager.h" 3 | 4 | @interface VENExperimentTableViewCell : UITableViewCell 5 | 6 | @property (nonatomic, strong) VENExperiment *experiment; 7 | @property (nonatomic, strong) IBOutlet UILabel *nameLabel; 8 | @property (nonatomic, strong) IBOutlet UILabel *detailsLabel; 9 | @property (nonatomic, strong) IBOutlet UISwitch *enabledSwitch; 10 | 11 | @property (nonatomic, strong) IBOutlet UILabel *optionsLabel; 12 | @property (nonatomic, strong) IBOutlet UITextField *optionsField; 13 | 14 | - (void)configureWithExperiment:(VENExperiment *)experiment; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /VENExperimentsManagerTests/VENTestExperiments.h: -------------------------------------------------------------------------------- 1 | #define VEN_EXPERIMENT_SOME_EXPERIMENT @"VEN_EXPERIMENT_SOME_EXPERIMENT" 2 | #define VEN_EXPERIMENT_AUTO_UPDATE @"VEN_EXPERIMENT_AUTO_UPDATE" 3 | #define VEN_EXPERIMENT_SMS_INVITES @"VEN_EXPERIMENT_SMS_INVITES" 4 | 5 | // Break out the payment screen options 6 | #define VEN_EXPERIMENT_PAYMENT_SCREEN @"VEN_EXPERIMENT_PAYMENT_SCREEN" 7 | #define VEN_EXPERIMENT_PAYMENT_SCREEN_DEFAULT @"VEN_EXPERIMENT_PAYMENT_SCREEN_DEFAULT" 8 | #define VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_1 @"VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_1" 9 | #define VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_2 @"VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_2" 10 | #define VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_3_DOESNT_EXIST @"VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_3_DOESNTEXIST" 11 | -------------------------------------------------------------------------------- /VENExperimentsManagerTests/VENExperimentsManagerTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.venmo.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Sample App/ExperimentsManagerSampleApp/VENAppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "VENAppDelegate.h" 2 | #import "VENExperimentsManager.h" 3 | 4 | @implementation VENAppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 7 | [VENExperimentsManager startExperimentsManagerWithPlistName:@"experiments"]; 8 | return YES; 9 | } 10 | 11 | - (void)applicationWillResignActive:(UIApplication *)application { 12 | 13 | } 14 | 15 | - (void)applicationDidEnterBackground:(UIApplication *)application { 16 | 17 | } 18 | 19 | - (void)applicationWillEnterForeground:(UIApplication *)application { 20 | 21 | } 22 | 23 | - (void)applicationDidBecomeActive:(UIApplication *)application { 24 | 25 | } 26 | 27 | - (void)applicationWillTerminate:(UIApplication *)application { 28 | 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Chris Maddern 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /VENExperimentsManager/VENExperiment.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface VENExperiment : NSObject 4 | 5 | @property (nonatomic, strong) NSString *identifier; 6 | @property (nonatomic, strong) NSString *name; 7 | @property (nonatomic, strong) NSString *details; 8 | @property (nonatomic, strong) NSDictionary *properties; 9 | @property (nonatomic, strong) NSDictionary *options; 10 | @property (nonatomic, strong) NSString *selectedOption; 11 | 12 | @property (nonatomic) BOOL enabled; 13 | @property (nonatomic) BOOL userEditable; 14 | @property (nonatomic) BOOL stable; 15 | @property (nonatomic) BOOL forceUpdate; 16 | 17 | 18 | - (instancetype)initWithIdentifier:(NSString *)identifier andConfigurationDictionary:(NSDictionary *)dictionary; 19 | 20 | 21 | - (NSDictionary *)dictionaryRepresentation; 22 | 23 | 24 | /** 25 | Get the human readable description of the currently selected options 26 | @return NSString of the description for the selected option. nil if there are no options or none are selected 27 | **/ 28 | - (NSString *)selectedOptionDescription; 29 | 30 | 31 | /** 32 | Determine whether the experiment supports options 33 | @return BOOL whether the experiment supports options 34 | **/ 35 | - (BOOL)supportsOptions; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /VENExperimentsManager.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "VENExperimentsManager" 3 | s.version = "1.0.1" 4 | s.summary = "Definition, management and control of experiments within an iOS app." 5 | s.description = <<-DESC 6 | VENExperimentsManager enables easy definition, management and control of experiments within an iOS app including the following: 7 | - Define experiments and 'experiment flag' code 8 | - Allow users to turn experiments on and off 9 | - Force turn-on or off experiments 10 | - Make experiments user editable or not 11 | - Let users know whether experiments are 'stable' or 'unstable' 12 | DESC 13 | 14 | s.homepage = "https://github.com/venmo/VENExperimentsManager" 15 | s.license = { :type => 'MIT', :file => 'LICENSE' } 16 | s.author = { "Venmo" => "engineers@venmo.com" } 17 | s.platform = :ios, '5.0' 18 | s.source = { :git => "https://github.com/venmo/VENExperimentsManager.git", :tag => "v#{s.version}" } 19 | s.source_files = 'VENExperimentsManager/**/*.{h,m}' 20 | s.resources = ["VENExperimentsManager/**/*.xib"] 21 | s.requires_arc = true 22 | end 23 | -------------------------------------------------------------------------------- /Sample App/ExperimentsManagerSampleApp/ExperimentsManagerSampleApp-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.venmo.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /VENExperimentsManagerTests/testExperiments.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | VEN_EXPERIMENT_AUTO_UPDATE 6 | 7 | VEN_EXPERIMENT_NAME 8 | Automatic Updates 9 | VEN_EXPERIMENT_ENABLED 10 | 11 | VEN_EXPERIMENT_USER_CHANGEABLE 12 | 13 | VEN_EXPERIMENT_FORCE_UPDATE 14 | 15 | VEN_EXPERIMENT_DETAILS 16 | Some details 17 | 18 | VEN_EXPERIMENT_SOME_EXPERIMENT 19 | 20 | VEN_EXPERIMENT_NAME 21 | Some Experiment Title 22 | VEN_EXPERIMENT_DETAILS 23 | Some details 2 24 | VEN_EXPERIMENT_ENABLED 25 | 26 | VEN_EXPERIMENT_USER_EDITABLE 27 | 28 | 29 | VEN_EXPERIMENT_SMS_INVITES 30 | 31 | VEN_EXPERIMENT_NAME 32 | SMS Invites 33 | VEN_EXPERIMENT_DEFAULT_VALUE 34 | 35 | VEN_EXPERIMENT_USER_CHANGEABLE 36 | 37 | VEN_EXPERIMENT_DETAILS 38 | Some details 3 39 | 40 | VEN_EXPERIMENT_PAYMENT_SCREEN 41 | 42 | VEN_EXPERIMENT_NAME 43 | New Payment Screen 44 | VEN_EXPERIMENT_DEFAULT_VALUE 45 | 46 | VEN_EXPERIMENT_USER_CHANGEABLE 47 | 48 | VEN_EXPERIMENT_DETAILS 49 | New payment experiments 50 | VEN_EXPERIMENT_DEFAULT_OPTION 51 | VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_1 52 | VEN_EXPERIMENT_OPTIONS 53 | 54 | VEN_EXPERIMENT_PAYMENT_SCREEN_DEFAULT 55 | Default payment screen 56 | VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_1 57 | Prototype 1 58 | VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_2 59 | Prototype 2 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Sample App/ExperimentsManagerSampleApp/experiments.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | VEN_EXPERIMENT_PAYMENT_SCREEN 6 | 7 | VEN_EXPERIMENT_NAME 8 | New Payment Screen 9 | VEN_EXPERIMENT_ENABLED 10 | 11 | VEN_EXPERIMENT_USER_EDITABLE 12 | 13 | VEN_EXPERIMENT_SHOULD_RANDOMIZE_DEFAULT 14 | 15 | VEN_EXPERIMENT_DETAILS 16 | New payment experiments 17 | VEN_EXPERIMENT_OPTIONS 18 | 19 | VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_1 20 | Prototype 1 21 | VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_2 22 | Prototype 2 23 | 24 | 25 | VEN_EXPERIMENT_SNOW 26 | 27 | VEN_EXPERIMENT_NAME 28 | Snowy Christmas 29 | VEN_EXPERIMENT_STABLE 30 | 31 | VEN_EXPERIMENT_ENABLED 32 | 33 | VEN_EXPERIMENT_USER_EDITABLE 34 | 35 | VEN_EXPERIMENT_DETAILS 36 | Add a snowy overlay to the whole Venmo app to get your Christmas vibe! 37 | 38 | VEN_EXPERIMENT_AUTO_UPDATE 39 | 40 | VEN_EXPERIMENT_DETAILS 41 | Automatically notifies you of available app updates for your current 'track'. This cannot be disabled on internal builds. 42 | VEN_EXPERIMENT_NAME 43 | Automatic Updates 44 | VEN_EXPERIMENT_ENABLED 45 | 46 | VEN_EXPERIMENT_STABLE 47 | 48 | VEN_EXPERIMENT_USER_EDITABLE 49 | 50 | VEN_EXPERIMENT_FORCE_UPDATE 51 | 52 | 53 | VEN_EXPERIMENT_SOME_EXPERIMENT 54 | 55 | VEN_EXPERIMENT_DETAILS 56 | This is an experiment which we don't want to talk about.. ;) 57 | VEN_EXPERIMENT_NAME 58 | Some Experiment 59 | VEN_EXPERIMENT_ENABLED 60 | 61 | VEN_EXPERIMENT_STABLE 62 | 63 | VEN_EXPERIMENT_USER_EDITABLE 64 | 65 | 66 | VEN_EXPERIMENT_MAINTENANCE_MODES 67 | 68 | VEN_EXPERIMENT_NAME 69 | Service Maintenance Mode 70 | VEN_EXPERIMENT_DETAILS 71 | Improved support for service error states 72 | VEN_EXPERIMENT_STABLE 73 | 74 | VEN_EXPERIMENT_ENABLED 75 | 76 | VEN_EXPERIMENT_USER_EDITABLE 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /VENExperimentsManager.xcodeproj/xcshareddata/xcschemes/VENExperimentsManager.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 52 | 53 | 54 | 55 | 61 | 62 | 64 | 65 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /VENExperimentsManager/VENExperimentsManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import "VENExperiment.h" 3 | 4 | extern NSString *const VENExperimentEnabledNotificationUserInfoKey; 5 | extern NSString *const VENExperimentOptionNotificationUserInfoKey; 6 | 7 | @interface VENExperimentsManager : NSObject 8 | 9 | @property (nonatomic) BOOL initialized; 10 | @property (nonatomic, strong) NSString *configurationFileName; 11 | 12 | #pragma mark - Initializers - 13 | + (void)startExperimentsManagerWithPlistName:(NSString *)plistName; 14 | 15 | #pragma mark - Experiment Management - 16 | 17 | + (NSArray *)allExperiments; 18 | + (VENExperiment *)experimentWithIdentifier:(NSString *)experimentIdentifier; 19 | 20 | 21 | /** 22 | Determines whether an experiment with a specified identifier is currently enabled 23 | @return BOOL representing whether the experiment is enabled 24 | @note This is a convenience method for [[[VENExperimentsManager sharedExperimentsManager] experimentWithIdentifier:experimentIdentifier] enabled] 25 | **/ 26 | + (BOOL)experimentIsEnabled:(NSString *)experimentIdentifier; 27 | 28 | 29 | /** 30 | Returns the selected option for an experiment which supports options. Returns nil otherwise. 31 | @return NSString with the key for the selected option for this experiment 32 | @note This will return a selectedOption whether or not the experiment is enabled 33 | **/ 34 | + (NSString *)selectedOptionForExperiment:(NSString *)experimentIdentifier; 35 | 36 | 37 | /** 38 | Resets all experiment states to those in the plist file in the bundle discarding any user overrides 39 | @warning This cannot be undone 40 | **/ 41 | + (void)deleteAllUserSettings; 42 | 43 | 44 | /** 45 | Determines whether experimentation is enabled 46 | @return BOOL indicating whether experimentation is enabled 47 | @note when experimentation is not enabled, experimentIsEnabled: will return NO for all experiments 48 | **/ 49 | + (BOOL)experimentationEnabled; 50 | 51 | 52 | /** 53 | Determines whether experimentation is enabled 54 | @param experimentIdentifier The experiment to set the enabled state for 55 | @param enabled The new state for the designated experiment 56 | **/ 57 | + (void)setExperimentWithIdentifier:(NSString *)experimentIdentifier isEnabled:(BOOL)enabled; 58 | 59 | 60 | /** 61 | Sets the selected option for an experiment that supports options. Does nothing if not. 62 | @param experimentIdentifier The experiment to set the enabled state for 63 | @param selectedOption The key for the selected experiment option 64 | **/ 65 | + (void)setSelectedOptionForExperimentWithIdentifier:(NSString *)experimentIdentifier 66 | selectedOption:(NSString *)selectedOption; 67 | 68 | /** 69 | Returns the name of the NSNotification for a given experiment identifier that is called whenever the enabled switch is toggled 70 | @return NSString NSNotification key to listen for when the enabled switch is toggled for the given experiment idenifier 71 | **/ 72 | + (NSString *)enabledChangedNotificationNameForIdentifier:(NSString *)identifier; 73 | 74 | 75 | /** 76 | Returns the name of the NSNotification for a given experiment identifier that is called whenever an experiment option is selected 77 | @return NSString NSNotification key to listen for when an option is selected for the given experiment identifier 78 | **/ 79 | + (NSString *)optionChangedNotificationNameForIdentifier:(NSString *)identifier; 80 | 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /VENExperimentsManager/SettingsViewController/VENExperimentTableViewCell.m: -------------------------------------------------------------------------------- 1 | #import "VENExperimentTableViewCell.h" 2 | 3 | @implementation VENExperimentTableViewCell 4 | 5 | - (void)configureWithExperiment:(VENExperiment *)experiment { 6 | self.experiment = experiment; 7 | 8 | self.nameLabel.text = experiment.name; 9 | self.detailsLabel.text = experiment.details; 10 | 11 | [self.enabledSwitch setOn:experiment.enabled animated:NO]; 12 | [self.enabledSwitch setEnabled:experiment.userEditable]; 13 | 14 | if (!experiment) { 15 | [self.enabledSwitch setOn:NO]; 16 | [self.enabledSwitch setEnabled:NO]; 17 | } 18 | 19 | self.detailsLabel.numberOfLines = 3; 20 | [self.detailsLabel sizeToFit]; 21 | 22 | if ([experiment supportsOptions]) { 23 | self.optionsLabel.alpha = 1; 24 | self.optionsField.alpha = 1; 25 | [self.optionsField setText:[self.experiment selectedOptionDescription]]; 26 | [self.optionsField setDelegate:self]; 27 | [[self.optionsField valueForKey:@"textInputTraits"] setValue:[UIColor clearColor] forKey:@"insertionPointColor"]; 28 | } 29 | else { 30 | self.optionsLabel.alpha = 0; 31 | self.optionsField.alpha = 0; 32 | } 33 | } 34 | 35 | 36 | - (IBAction)switchValueChanged:(id)sender { 37 | BOOL isON = [((UISwitch *)sender) isOn]; 38 | [VENExperimentsManager setExperimentWithIdentifier:self.experiment.identifier isEnabled:isON]; 39 | NSDictionary *userInfo = @{VENExperimentEnabledNotificationUserInfoKey: @(isON)}; 40 | [[NSNotificationCenter defaultCenter] postNotificationName:[VENExperimentsManager enabledChangedNotificationNameForIdentifier:self.experiment.identifier] 41 | object:nil 42 | userInfo:userInfo]; 43 | } 44 | 45 | 46 | - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { 47 | return 1; 48 | } 49 | 50 | 51 | - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { 52 | return [[self.experiment options] count]; 53 | } 54 | 55 | 56 | - (NSString *)pickerView:(UIPickerView *)pickerView 57 | titleForRow:(NSInteger)row 58 | forComponent:(NSInteger)component { 59 | return [[self.experiment options] objectForKey:[[[self.experiment options] allKeys] objectAtIndex:row]]; 60 | } 61 | 62 | 63 | - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { 64 | NSString *selectedOption = [[[self.experiment options] allKeys] objectAtIndex:row]; 65 | [VENExperimentsManager setSelectedOptionForExperimentWithIdentifier:self.experiment.identifier 66 | selectedOption:selectedOption]; 67 | [self.optionsField setText:[self.experiment selectedOptionDescription]]; 68 | NSDictionary *userInfo = @{VENExperimentOptionNotificationUserInfoKey: selectedOption}; 69 | [[NSNotificationCenter defaultCenter] postNotificationName:[VENExperimentsManager optionChangedNotificationNameForIdentifier:self.experiment.identifier] 70 | object:nil 71 | userInfo:userInfo]; 72 | 73 | [self.optionsField resignFirstResponder]; 74 | } 75 | 76 | 77 | -(BOOL)textFieldShouldBeginEditing:(UITextField *)textField { 78 | UIPickerView *pickerView = [[UIPickerView alloc] init]; 79 | pickerView.dataSource = self; 80 | pickerView.delegate = self; 81 | 82 | NSUInteger selectedIndex = [[[self.experiment options] allKeys] indexOfObject:self.experiment.selectedOption]; 83 | [pickerView selectRow:selectedIndex inComponent:0 animated:YES]; 84 | textField.inputView = pickerView; 85 | return YES; 86 | } 87 | 88 | @end 89 | -------------------------------------------------------------------------------- /Sample App/ExperimentsManagerSampleApp/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## VENExperimentsManager 2 | 3 | [![Build Status](https://travis-ci.org/venmo/VENExperimentsManager.svg?branch=master)](https://travis-ci.org/venmo/VENExperimentsManager) 4 | 5 | VENExperimentsManager enables easy definition, management and control of experiments within an iOS app including the following: 6 | - Define experiments and 'experiment flag' code 7 | - Allow users to turn experiments on and off 8 | - Force turn-on or off experiments 9 | - Make experiments user-editable or fixed 10 | - Let users know whether experiments are 'stable' or 'unstable' 11 | 12 | 13 | 14 | ### Usage 15 | 16 | ```objc 17 | [VENExperimentsManager startExperimentsManagerWithPlistName:@"testExperiments"]; 18 | ``` 19 | 20 | Then you can access experiments as follows.. 21 | ```objc 22 | VENExperiment *experiment = [VENExperimentsManager experimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT]; 23 | 24 | // Or if you just want to know if it's enabled 25 | [VENExperimentsManager experimentIsEnabled:VEN_EXPERIMENT_SOME_EXPERIMENT]; 26 | ``` 27 | 28 | Multi-option experiments (new in `v0.2.0`) can be used as follows.. 29 | 30 | ```objc 31 | NSString *selectedOptionForExperiment = [VENExperimentsManager selectedOptionForExperiment:VEN_EXPERIMENT_SOME_EXPERIMENT]; 32 | 33 | // Or you can get the experiment and inspect it.. 34 | VENExperiment *experiment = [VENExperimentsManager experimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT]; 35 | 36 | // Determine if the experiment has options 37 | BOOL hasOptions = [experiment supportsOptions]; 38 | 39 | // Get the selected option 40 | NSString *selectedOption = [experiment selectedOption]; 41 | 42 | // Get the readable description of the selected option 43 | [experiment selectedOptionDescription]; 44 | 45 | ``` 46 | 47 | The plist file defining experiments is a dictionary of experiment-identifier : experiment-definition dictionaries. It can be easily configured in XCode. 48 | 49 | 50 | 51 | 52 | A sample `experiments.plist` file can be found in the Sample Application. 53 | 54 | ### Key descriptions 55 | `VEN_EXPERIMENT_NAME` : `String` - The name of the experiment as it will appear in the Settings view 56 | 57 | `VEN_EXPERIMENT_STABLE` : `Boolean` - Whether the experiment is stable or not (grouped in Settings view) 58 | 59 | `VEN_EXPERIMENT_ENABLED` : `Boolean` - The default enabled state for the experiment 60 | 61 | `VEN_EXPERIMENT_USER_EDITABLE` : `Boolean` - Whether the user can change the state of the experiment 62 | 63 | `VEN_EXPERIMENT_FORCE_UPDATE` : `Boolean` - If this is YES, the experiment will be force-moved to it's default state every open. 64 | 65 | `VEN_EXPERIMENT_OPTIONS` : `Dictionary` - Key-Value pairs of `KEY` : `Readable description` for options for this experiment. 66 | 67 | `VEN_EXPERIMENT_DEFAULT_OPTION`: `String` - The key of the default option in the dictionary of options 68 | 69 | ### Experiment Settings View Controller 70 | 71 | The library also contains a basic Experiment Settings View Controller which you can present to allow users to enable and disable experiments. 72 | 73 | ```objc 74 | #import "VENExperimentsSettingsTVC.h" 75 | 76 | ... 77 | 78 | VENExperimentsSettingsTVC *settingsTVC = [[VENExperimentsSettingsTVC alloc] init]; 79 | [self.navigationController pushViewController:settingsTVC animated:YES]; 80 | ``` 81 | This will give an Experiment Settings screen that looks like this... 82 | 83 | 84 | ### Contributing 85 | 86 | We'd love to see your ideas for improving this library! The best way to contribute is by submitting a pull request. We'll do our best to respond to your patch as soon as possible. You can also submit a new Github issue if you find bugs or have questions. 87 | 88 | Please make sure to follow our general coding style and add test coverage for new features! 89 | 90 | 1. Fork it 91 | 2. Create your feature branch (`git checkout -b my-new-feature`) 92 | 3. Commit your changes (`git commit -am 'Added some feature'`) 93 | 4. Push to the branch (`git push origin my-new-feature`) 94 | 5. Create new Pull Request 95 | -------------------------------------------------------------------------------- /VENExperimentsManager/SettingsViewController/VENExperimentsSettingsTVC.m: -------------------------------------------------------------------------------- 1 | #import "VENExperimentsSettingsTVC.h" 2 | #import "VENExperimentsManager.h" 3 | #import "VENExperimentTableViewCell.h" 4 | 5 | @interface VENExperimentsSettingsTVC () 6 | 7 | @property (nonatomic, strong) NSIndexPath *lastSelectedIndexPath; 8 | 9 | @end 10 | 11 | @implementation VENExperimentsSettingsTVC 12 | 13 | - (void)viewDidLoad { 14 | [super viewDidLoad]; 15 | self.title = NSLocalizedString(@"Experiments", nil); 16 | 17 | NSArray *experiments = [VENExperimentsManager allExperiments]; 18 | self.stableExperiments = [NSMutableArray array]; 19 | self.unstableExperiments = [NSMutableArray array]; 20 | 21 | for (VENExperiment *experiment in experiments) { 22 | if (experiment.stable) { 23 | [self.stableExperiments addObject:experiment]; 24 | } 25 | else { 26 | [self.unstableExperiments addObject:experiment]; 27 | } 28 | } 29 | 30 | [self.tableView registerNib:[UINib nibWithNibName:@"VENExperimentTableViewCell" bundle:nil] forCellReuseIdentifier:@"VENExperimentTableViewCell"]; 31 | 32 | UIView *footerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 60)]; 33 | UILabel *label = [[UILabel alloc] init]; 34 | label.textAlignment = NSTextAlignmentCenter; 35 | label.textColor = [UIColor grayColor]; 36 | label.numberOfLines = 0; 37 | label.frame = CGRectMake(0, 10, CGRectGetWidth(self.view.frame), 60); 38 | label.font = [UIFont systemFontOfSize:14]; 39 | label.text = @"Most experiments will not take effect until application restart."; 40 | [footerView addSubview:label]; 41 | 42 | self.tableView.tableFooterView = footerView; 43 | [self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone]; 44 | } 45 | 46 | 47 | -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 48 | if ([[tableView indexPathForSelectedRow] isEqual:indexPath]) { 49 | VENExperiment *experiment; 50 | if (indexPath.section == 0) { 51 | experiment = self.stableExperiments[indexPath.row]; 52 | } 53 | else if (indexPath.section == 1) { 54 | experiment = self.unstableExperiments[indexPath.row]; 55 | } 56 | 57 | return 100 + ([experiment supportsOptions] ? 50 : 0); 58 | } 59 | return 44; 60 | } 61 | 62 | 63 | #pragma mark - Table view data source 64 | 65 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 66 | return [self.stableExperiments count] + [self.unstableExperiments count] > 0 ? 2 : 0; 67 | } 68 | 69 | 70 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 71 | switch (section) { 72 | case 0: 73 | return [self.stableExperiments count]; 74 | break; 75 | case 1: 76 | return [self.unstableExperiments count]; 77 | break; 78 | default: 79 | return 0; 80 | break; 81 | } 82 | } 83 | 84 | 85 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 86 | if (section == 0) { 87 | return @"Stable"; 88 | } 89 | return @"Unstable"; 90 | } 91 | 92 | 93 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 94 | static NSString *CellIdentifier = @"VENExperimentTableViewCell"; 95 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; 96 | 97 | VENExperiment *experiment; 98 | 99 | switch ([indexPath section]) { 100 | case 0: 101 | experiment = [self.stableExperiments objectAtIndex:[indexPath row]]; 102 | break; 103 | case 1: 104 | experiment = [self.unstableExperiments objectAtIndex:[indexPath row]]; 105 | break; 106 | default: 107 | break; 108 | } 109 | 110 | [((VENExperimentTableViewCell *) cell) configureWithExperiment:experiment]; 111 | 112 | return cell; 113 | } 114 | 115 | 116 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 117 | 118 | if ([indexPath isEqual:self.lastSelectedIndexPath]) { 119 | [self.view endEditing:YES]; 120 | [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; 121 | self.lastSelectedIndexPath = nil; 122 | } 123 | else { 124 | self.lastSelectedIndexPath = indexPath; 125 | } 126 | 127 | [tableView beginUpdates]; 128 | [tableView endUpdates]; 129 | } 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /VENExperimentsManager/VENExperiment.m: -------------------------------------------------------------------------------- 1 | #import "VENExperiment.h" 2 | 3 | #define VEN_KEY_EXPERIMENT_IDENTIFIER @"VEN_EXPERIMENT_IDENTIFIER" 4 | #define VEN_KEY_EXPERIMENT_NAME @"VEN_EXPERIMENT_NAME" 5 | #define VEN_KEY_EXPERIMENT_ENABLED @"VEN_EXPERIMENT_ENABLED" 6 | #define VEN_KEY_EXPERIMENT_USER_EDITABLE @"VEN_EXPERIMENT_USER_EDITABLE" 7 | #define VEN_KEY_EXPERIMENT_FORCE_UPDATE @"VEN_EXPERIMENT_FORCE_UPDATE" 8 | #define VEN_KEY_EXPERIMENT_STABLE @"VEN_EXPERIMENT_STABLE" 9 | #define VEN_KEY_EXPERIMENT_DETAILS @"VEN_EXPERIMENT_DETAILS" 10 | #define VEN_KEY_EXPERIMENT_DEFAULT_OPTION @"VEN_EXPERIMENT_DEFAULT_OPTION" 11 | #define VEN_KEY_EXPERIMENT_OPTIONS @"VEN_EXPERIMENT_OPTIONS" 12 | #define VEN_KEY_EXPERIMENT_SHOULD_RANDOMIZE_DEFAULT @"VEN_EXPERIMENT_SHOULD_RANDOMIZE_DEFAULT" 13 | 14 | @implementation VENExperiment 15 | 16 | - (instancetype)initWithIdentifier:(NSString *)identifier andConfigurationDictionary:(NSDictionary *)dictionary { 17 | self = [super init]; 18 | 19 | if (self) { 20 | 21 | if (identifier) { 22 | self.identifier = identifier; 23 | } 24 | else if (dictionary[VEN_KEY_EXPERIMENT_IDENTIFIER]) { 25 | self.identifier = dictionary[VEN_KEY_EXPERIMENT_IDENTIFIER]; 26 | } 27 | 28 | self.name = dictionary[VEN_KEY_EXPERIMENT_NAME] ?: @"Unknown"; 29 | self.details = dictionary[VEN_KEY_EXPERIMENT_DETAILS] ?: @"No experiment details"; 30 | self.options = dictionary[VEN_KEY_EXPERIMENT_OPTIONS] ?: @{}; 31 | 32 | NSString *selectedOption = dictionary[VEN_KEY_EXPERIMENT_DEFAULT_OPTION]; 33 | if (selectedOption && [self.options count] && self.options[selectedOption]) { 34 | self.selectedOption = selectedOption; 35 | } 36 | 37 | NSNumber *boolNumber = dictionary[VEN_KEY_EXPERIMENT_ENABLED]; 38 | if (boolNumber != nil) { 39 | self.enabled = [boolNumber boolValue]; 40 | } 41 | 42 | boolNumber = dictionary[VEN_KEY_EXPERIMENT_USER_EDITABLE]; 43 | if (boolNumber != nil) { 44 | self.userEditable = [boolNumber boolValue]; 45 | } 46 | 47 | boolNumber = dictionary[VEN_KEY_EXPERIMENT_FORCE_UPDATE]; 48 | if (boolNumber != nil) { 49 | self.forceUpdate = [boolNumber boolValue]; 50 | } 51 | 52 | boolNumber = dictionary[VEN_KEY_EXPERIMENT_STABLE]; 53 | if (boolNumber != nil) { 54 | self.stable = [boolNumber boolValue]; 55 | } 56 | 57 | if ([self.options count] && !self.selectedOption && [dictionary[VEN_KEY_EXPERIMENT_SHOULD_RANDOMIZE_DEFAULT] boolValue]) { 58 | u_int32_t randomUpperBound = (int)[self.options count]; 59 | unsigned long randomOptionIndex = arc4random_uniform(randomUpperBound); 60 | NSString *randomOption = [self.options allKeys][randomOptionIndex]; 61 | self.selectedOption = randomOption; 62 | } 63 | else if ([self.options count] && !self.selectedOption) { 64 | self.selectedOption = [[self.options allKeys] firstObject]; 65 | } 66 | } 67 | return self; 68 | } 69 | 70 | 71 | - (NSDictionary *)dictionaryRepresentation { 72 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 73 | 74 | dictionary[VEN_KEY_EXPERIMENT_NAME] = self.name; 75 | dictionary[VEN_KEY_EXPERIMENT_IDENTIFIER] = self.identifier; 76 | dictionary[VEN_KEY_EXPERIMENT_STABLE] = [NSNumber numberWithBool:self.stable]; 77 | dictionary[VEN_KEY_EXPERIMENT_FORCE_UPDATE] = [NSNumber numberWithBool:self.forceUpdate]; 78 | dictionary[VEN_KEY_EXPERIMENT_USER_EDITABLE] = [NSNumber numberWithBool:self.userEditable]; 79 | dictionary[VEN_KEY_EXPERIMENT_ENABLED] = [NSNumber numberWithBool:self.enabled]; 80 | 81 | 82 | if (self.details) { 83 | dictionary[VEN_KEY_EXPERIMENT_DETAILS] = self.details; 84 | } 85 | if (self.options) { 86 | dictionary[VEN_KEY_EXPERIMENT_OPTIONS] = self.options; 87 | } 88 | if (self.selectedOption) { 89 | dictionary[VEN_KEY_EXPERIMENT_DEFAULT_OPTION] = self.selectedOption; 90 | } 91 | 92 | return [NSDictionary dictionaryWithDictionary:dictionary]; 93 | } 94 | 95 | 96 | - (NSString *)selectedOptionDescription { 97 | if (self.selectedOption && [self.options count]) { 98 | return self.options[self.selectedOption]; 99 | } 100 | return nil; 101 | } 102 | 103 | 104 | - (void)setSelectedOption:(NSString *)selectedOption { 105 | if (selectedOption && [self.options count] && self.options[selectedOption]) { 106 | _selectedOption = selectedOption; 107 | } 108 | } 109 | 110 | 111 | - (BOOL)supportsOptions { 112 | return [self.options count] > 0 ? YES : NO; 113 | } 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /VENExperimentsManager/SettingsViewController/VENExperimentTableViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 38 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /VENExperimentsManager/VENExperimentsManager.m: -------------------------------------------------------------------------------- 1 | #import "VENExperimentsManager.h" 2 | #import "VENExperiment.h" 3 | 4 | #define VEN_KEY_LOCAL_EXPERIMENT_VALUES @"com.venmo.experiments.manager.overrides" 5 | 6 | NSString *const VENExperimentEnabledNotificationUserInfoKey = @"VENExperimentEnabledNotificationUserInfoKey"; 7 | NSString *const VENExperimentOptionNotificationUserInfoKey = @"VENExperimentOptionNotificationUserInfoKey"; 8 | 9 | static VENExperimentsManager *experimentsManager = nil; 10 | 11 | @interface VENExperimentsManager () {} 12 | 13 | @property (nonatomic, strong) NSMutableDictionary *experiments; 14 | 15 | - (VENExperiment *)experimentWithIdentifier:(NSString *)experimentIdentifier; 16 | - (BOOL)experimentIsEnabled:(NSString *)experimentIdentifier; 17 | 18 | @end 19 | 20 | @implementation VENExperimentsManager 21 | 22 | 23 | + (void)startExperimentsManagerWithPlistName:(NSString *)plistName { 24 | 25 | static dispatch_once_t once; 26 | 27 | dispatch_once(&once, ^ { 28 | experimentsManager = [[self alloc] init]; 29 | if (![experimentsManager startExperimentsManagerWithPlistName:plistName]) { 30 | experimentsManager = nil; 31 | } 32 | }); 33 | } 34 | 35 | 36 | + (VENExperimentsManager *)sharedExperimentsManager { 37 | return experimentsManager; 38 | } 39 | 40 | 41 | - (BOOL)startExperimentsManagerWithPlistName:(NSString *)plistName { 42 | self.initialized = NO; 43 | self.configurationFileName = plistName; 44 | 45 | self.experiments = [self initialStateForPlist:plistName]; 46 | 47 | if (!self.experiments || ![self.experiments count]) { 48 | return NO; 49 | } 50 | 51 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 52 | NSArray *localExperiments = [defaults objectForKey:[self persistenceKeyForCurrentConfigurationFile]]; 53 | 54 | for (NSDictionary *experimentDictionary in localExperiments) { 55 | VENExperiment *experiment = [[VENExperiment alloc] initWithIdentifier:nil 56 | andConfigurationDictionary:experimentDictionary]; 57 | VENExperiment *baseExp = self.experiments[experiment.identifier]; 58 | 59 | if (baseExp) { 60 | 61 | // Always update some basic fields 62 | experiment.userEditable = baseExp.userEditable; 63 | experiment.details = baseExp.details; 64 | experiment.options = baseExp.options; 65 | 66 | if (baseExp.forceUpdate) { 67 | experiment.forceUpdate = YES; 68 | experiment.enabled = baseExp.enabled; 69 | experiment.userEditable = baseExp.userEditable; 70 | experiment.selectedOption = baseExp.selectedOption; 71 | experiment.stable = baseExp.stable; 72 | } 73 | // If the new options make the previous selection invalid 74 | else if (!experiment.options[experiment.selectedOption]) { 75 | experiment.selectedOption = baseExp.selectedOption; 76 | } 77 | 78 | self.experiments[experiment.identifier] = experiment; 79 | } 80 | else { 81 | // Do not add experiments in UserDefaults which are not in the plist 82 | } 83 | } 84 | 85 | [self persistExperimentStates]; 86 | self.initialized = YES; 87 | 88 | return YES; 89 | } 90 | 91 | 92 | - (NSMutableDictionary *)initialStateForPlist:(NSString *)plistName { 93 | NSBundle *bundle = [NSBundle mainBundle]; 94 | bundle = [NSBundle bundleForClass:[self class]]; 95 | 96 | NSString *plistPath = [bundle pathForResource:plistName ofType:@"plist"]; 97 | NSDictionary *experimentsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath]; 98 | 99 | NSMutableDictionary *experimentObjects = [NSMutableDictionary dictionary]; 100 | for (NSString *experimentIdentifier in experimentsDictionary) { 101 | VENExperiment *experiment = [[VENExperiment alloc] initWithIdentifier:experimentIdentifier andConfigurationDictionary:experimentsDictionary[experimentIdentifier]]; 102 | if (experiment) { 103 | [experimentObjects setObject:experiment forKey:experimentIdentifier]; 104 | } 105 | } 106 | 107 | if (![experimentObjects count]) { 108 | return nil; 109 | } 110 | return experimentObjects; 111 | } 112 | 113 | 114 | - (void)persistExperimentStates { 115 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 116 | NSMutableArray *experimentDictionaries = [NSMutableArray array]; 117 | 118 | for (NSString *experimentIdentifier in [self.experiments allKeys]) { 119 | VENExperiment *experiment = self.experiments[experimentIdentifier]; 120 | [experimentDictionaries addObject:[experiment dictionaryRepresentation]]; 121 | } 122 | 123 | [defaults setObject:experimentDictionaries forKey:[self persistenceKeyForCurrentConfigurationFile]]; 124 | [defaults synchronize]; 125 | } 126 | 127 | 128 | - (NSArray *)allExperiments { 129 | return [self.experiments allValues]; 130 | } 131 | 132 | 133 | - (VENExperiment *)experimentWithIdentifier:(NSString *)experimentIdentifier { 134 | if (![[self class] experimentationEnabled]) { 135 | return nil; 136 | } 137 | 138 | VENExperiment *experiment = self.experiments[experimentIdentifier]; 139 | return experiment; 140 | } 141 | 142 | 143 | - (BOOL)experimentIsEnabled:(NSString *)experimentIdentifier { 144 | if (![[self class] experimentationEnabled]) { 145 | return NO; 146 | } 147 | 148 | return [[self experimentWithIdentifier:experimentIdentifier] enabled]; 149 | } 150 | 151 | 152 | - (void)setExperimentWithIdentifier:(NSString *)experimentIdentifier isEnabled:(BOOL)enabled { 153 | VENExperiment *experiment = [self experimentWithIdentifier:experimentIdentifier]; 154 | [experiment setEnabled:enabled]; 155 | self.experiments[experiment.identifier] = experiment; 156 | 157 | [self persistExperimentStates]; 158 | } 159 | 160 | 161 | - (void)setSelectedOptionForExperimentWithIdentifier:(NSString *)experimentIdentifier 162 | selectedOptions:(NSString *)selectedOption { 163 | VENExperiment *experiment = [self experimentWithIdentifier:experimentIdentifier]; 164 | experiment.selectedOption = selectedOption; 165 | self.experiments[experiment.identifier] = experiment; 166 | 167 | [self persistExperimentStates]; 168 | } 169 | 170 | 171 | - (void)deleteAllUserSettings { 172 | 173 | NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; 174 | [userDefaults removeObjectForKey:[self persistenceKeyForCurrentConfigurationFile]]; 175 | [userDefaults synchronize]; 176 | 177 | self.experiments = [self initialStateForPlist:self.configurationFileName]; 178 | } 179 | 180 | #pragma mark - Helper methods 181 | 182 | - (NSString *)persistenceKeyForCurrentConfigurationFile { 183 | return [NSString stringWithFormat:@"%@-%@", VEN_KEY_LOCAL_EXPERIMENT_VALUES, self.configurationFileName]; 184 | } 185 | 186 | 187 | #pragma mark - Static Accessors 188 | 189 | + (VENExperiment *)experimentWithIdentifier:(NSString *)experimentIdentifier { 190 | return [[self sharedExperimentsManager] experimentWithIdentifier:experimentIdentifier]; 191 | } 192 | 193 | + (BOOL)experimentIsEnabled:(NSString *)experimentIdentifier { 194 | return [[[self sharedExperimentsManager] experimentWithIdentifier:experimentIdentifier] enabled] ?: NO; 195 | } 196 | 197 | + (NSString *)selectedOptionForExperiment:(NSString *)experimentIdentifier { 198 | return [[[self sharedExperimentsManager] experimentWithIdentifier:experimentIdentifier] selectedOption]; 199 | } 200 | 201 | 202 | + (void)setExperimentWithIdentifier:(NSString *)experimentIdentifier isEnabled:(BOOL)enabled { 203 | [[self sharedExperimentsManager] setExperimentWithIdentifier:experimentIdentifier isEnabled:enabled]; 204 | } 205 | 206 | + (void)setSelectedOptionForExperimentWithIdentifier:(NSString *)experimentIdentifier 207 | selectedOption:(NSString *)selectedOption { 208 | [[self sharedExperimentsManager] setSelectedOptionForExperimentWithIdentifier:experimentIdentifier 209 | selectedOptions:selectedOption]; 210 | } 211 | 212 | 213 | + (void)deleteAllUserSettings { 214 | [[self sharedExperimentsManager] deleteAllUserSettings]; 215 | } 216 | 217 | 218 | + (NSArray *)allExperiments { 219 | return [[self sharedExperimentsManager] allExperiments]; 220 | } 221 | 222 | 223 | + (BOOL)experimentationEnabled { 224 | return [[VENExperimentsManager sharedExperimentsManager] initialized]; 225 | } 226 | 227 | 228 | + (NSString *)enabledChangedNotificationNameForIdentifier:(NSString *)identifier { 229 | return [identifier stringByAppendingString:@"_ENABLED_CHANGED_NOTIFICATION_KEY"]; 230 | } 231 | 232 | 233 | + (NSString *)optionChangedNotificationNameForIdentifier:(NSString *)identifier { 234 | return [identifier stringByAppendingString:@"_OPTION_CHANGED_NOTIFICATION_KEY"]; 235 | } 236 | 237 | @end 238 | -------------------------------------------------------------------------------- /VENExperimentsManagerTests/VENExperimentsManagerTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "VENExperimentsManager.h" 3 | #import "VENTestExperiments.h" 4 | 5 | @interface VENExperimentsManager (Private) 6 | 7 | - (BOOL)startExperimentsManagerWithPlistName:(NSString *)plistName; 8 | - (VENExperiment *)experimentWithIdentifier:(NSString *)experimentIdentifier; 9 | - (BOOL)experimentIsEnabled:(NSString *)experimentIdentifier; 10 | - (void)setExperimentWithIdentifier:(NSString *)experimentIdentifier isEnabled:(BOOL)enabled; 11 | - (void)setSelectedOptionForExperimentWithIdentifier:(NSString *)experimentIdentifier 12 | selectedOptions:(NSString *)selectedOption; 13 | 14 | @end 15 | 16 | @interface VENExperimentsManagerTests : XCTestCase 17 | 18 | @end 19 | 20 | @implementation VENExperimentsManagerTests 21 | 22 | - (void)setUp { 23 | [VENExperimentsManager startExperimentsManagerWithPlistName:@"testExperiments"]; 24 | [VENExperimentsManager deleteAllUserSettings]; 25 | [super setUp]; 26 | } 27 | 28 | - (void)tearDown { 29 | 30 | [VENExperimentsManager deleteAllUserSettings]; 31 | 32 | [super tearDown]; 33 | } 34 | 35 | 36 | - (void)testExperimentManagerCreation { 37 | VENExperiment *experiment = [VENExperimentsManager experimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT]; 38 | XCTAssertFalse([experiment enabled], @"Incorrectly loaded base state"); 39 | XCTAssertEqualObjects(experiment.name, @"Some Experiment Title", @"Incorrectly loaded base state"); 40 | XCTAssertEqualObjects(experiment.identifier, @"VEN_EXPERIMENT_SOME_EXPERIMENT", @"Incorrectly loaded base state"); 41 | XCTAssertEqualObjects(experiment.details, @"Some details 2", @"Incorrectly loaded base state"); 42 | 43 | experiment = [VENExperimentsManager experimentWithIdentifier:VEN_EXPERIMENT_AUTO_UPDATE]; 44 | XCTAssertNotNil(experiment, @"Should return an experiment that exists"); 45 | XCTAssertTrue([experiment enabled], @"Should load correctly with correct test file"); 46 | } 47 | 48 | 49 | - (void)testChangingExperimentValue { 50 | VENExperiment *experiment = [VENExperimentsManager experimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT]; 51 | XCTAssertFalse([experiment enabled], @"Incorrectly loaded base state"); 52 | XCTAssertEqualObjects(experiment.name, @"Some Experiment Title", @"Incorrectly loaded base state"); 53 | XCTAssertEqualObjects(experiment.identifier, @"VEN_EXPERIMENT_SOME_EXPERIMENT", @"Incorrectly loaded base state"); 54 | XCTAssertEqualObjects(experiment.details, @"Some details 2", @"Incorrectly loaded base state"); 55 | 56 | [VENExperimentsManager setExperimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT isEnabled:YES]; 57 | experiment = [VENExperimentsManager experimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT]; 58 | XCTAssertTrue([experiment enabled], @"Changing experiment state was not reflected correctly"); 59 | [VENExperimentsManager deleteAllUserSettings]; 60 | } 61 | 62 | 63 | - (void)testDeletingUserData { 64 | 65 | VENExperiment *experiment = [VENExperimentsManager experimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT]; 66 | XCTAssertFalse([experiment enabled], @"Incorrectly loaded base state"); 67 | XCTAssertEqualObjects(experiment.name, @"Some Experiment Title", @"Incorrectly loaded base state"); 68 | XCTAssertEqualObjects(experiment.identifier, @"VEN_EXPERIMENT_SOME_EXPERIMENT", @"Incorrectly loaded base state"); 69 | XCTAssertEqualObjects(experiment.details, @"Some details 2", @"Incorrectly loaded base state"); 70 | 71 | [VENExperimentsManager setExperimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT isEnabled:YES]; 72 | experiment = [VENExperimentsManager experimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT]; 73 | XCTAssertTrue([experiment enabled], @"Changing experiment state was not reflected correctly"); 74 | [VENExperimentsManager deleteAllUserSettings]; 75 | 76 | experiment = [VENExperimentsManager experimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT]; 77 | XCTAssertFalse([experiment enabled], @"Incorrectly loaded base state"); 78 | } 79 | 80 | 81 | - (void)testNoConfigurationCausesNoTests { 82 | VENExperimentsManager *experimentsManager = [[VENExperimentsManager alloc] init]; 83 | BOOL configured = [experimentsManager startExperimentsManagerWithPlistName:@"testExperiments"]; 84 | 85 | XCTAssertTrue(configured, @"Could not configure new experiments manager"); 86 | 87 | VENExperiment *experiment = [experimentsManager experimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT]; 88 | XCTAssertFalse([experiment enabled], @"Incorrectly loaded base state"); 89 | XCTAssertEqualObjects(experiment.name, @"Some Experiment Title", @"Incorrectly loaded base state"); 90 | XCTAssertEqualObjects(experiment.identifier, @"VEN_EXPERIMENT_SOME_EXPERIMENT", @"Incorrectly loaded base state"); 91 | experiment = [experimentsManager experimentWithIdentifier:VEN_EXPERIMENT_AUTO_UPDATE]; 92 | XCTAssertNotNil(experiment, @"Should return an experiment that exists"); 93 | XCTAssertTrue([experiment enabled], @"Should load correctly with correct test file"); 94 | 95 | experimentsManager = [[VENExperimentsManager alloc] init]; 96 | configured = [experimentsManager startExperimentsManagerWithPlistName:@"testExperimentsFAKE"]; 97 | 98 | XCTAssertFalse(configured, @"Could not configure new experiments manager"); 99 | experiment = [experimentsManager experimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT]; 100 | XCTAssertNil(experiment, @"Should not return an experiment that does not exist"); 101 | XCTAssertFalse([experiment enabled], @"enabled on nil should return FALSE"); 102 | XCTAssertFalse([experimentsManager experimentIsEnabled:VEN_EXPERIMENT_AUTO_UPDATE], @"Should not enable a non-existant experiment"); 103 | 104 | } 105 | 106 | 107 | - (void)testNoConfigurationCausesFalseResponseToAllTests { 108 | 109 | VENExperimentsManager *experimentsManager = [[VENExperimentsManager alloc] init]; 110 | BOOL configured = [experimentsManager startExperimentsManagerWithPlistName:@"testExperiments"]; 111 | 112 | XCTAssertTrue(configured, @"Could not configure new experiments manager"); 113 | 114 | VENExperiment *experiment = [experimentsManager experimentWithIdentifier:VEN_EXPERIMENT_SOME_EXPERIMENT]; 115 | XCTAssertFalse([experiment enabled], @"Incorrectly loaded base state"); 116 | XCTAssertEqualObjects(experiment.name, @"Some Experiment Title", @"Incorrectly loaded base state"); 117 | XCTAssertEqualObjects(experiment.identifier, @"VEN_EXPERIMENT_SOME_EXPERIMENT", @"Incorrectly loaded base state"); 118 | 119 | experimentsManager = [[VENExperimentsManager alloc] init]; 120 | configured = [experimentsManager startExperimentsManagerWithPlistName:@"testExperimentsFAKE"]; 121 | 122 | XCTAssertFalse(configured, @"Could not configure new experiments manager"); 123 | 124 | XCTAssertFalse([experimentsManager experimentIsEnabled:VEN_EXPERIMENT_SOME_EXPERIMENT], @"Should not enable a non-existant experiment"); 125 | XCTAssertFalse([experimentsManager experimentIsEnabled:VEN_EXPERIMENT_AUTO_UPDATE], @"Should not enable an experiment when there is no config file"); 126 | } 127 | 128 | 129 | - (void)testOptionsAreLoadedIntoExperiments { 130 | VENExperimentsManager *experimentsManager = [[VENExperimentsManager alloc] init]; 131 | [experimentsManager startExperimentsManagerWithPlistName:@"testExperiments"]; 132 | 133 | VENExperiment *experiment = [experimentsManager experimentWithIdentifier:VEN_EXPERIMENT_PAYMENT_SCREEN]; 134 | XCTAssertTrue([experiment.options count] == 3, @"Incorrectly loaded options"); 135 | XCTAssertEqualObjects(experiment.selectedOption, VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_1, @"Incorrectly loaded selectedOption"); 136 | 137 | XCTAssertEqualObjects([experiment selectedOptionDescription], @"Prototype 1", @"Incorrectly loaded or calculated selected option description"); 138 | 139 | [experimentsManager setSelectedOptionForExperimentWithIdentifier:VEN_EXPERIMENT_PAYMENT_SCREEN selectedOptions:VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_2]; 140 | 141 | experimentsManager = [[VENExperimentsManager alloc] init]; 142 | [experimentsManager startExperimentsManagerWithPlistName:@"testExperiments"]; 143 | 144 | experiment = [experimentsManager experimentWithIdentifier:VEN_EXPERIMENT_PAYMENT_SCREEN]; 145 | 146 | XCTAssertEqualObjects(experiment.selectedOption, VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_2, @"Incorrectly loaded selectedOption after saving"); 147 | XCTAssertEqualObjects([experiment selectedOptionDescription], @"Prototype 2", @"Incorrectly loaded or calculated selected option description after saving"); 148 | 149 | [experimentsManager setSelectedOptionForExperimentWithIdentifier:VEN_EXPERIMENT_PAYMENT_SCREEN selectedOptions:VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_1]; 150 | 151 | experiment = [experimentsManager experimentWithIdentifier:VEN_EXPERIMENT_PAYMENT_SCREEN]; 152 | 153 | XCTAssertEqualObjects(experiment.selectedOption, VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_1, @"Incorrectly loaded selectedOption"); 154 | XCTAssertEqualObjects([experiment selectedOptionDescription], @"Prototype 1", @"Incorrectly loaded or calculated selected option description"); 155 | 156 | } 157 | 158 | 159 | - (void)testASelectedOptionCannotBeSetIfItDoesntExistInOptions { 160 | VENExperimentsManager * experimentsManager = [[VENExperimentsManager alloc] init]; 161 | [experimentsManager startExperimentsManagerWithPlistName:@"testExperiments"]; 162 | 163 | [experimentsManager setSelectedOptionForExperimentWithIdentifier:VEN_EXPERIMENT_PAYMENT_SCREEN selectedOptions:VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_3_DOESNT_EXIST]; 164 | 165 | VENExperiment *experiment = [experimentsManager experimentWithIdentifier:VEN_EXPERIMENT_PAYMENT_SCREEN]; 166 | 167 | XCTAssertEqualObjects(experiment.selectedOption, VEN_EXPERIMENT_PAYMENT_SCREEN_PROTO_1, @"Incorrectly loaded selectedOption"); 168 | XCTAssertEqualObjects([experiment selectedOptionDescription], @"Prototype 1", @"Incorrectly loaded or calculated selected option description"); 169 | } 170 | 171 | 172 | - (void)testSupportsOptionsCorrectlyDeterminesWhetherOptionsAreSupported { 173 | VENExperimentsManager * experimentsManager = [[VENExperimentsManager alloc] init]; 174 | [experimentsManager startExperimentsManagerWithPlistName:@"testExperiments"]; 175 | 176 | VENExperiment *experiment = [experimentsManager experimentWithIdentifier:VEN_EXPERIMENT_PAYMENT_SCREEN]; 177 | 178 | XCTAssertTrue([experiment supportsOptions], @"An experiment with options should support options"); 179 | 180 | experiment = [experimentsManager experimentWithIdentifier:VEN_EXPERIMENT_AUTO_UPDATE]; 181 | XCTAssertFalse([experiment supportsOptions], @"An experiment with no options should not support options"); 182 | 183 | } 184 | 185 | @end 186 | -------------------------------------------------------------------------------- /VENExperimentsManager.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 612817FC1A854E33002D1319 /* VENExperimentsSettingsTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 612817F81A854E33002D1319 /* VENExperimentsSettingsTVC.m */; }; 11 | 612817FD1A854E33002D1319 /* VENExperimentTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 612817FA1A854E33002D1319 /* VENExperimentTableViewCell.m */; }; 12 | C21EE10E1865D1BC00C3A07D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C21EE10D1865D1BC00C3A07D /* Foundation.framework */; }; 13 | C21EE11C1865D1BC00C3A07D /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C21EE11B1865D1BC00C3A07D /* XCTest.framework */; }; 14 | C21EE11D1865D1BC00C3A07D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C21EE10D1865D1BC00C3A07D /* Foundation.framework */; }; 15 | C21EE11F1865D1BC00C3A07D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C21EE11E1865D1BC00C3A07D /* UIKit.framework */; }; 16 | C21EE1221865D1BC00C3A07D /* libVENExperimentsManager.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C21EE10A1865D1BC00C3A07D /* libVENExperimentsManager.a */; }; 17 | C21EE1281865D1BC00C3A07D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C21EE1261865D1BC00C3A07D /* InfoPlist.strings */; }; 18 | C21EE12A1865D1BC00C3A07D /* VENExperimentsManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C21EE1291865D1BC00C3A07D /* VENExperimentsManagerTests.m */; }; 19 | C21EE1371865D1DC00C3A07D /* VENExperimentsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C21EE1341865D1DC00C3A07D /* VENExperimentsManager.m */; }; 20 | C21EE13B1865D23D00C3A07D /* VENExperiment.m in Sources */ = {isa = PBXBuildFile; fileRef = C21EE13A1865D23D00C3A07D /* VENExperiment.m */; }; 21 | C21EE13D1865D7F700C3A07D /* testExperiments.plist in Resources */ = {isa = PBXBuildFile; fileRef = C21EE13C1865D7F700C3A07D /* testExperiments.plist */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | C21EE1201865D1BC00C3A07D /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = C21EE1021865D1BC00C3A07D /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = C21EE1091865D1BC00C3A07D; 30 | remoteInfo = VENExperimentsManager; 31 | }; 32 | /* End PBXContainerItemProxy section */ 33 | 34 | /* Begin PBXCopyFilesBuildPhase section */ 35 | C21EE1081865D1BC00C3A07D /* CopyFiles */ = { 36 | isa = PBXCopyFilesBuildPhase; 37 | buildActionMask = 2147483647; 38 | dstPath = "include/$(PRODUCT_NAME)"; 39 | dstSubfolderSpec = 16; 40 | files = ( 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXCopyFilesBuildPhase section */ 45 | 46 | /* Begin PBXFileReference section */ 47 | 612817F71A854E33002D1319 /* VENExperimentsSettingsTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VENExperimentsSettingsTVC.h; path = SettingsViewController/VENExperimentsSettingsTVC.h; sourceTree = ""; }; 48 | 612817F81A854E33002D1319 /* VENExperimentsSettingsTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VENExperimentsSettingsTVC.m; path = SettingsViewController/VENExperimentsSettingsTVC.m; sourceTree = ""; }; 49 | 612817F91A854E33002D1319 /* VENExperimentTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = VENExperimentTableViewCell.h; path = SettingsViewController/VENExperimentTableViewCell.h; sourceTree = ""; }; 50 | 612817FA1A854E33002D1319 /* VENExperimentTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = VENExperimentTableViewCell.m; path = SettingsViewController/VENExperimentTableViewCell.m; sourceTree = ""; }; 51 | 612817FB1A854E33002D1319 /* VENExperimentTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = VENExperimentTableViewCell.xib; path = SettingsViewController/VENExperimentTableViewCell.xib; sourceTree = ""; }; 52 | C21EE10A1865D1BC00C3A07D /* libVENExperimentsManager.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libVENExperimentsManager.a; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | C21EE10D1865D1BC00C3A07D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 54 | C21EE1111865D1BC00C3A07D /* VENExperimentsManager-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "VENExperimentsManager-Prefix.pch"; sourceTree = ""; }; 55 | C21EE11A1865D1BC00C3A07D /* VENExperimentsManagerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VENExperimentsManagerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | C21EE11B1865D1BC00C3A07D /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 57 | C21EE11E1865D1BC00C3A07D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; 58 | C21EE1251865D1BC00C3A07D /* VENExperimentsManagerTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "VENExperimentsManagerTests-Info.plist"; sourceTree = ""; }; 59 | C21EE1271865D1BC00C3A07D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 60 | C21EE1291865D1BC00C3A07D /* VENExperimentsManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VENExperimentsManagerTests.m; sourceTree = ""; }; 61 | C21EE1331865D1DC00C3A07D /* VENExperimentsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VENExperimentsManager.h; sourceTree = ""; }; 62 | C21EE1341865D1DC00C3A07D /* VENExperimentsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VENExperimentsManager.m; sourceTree = ""; }; 63 | C21EE1391865D23D00C3A07D /* VENExperiment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VENExperiment.h; sourceTree = ""; }; 64 | C21EE13A1865D23D00C3A07D /* VENExperiment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VENExperiment.m; sourceTree = ""; }; 65 | C21EE13C1865D7F700C3A07D /* testExperiments.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = testExperiments.plist; sourceTree = ""; }; 66 | C21EE13E1865E05700C3A07D /* VENTestExperiments.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VENTestExperiments.h; sourceTree = ""; }; 67 | /* End PBXFileReference section */ 68 | 69 | /* Begin PBXFrameworksBuildPhase section */ 70 | C21EE1071865D1BC00C3A07D /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | C21EE10E1865D1BC00C3A07D /* Foundation.framework in Frameworks */, 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | C21EE1171865D1BC00C3A07D /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | C21EE1221865D1BC00C3A07D /* libVENExperimentsManager.a in Frameworks */, 83 | C21EE11C1865D1BC00C3A07D /* XCTest.framework in Frameworks */, 84 | C21EE11F1865D1BC00C3A07D /* UIKit.framework in Frameworks */, 85 | C21EE11D1865D1BC00C3A07D /* Foundation.framework in Frameworks */, 86 | ); 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | /* End PBXFrameworksBuildPhase section */ 90 | 91 | /* Begin PBXGroup section */ 92 | 612817F61A854DD6002D1319 /* SettingsViewController */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 612817F71A854E33002D1319 /* VENExperimentsSettingsTVC.h */, 96 | 612817F81A854E33002D1319 /* VENExperimentsSettingsTVC.m */, 97 | 612817F91A854E33002D1319 /* VENExperimentTableViewCell.h */, 98 | 612817FA1A854E33002D1319 /* VENExperimentTableViewCell.m */, 99 | 612817FB1A854E33002D1319 /* VENExperimentTableViewCell.xib */, 100 | ); 101 | name = SettingsViewController; 102 | sourceTree = ""; 103 | }; 104 | C21EE1011865D1BC00C3A07D = { 105 | isa = PBXGroup; 106 | children = ( 107 | C21EE10F1865D1BC00C3A07D /* VENExperimentsManager */, 108 | C21EE1231865D1BC00C3A07D /* VENExperimentsManagerTests */, 109 | C21EE10C1865D1BC00C3A07D /* Frameworks */, 110 | C21EE10B1865D1BC00C3A07D /* Products */, 111 | ); 112 | sourceTree = ""; 113 | }; 114 | C21EE10B1865D1BC00C3A07D /* Products */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | C21EE10A1865D1BC00C3A07D /* libVENExperimentsManager.a */, 118 | C21EE11A1865D1BC00C3A07D /* VENExperimentsManagerTests.xctest */, 119 | ); 120 | name = Products; 121 | sourceTree = ""; 122 | }; 123 | C21EE10C1865D1BC00C3A07D /* Frameworks */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | C21EE10D1865D1BC00C3A07D /* Foundation.framework */, 127 | C21EE11B1865D1BC00C3A07D /* XCTest.framework */, 128 | C21EE11E1865D1BC00C3A07D /* UIKit.framework */, 129 | ); 130 | name = Frameworks; 131 | sourceTree = ""; 132 | }; 133 | C21EE10F1865D1BC00C3A07D /* VENExperimentsManager */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 612817F61A854DD6002D1319 /* SettingsViewController */, 137 | C21EE1341865D1DC00C3A07D /* VENExperimentsManager.m */, 138 | C21EE1331865D1DC00C3A07D /* VENExperimentsManager.h */, 139 | C21EE1391865D23D00C3A07D /* VENExperiment.h */, 140 | C21EE13A1865D23D00C3A07D /* VENExperiment.m */, 141 | C21EE1101865D1BC00C3A07D /* Supporting Files */, 142 | ); 143 | path = VENExperimentsManager; 144 | sourceTree = ""; 145 | }; 146 | C21EE1101865D1BC00C3A07D /* Supporting Files */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | C21EE1111865D1BC00C3A07D /* VENExperimentsManager-Prefix.pch */, 150 | ); 151 | name = "Supporting Files"; 152 | sourceTree = ""; 153 | }; 154 | C21EE1231865D1BC00C3A07D /* VENExperimentsManagerTests */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | C21EE13C1865D7F700C3A07D /* testExperiments.plist */, 158 | C21EE13E1865E05700C3A07D /* VENTestExperiments.h */, 159 | C21EE1291865D1BC00C3A07D /* VENExperimentsManagerTests.m */, 160 | C21EE1241865D1BC00C3A07D /* Supporting Files */, 161 | ); 162 | path = VENExperimentsManagerTests; 163 | sourceTree = ""; 164 | }; 165 | C21EE1241865D1BC00C3A07D /* Supporting Files */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | C21EE1251865D1BC00C3A07D /* VENExperimentsManagerTests-Info.plist */, 169 | C21EE1261865D1BC00C3A07D /* InfoPlist.strings */, 170 | ); 171 | name = "Supporting Files"; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXGroup section */ 175 | 176 | /* Begin PBXNativeTarget section */ 177 | C21EE1091865D1BC00C3A07D /* VENExperimentsManager */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = C21EE12D1865D1BC00C3A07D /* Build configuration list for PBXNativeTarget "VENExperimentsManager" */; 180 | buildPhases = ( 181 | C21EE1061865D1BC00C3A07D /* Sources */, 182 | C21EE1071865D1BC00C3A07D /* Frameworks */, 183 | C21EE1081865D1BC00C3A07D /* CopyFiles */, 184 | ); 185 | buildRules = ( 186 | ); 187 | dependencies = ( 188 | ); 189 | name = VENExperimentsManager; 190 | productName = VENExperimentsManager; 191 | productReference = C21EE10A1865D1BC00C3A07D /* libVENExperimentsManager.a */; 192 | productType = "com.apple.product-type.library.static"; 193 | }; 194 | C21EE1191865D1BC00C3A07D /* VENExperimentsManagerTests */ = { 195 | isa = PBXNativeTarget; 196 | buildConfigurationList = C21EE1301865D1BC00C3A07D /* Build configuration list for PBXNativeTarget "VENExperimentsManagerTests" */; 197 | buildPhases = ( 198 | C21EE1161865D1BC00C3A07D /* Sources */, 199 | C21EE1171865D1BC00C3A07D /* Frameworks */, 200 | C21EE1181865D1BC00C3A07D /* Resources */, 201 | ); 202 | buildRules = ( 203 | ); 204 | dependencies = ( 205 | C21EE1211865D1BC00C3A07D /* PBXTargetDependency */, 206 | ); 207 | name = VENExperimentsManagerTests; 208 | productName = VENExperimentsManagerTests; 209 | productReference = C21EE11A1865D1BC00C3A07D /* VENExperimentsManagerTests.xctest */; 210 | productType = "com.apple.product-type.bundle.unit-test"; 211 | }; 212 | /* End PBXNativeTarget section */ 213 | 214 | /* Begin PBXProject section */ 215 | C21EE1021865D1BC00C3A07D /* Project object */ = { 216 | isa = PBXProject; 217 | attributes = { 218 | LastUpgradeCheck = 0500; 219 | ORGANIZATIONNAME = Venmo; 220 | }; 221 | buildConfigurationList = C21EE1051865D1BC00C3A07D /* Build configuration list for PBXProject "VENExperimentsManager" */; 222 | compatibilityVersion = "Xcode 3.2"; 223 | developmentRegion = English; 224 | hasScannedForEncodings = 0; 225 | knownRegions = ( 226 | en, 227 | ); 228 | mainGroup = C21EE1011865D1BC00C3A07D; 229 | productRefGroup = C21EE10B1865D1BC00C3A07D /* Products */; 230 | projectDirPath = ""; 231 | projectRoot = ""; 232 | targets = ( 233 | C21EE1091865D1BC00C3A07D /* VENExperimentsManager */, 234 | C21EE1191865D1BC00C3A07D /* VENExperimentsManagerTests */, 235 | ); 236 | }; 237 | /* End PBXProject section */ 238 | 239 | /* Begin PBXResourcesBuildPhase section */ 240 | C21EE1181865D1BC00C3A07D /* Resources */ = { 241 | isa = PBXResourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | C21EE1281865D1BC00C3A07D /* InfoPlist.strings in Resources */, 245 | C21EE13D1865D7F700C3A07D /* testExperiments.plist in Resources */, 246 | ); 247 | runOnlyForDeploymentPostprocessing = 0; 248 | }; 249 | /* End PBXResourcesBuildPhase section */ 250 | 251 | /* Begin PBXSourcesBuildPhase section */ 252 | C21EE1061865D1BC00C3A07D /* Sources */ = { 253 | isa = PBXSourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | 612817FD1A854E33002D1319 /* VENExperimentTableViewCell.m in Sources */, 257 | 612817FC1A854E33002D1319 /* VENExperimentsSettingsTVC.m in Sources */, 258 | C21EE13B1865D23D00C3A07D /* VENExperiment.m in Sources */, 259 | C21EE1371865D1DC00C3A07D /* VENExperimentsManager.m in Sources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | C21EE1161865D1BC00C3A07D /* Sources */ = { 264 | isa = PBXSourcesBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | C21EE12A1865D1BC00C3A07D /* VENExperimentsManagerTests.m in Sources */, 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | }; 271 | /* End PBXSourcesBuildPhase section */ 272 | 273 | /* Begin PBXTargetDependency section */ 274 | C21EE1211865D1BC00C3A07D /* PBXTargetDependency */ = { 275 | isa = PBXTargetDependency; 276 | target = C21EE1091865D1BC00C3A07D /* VENExperimentsManager */; 277 | targetProxy = C21EE1201865D1BC00C3A07D /* PBXContainerItemProxy */; 278 | }; 279 | /* End PBXTargetDependency section */ 280 | 281 | /* Begin PBXVariantGroup section */ 282 | C21EE1261865D1BC00C3A07D /* InfoPlist.strings */ = { 283 | isa = PBXVariantGroup; 284 | children = ( 285 | C21EE1271865D1BC00C3A07D /* en */, 286 | ); 287 | name = InfoPlist.strings; 288 | sourceTree = ""; 289 | }; 290 | /* End PBXVariantGroup section */ 291 | 292 | /* Begin XCBuildConfiguration section */ 293 | C21EE12B1865D1BC00C3A07D /* Debug */ = { 294 | isa = XCBuildConfiguration; 295 | buildSettings = { 296 | ALWAYS_SEARCH_USER_PATHS = NO; 297 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 298 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 299 | CLANG_CXX_LIBRARY = "libc++"; 300 | CLANG_ENABLE_MODULES = YES; 301 | CLANG_ENABLE_OBJC_ARC = YES; 302 | CLANG_WARN_BOOL_CONVERSION = YES; 303 | CLANG_WARN_CONSTANT_CONVERSION = YES; 304 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 305 | CLANG_WARN_EMPTY_BODY = YES; 306 | CLANG_WARN_ENUM_CONVERSION = YES; 307 | CLANG_WARN_INT_CONVERSION = YES; 308 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 309 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 310 | COPY_PHASE_STRIP = NO; 311 | GCC_C_LANGUAGE_STANDARD = gnu99; 312 | GCC_DYNAMIC_NO_PIC = NO; 313 | GCC_OPTIMIZATION_LEVEL = 0; 314 | GCC_PREPROCESSOR_DEFINITIONS = ( 315 | "DEBUG=1", 316 | "$(inherited)", 317 | ); 318 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 319 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 320 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 321 | GCC_WARN_UNDECLARED_SELECTOR = YES; 322 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 323 | GCC_WARN_UNUSED_FUNCTION = YES; 324 | GCC_WARN_UNUSED_VARIABLE = YES; 325 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 326 | ONLY_ACTIVE_ARCH = YES; 327 | SDKROOT = iphoneos; 328 | }; 329 | name = Debug; 330 | }; 331 | C21EE12C1865D1BC00C3A07D /* Release */ = { 332 | isa = XCBuildConfiguration; 333 | buildSettings = { 334 | ALWAYS_SEARCH_USER_PATHS = NO; 335 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 336 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 337 | CLANG_CXX_LIBRARY = "libc++"; 338 | CLANG_ENABLE_MODULES = YES; 339 | CLANG_ENABLE_OBJC_ARC = YES; 340 | CLANG_WARN_BOOL_CONVERSION = YES; 341 | CLANG_WARN_CONSTANT_CONVERSION = YES; 342 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 343 | CLANG_WARN_EMPTY_BODY = YES; 344 | CLANG_WARN_ENUM_CONVERSION = YES; 345 | CLANG_WARN_INT_CONVERSION = YES; 346 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 347 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 348 | COPY_PHASE_STRIP = YES; 349 | ENABLE_NS_ASSERTIONS = NO; 350 | GCC_C_LANGUAGE_STANDARD = gnu99; 351 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 352 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 353 | GCC_WARN_UNDECLARED_SELECTOR = YES; 354 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 355 | GCC_WARN_UNUSED_FUNCTION = YES; 356 | GCC_WARN_UNUSED_VARIABLE = YES; 357 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 358 | SDKROOT = iphoneos; 359 | VALIDATE_PRODUCT = YES; 360 | }; 361 | name = Release; 362 | }; 363 | C21EE12E1865D1BC00C3A07D /* Debug */ = { 364 | isa = XCBuildConfiguration; 365 | buildSettings = { 366 | DSTROOT = /tmp/VENExperimentsManager.dst; 367 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 368 | GCC_PREFIX_HEADER = "VENExperimentsManager/VENExperimentsManager-Prefix.pch"; 369 | OTHER_LDFLAGS = "-ObjC"; 370 | PRODUCT_NAME = "$(TARGET_NAME)"; 371 | SKIP_INSTALL = YES; 372 | }; 373 | name = Debug; 374 | }; 375 | C21EE12F1865D1BC00C3A07D /* Release */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | DSTROOT = /tmp/VENExperimentsManager.dst; 379 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 380 | GCC_PREFIX_HEADER = "VENExperimentsManager/VENExperimentsManager-Prefix.pch"; 381 | OTHER_LDFLAGS = "-ObjC"; 382 | PRODUCT_NAME = "$(TARGET_NAME)"; 383 | SKIP_INSTALL = YES; 384 | }; 385 | name = Release; 386 | }; 387 | C21EE1311865D1BC00C3A07D /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 391 | FRAMEWORK_SEARCH_PATHS = ( 392 | "$(SDKROOT)/Developer/Library/Frameworks", 393 | "$(inherited)", 394 | "$(DEVELOPER_FRAMEWORKS_DIR)", 395 | ); 396 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 397 | GCC_PREFIX_HEADER = "VENExperimentsManager/VENExperimentsManager-Prefix.pch"; 398 | GCC_PREPROCESSOR_DEFINITIONS = ( 399 | "DEBUG=1", 400 | "$(inherited)", 401 | ); 402 | INFOPLIST_FILE = "VENExperimentsManagerTests/VENExperimentsManagerTests-Info.plist"; 403 | PRODUCT_NAME = "$(TARGET_NAME)"; 404 | TESTING = 1; 405 | WRAPPER_EXTENSION = xctest; 406 | }; 407 | name = Debug; 408 | }; 409 | C21EE1321865D1BC00C3A07D /* Release */ = { 410 | isa = XCBuildConfiguration; 411 | buildSettings = { 412 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 413 | FRAMEWORK_SEARCH_PATHS = ( 414 | "$(SDKROOT)/Developer/Library/Frameworks", 415 | "$(inherited)", 416 | "$(DEVELOPER_FRAMEWORKS_DIR)", 417 | ); 418 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 419 | GCC_PREFIX_HEADER = "VENExperimentsManager/VENExperimentsManager-Prefix.pch"; 420 | INFOPLIST_FILE = "VENExperimentsManagerTests/VENExperimentsManagerTests-Info.plist"; 421 | PRODUCT_NAME = "$(TARGET_NAME)"; 422 | TESTING = 1; 423 | WRAPPER_EXTENSION = xctest; 424 | }; 425 | name = Release; 426 | }; 427 | /* End XCBuildConfiguration section */ 428 | 429 | /* Begin XCConfigurationList section */ 430 | C21EE1051865D1BC00C3A07D /* Build configuration list for PBXProject "VENExperimentsManager" */ = { 431 | isa = XCConfigurationList; 432 | buildConfigurations = ( 433 | C21EE12B1865D1BC00C3A07D /* Debug */, 434 | C21EE12C1865D1BC00C3A07D /* Release */, 435 | ); 436 | defaultConfigurationIsVisible = 0; 437 | defaultConfigurationName = Release; 438 | }; 439 | C21EE12D1865D1BC00C3A07D /* Build configuration list for PBXNativeTarget "VENExperimentsManager" */ = { 440 | isa = XCConfigurationList; 441 | buildConfigurations = ( 442 | C21EE12E1865D1BC00C3A07D /* Debug */, 443 | C21EE12F1865D1BC00C3A07D /* Release */, 444 | ); 445 | defaultConfigurationIsVisible = 0; 446 | defaultConfigurationName = Release; 447 | }; 448 | C21EE1301865D1BC00C3A07D /* Build configuration list for PBXNativeTarget "VENExperimentsManagerTests" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | C21EE1311865D1BC00C3A07D /* Debug */, 452 | C21EE1321865D1BC00C3A07D /* Release */, 453 | ); 454 | defaultConfigurationIsVisible = 0; 455 | defaultConfigurationName = Release; 456 | }; 457 | /* End XCConfigurationList section */ 458 | }; 459 | rootObject = C21EE1021865D1BC00C3A07D /* Project object */; 460 | } 461 | -------------------------------------------------------------------------------- /Sample App/ExperimentsManagerSampleApp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C249F3221877293D00D417B2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C249F3211877293D00D417B2 /* Foundation.framework */; }; 11 | C249F3241877293D00D417B2 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C249F3231877293D00D417B2 /* CoreGraphics.framework */; }; 12 | C249F3261877293D00D417B2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C249F3251877293D00D417B2 /* UIKit.framework */; }; 13 | C249F32C1877293D00D417B2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C249F32A1877293D00D417B2 /* InfoPlist.strings */; }; 14 | C249F32E1877293D00D417B2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C249F32D1877293D00D417B2 /* main.m */; }; 15 | C249F3321877293D00D417B2 /* VENAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C249F3311877293D00D417B2 /* VENAppDelegate.m */; }; 16 | C249F3351877293D00D417B2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C249F3331877293D00D417B2 /* Main.storyboard */; }; 17 | C249F33A1877293D00D417B2 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C249F3391877293D00D417B2 /* Images.xcassets */; }; 18 | C249F3411877293D00D417B2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C249F3401877293D00D417B2 /* XCTest.framework */; }; 19 | C249F3421877293E00D417B2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C249F3211877293D00D417B2 /* Foundation.framework */; }; 20 | C249F3431877293E00D417B2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C249F3251877293D00D417B2 /* UIKit.framework */; }; 21 | C2E135291877295E005F3013 /* VENExperiment.m in Sources */ = {isa = PBXBuildFile; fileRef = C2E135251877295E005F3013 /* VENExperiment.m */; }; 22 | C2E1352A1877295E005F3013 /* VENExperimentsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C2E135281877295E005F3013 /* VENExperimentsManager.m */; }; 23 | C2E1354518772FA3005F3013 /* VENExperimentsSettingsTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = C2E1354118772FA3005F3013 /* VENExperimentsSettingsTVC.m */; }; 24 | C2E1354618772FA3005F3013 /* VENExperimentTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = C2E1354318772FA3005F3013 /* VENExperimentTableViewCell.m */; }; 25 | C2E1354718772FA3005F3013 /* VENExperimentTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C2E1354418772FA3005F3013 /* VENExperimentTableViewCell.xib */; }; 26 | C2E1354818772FCF005F3013 /* experiments.plist in Resources */ = {isa = PBXBuildFile; fileRef = C2E1353D18772B5A005F3013 /* experiments.plist */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | C249F3441877293E00D417B2 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = C249F3161877293D00D417B2 /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = C249F31D1877293D00D417B2; 35 | remoteInfo = ExperimentsMangaerSampleApp; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | C249F31E1877293D00D417B2 /* ExperimentsManagerSampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExperimentsManagerSampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | C249F3211877293D00D417B2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 42 | C249F3231877293D00D417B2 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 43 | C249F3251877293D00D417B2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 44 | C249F3291877293D00D417B2 /* ExperimentsManagerSampleApp-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ExperimentsManagerSampleApp-Info.plist"; sourceTree = ""; }; 45 | C249F32B1877293D00D417B2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 46 | C249F32D1877293D00D417B2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 47 | C249F32F1877293D00D417B2 /* ExperimentsManagerSampleApp-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ExperimentsManagerSampleApp-Prefix.pch"; sourceTree = ""; }; 48 | C249F3301877293D00D417B2 /* VENAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VENAppDelegate.h; sourceTree = ""; }; 49 | C249F3311877293D00D417B2 /* VENAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VENAppDelegate.m; sourceTree = ""; }; 50 | C249F3341877293D00D417B2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 51 | C249F3391877293D00D417B2 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 52 | C249F33F1877293D00D417B2 /* ExperimentsManagerSampleAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExperimentsManagerSampleAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | C249F3401877293D00D417B2 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 54 | C2E135241877295E005F3013 /* VENExperiment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VENExperiment.h; sourceTree = ""; }; 55 | C2E135251877295E005F3013 /* VENExperiment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VENExperiment.m; sourceTree = ""; }; 56 | C2E135271877295E005F3013 /* VENExperimentsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VENExperimentsManager.h; sourceTree = ""; }; 57 | C2E135281877295E005F3013 /* VENExperimentsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VENExperimentsManager.m; sourceTree = ""; }; 58 | C2E1353D18772B5A005F3013 /* experiments.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = experiments.plist; sourceTree = ""; }; 59 | C2E1354018772FA3005F3013 /* VENExperimentsSettingsTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VENExperimentsSettingsTVC.h; sourceTree = ""; }; 60 | C2E1354118772FA3005F3013 /* VENExperimentsSettingsTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VENExperimentsSettingsTVC.m; sourceTree = ""; }; 61 | C2E1354218772FA3005F3013 /* VENExperimentTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VENExperimentTableViewCell.h; sourceTree = ""; }; 62 | C2E1354318772FA3005F3013 /* VENExperimentTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VENExperimentTableViewCell.m; sourceTree = ""; }; 63 | C2E1354418772FA3005F3013 /* VENExperimentTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = VENExperimentTableViewCell.xib; sourceTree = ""; }; 64 | /* End PBXFileReference section */ 65 | 66 | /* Begin PBXFrameworksBuildPhase section */ 67 | C249F31B1877293D00D417B2 /* Frameworks */ = { 68 | isa = PBXFrameworksBuildPhase; 69 | buildActionMask = 2147483647; 70 | files = ( 71 | C249F3241877293D00D417B2 /* CoreGraphics.framework in Frameworks */, 72 | C249F3261877293D00D417B2 /* UIKit.framework in Frameworks */, 73 | C249F3221877293D00D417B2 /* Foundation.framework in Frameworks */, 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | C249F33C1877293D00D417B2 /* Frameworks */ = { 78 | isa = PBXFrameworksBuildPhase; 79 | buildActionMask = 2147483647; 80 | files = ( 81 | C249F3411877293D00D417B2 /* XCTest.framework in Frameworks */, 82 | C249F3431877293E00D417B2 /* UIKit.framework in Frameworks */, 83 | C249F3421877293E00D417B2 /* Foundation.framework in Frameworks */, 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXFrameworksBuildPhase section */ 88 | 89 | /* Begin PBXGroup section */ 90 | C249F3151877293D00D417B2 = { 91 | isa = PBXGroup; 92 | children = ( 93 | C249F3271877293D00D417B2 /* ExperimentsManagerSampleApp */, 94 | C249F3201877293D00D417B2 /* Frameworks */, 95 | C249F31F1877293D00D417B2 /* Products */, 96 | ); 97 | sourceTree = ""; 98 | }; 99 | C249F31F1877293D00D417B2 /* Products */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | C249F31E1877293D00D417B2 /* ExperimentsManagerSampleApp.app */, 103 | C249F33F1877293D00D417B2 /* ExperimentsManagerSampleAppTests.xctest */, 104 | ); 105 | name = Products; 106 | sourceTree = ""; 107 | }; 108 | C249F3201877293D00D417B2 /* Frameworks */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | C249F3211877293D00D417B2 /* Foundation.framework */, 112 | C249F3231877293D00D417B2 /* CoreGraphics.framework */, 113 | C249F3251877293D00D417B2 /* UIKit.framework */, 114 | C249F3401877293D00D417B2 /* XCTest.framework */, 115 | ); 116 | name = Frameworks; 117 | sourceTree = ""; 118 | }; 119 | C249F3271877293D00D417B2 /* ExperimentsManagerSampleApp */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | C2E135231877295E005F3013 /* VENExperimentsManager */, 123 | C249F3301877293D00D417B2 /* VENAppDelegate.h */, 124 | C249F3311877293D00D417B2 /* VENAppDelegate.m */, 125 | C249F3331877293D00D417B2 /* Main.storyboard */, 126 | C2E1353D18772B5A005F3013 /* experiments.plist */, 127 | C249F3391877293D00D417B2 /* Images.xcassets */, 128 | C249F3281877293D00D417B2 /* Supporting Files */, 129 | ); 130 | path = ExperimentsManagerSampleApp; 131 | sourceTree = ""; 132 | }; 133 | C249F3281877293D00D417B2 /* Supporting Files */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | C249F3291877293D00D417B2 /* ExperimentsManagerSampleApp-Info.plist */, 137 | C249F32A1877293D00D417B2 /* InfoPlist.strings */, 138 | C249F32D1877293D00D417B2 /* main.m */, 139 | C249F32F1877293D00D417B2 /* ExperimentsManagerSampleApp-Prefix.pch */, 140 | ); 141 | name = "Supporting Files"; 142 | sourceTree = ""; 143 | }; 144 | C2E135231877295E005F3013 /* VENExperimentsManager */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | C2E1353F18772FA3005F3013 /* Settings View Controller */, 148 | C2E135241877295E005F3013 /* VENExperiment.h */, 149 | C2E135251877295E005F3013 /* VENExperiment.m */, 150 | C2E135271877295E005F3013 /* VENExperimentsManager.h */, 151 | C2E135281877295E005F3013 /* VENExperimentsManager.m */, 152 | ); 153 | name = VENExperimentsManager; 154 | path = ../../VENExperimentsManager; 155 | sourceTree = ""; 156 | }; 157 | C2E1353F18772FA3005F3013 /* Settings View Controller */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | C2E1354018772FA3005F3013 /* VENExperimentsSettingsTVC.h */, 161 | C2E1354118772FA3005F3013 /* VENExperimentsSettingsTVC.m */, 162 | C2E1354218772FA3005F3013 /* VENExperimentTableViewCell.h */, 163 | C2E1354318772FA3005F3013 /* VENExperimentTableViewCell.m */, 164 | C2E1354418772FA3005F3013 /* VENExperimentTableViewCell.xib */, 165 | ); 166 | path = "Settings View Controller"; 167 | sourceTree = ""; 168 | }; 169 | /* End PBXGroup section */ 170 | 171 | /* Begin PBXNativeTarget section */ 172 | C249F31D1877293D00D417B2 /* ExperimentsManagerSampleApp */ = { 173 | isa = PBXNativeTarget; 174 | buildConfigurationList = C249F3501877293E00D417B2 /* Build configuration list for PBXNativeTarget "ExperimentsManagerSampleApp" */; 175 | buildPhases = ( 176 | C249F31A1877293D00D417B2 /* Sources */, 177 | C249F31B1877293D00D417B2 /* Frameworks */, 178 | C249F31C1877293D00D417B2 /* Resources */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | ); 184 | name = ExperimentsManagerSampleApp; 185 | productName = ExperimentsMangaerSampleApp; 186 | productReference = C249F31E1877293D00D417B2 /* ExperimentsManagerSampleApp.app */; 187 | productType = "com.apple.product-type.application"; 188 | }; 189 | C249F33E1877293D00D417B2 /* ExperimentsManagerSampleAppTests */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = C249F3531877293E00D417B2 /* Build configuration list for PBXNativeTarget "ExperimentsManagerSampleAppTests" */; 192 | buildPhases = ( 193 | C249F33B1877293D00D417B2 /* Sources */, 194 | C249F33C1877293D00D417B2 /* Frameworks */, 195 | C249F33D1877293D00D417B2 /* Resources */, 196 | ); 197 | buildRules = ( 198 | ); 199 | dependencies = ( 200 | C249F3451877293E00D417B2 /* PBXTargetDependency */, 201 | ); 202 | name = ExperimentsManagerSampleAppTests; 203 | productName = ExperimentsMangaerSampleAppTests; 204 | productReference = C249F33F1877293D00D417B2 /* ExperimentsManagerSampleAppTests.xctest */; 205 | productType = "com.apple.product-type.bundle.unit-test"; 206 | }; 207 | /* End PBXNativeTarget section */ 208 | 209 | /* Begin PBXProject section */ 210 | C249F3161877293D00D417B2 /* Project object */ = { 211 | isa = PBXProject; 212 | attributes = { 213 | CLASSPREFIX = VEN; 214 | LastUpgradeCheck = 0500; 215 | ORGANIZATIONNAME = Venmo; 216 | TargetAttributes = { 217 | C249F33E1877293D00D417B2 = { 218 | TestTargetID = C249F31D1877293D00D417B2; 219 | }; 220 | }; 221 | }; 222 | buildConfigurationList = C249F3191877293D00D417B2 /* Build configuration list for PBXProject "ExperimentsManagerSampleApp" */; 223 | compatibilityVersion = "Xcode 3.2"; 224 | developmentRegion = English; 225 | hasScannedForEncodings = 0; 226 | knownRegions = ( 227 | en, 228 | Base, 229 | ); 230 | mainGroup = C249F3151877293D00D417B2; 231 | productRefGroup = C249F31F1877293D00D417B2 /* Products */; 232 | projectDirPath = ""; 233 | projectRoot = ""; 234 | targets = ( 235 | C249F31D1877293D00D417B2 /* ExperimentsManagerSampleApp */, 236 | C249F33E1877293D00D417B2 /* ExperimentsManagerSampleAppTests */, 237 | ); 238 | }; 239 | /* End PBXProject section */ 240 | 241 | /* Begin PBXResourcesBuildPhase section */ 242 | C249F31C1877293D00D417B2 /* Resources */ = { 243 | isa = PBXResourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | C249F33A1877293D00D417B2 /* Images.xcassets in Resources */, 247 | C2E1354718772FA3005F3013 /* VENExperimentTableViewCell.xib in Resources */, 248 | C249F32C1877293D00D417B2 /* InfoPlist.strings in Resources */, 249 | C2E1354818772FCF005F3013 /* experiments.plist in Resources */, 250 | C249F3351877293D00D417B2 /* Main.storyboard in Resources */, 251 | ); 252 | runOnlyForDeploymentPostprocessing = 0; 253 | }; 254 | C249F33D1877293D00D417B2 /* Resources */ = { 255 | isa = PBXResourcesBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXResourcesBuildPhase section */ 262 | 263 | /* Begin PBXSourcesBuildPhase section */ 264 | C249F31A1877293D00D417B2 /* Sources */ = { 265 | isa = PBXSourcesBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | C2E1354618772FA3005F3013 /* VENExperimentTableViewCell.m in Sources */, 269 | C2E1354518772FA3005F3013 /* VENExperimentsSettingsTVC.m in Sources */, 270 | C2E1352A1877295E005F3013 /* VENExperimentsManager.m in Sources */, 271 | C2E135291877295E005F3013 /* VENExperiment.m in Sources */, 272 | C249F3321877293D00D417B2 /* VENAppDelegate.m in Sources */, 273 | C249F32E1877293D00D417B2 /* main.m in Sources */, 274 | ); 275 | runOnlyForDeploymentPostprocessing = 0; 276 | }; 277 | C249F33B1877293D00D417B2 /* Sources */ = { 278 | isa = PBXSourcesBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | /* End PBXSourcesBuildPhase section */ 285 | 286 | /* Begin PBXTargetDependency section */ 287 | C249F3451877293E00D417B2 /* PBXTargetDependency */ = { 288 | isa = PBXTargetDependency; 289 | target = C249F31D1877293D00D417B2 /* ExperimentsManagerSampleApp */; 290 | targetProxy = C249F3441877293E00D417B2 /* PBXContainerItemProxy */; 291 | }; 292 | /* End PBXTargetDependency section */ 293 | 294 | /* Begin PBXVariantGroup section */ 295 | C249F32A1877293D00D417B2 /* InfoPlist.strings */ = { 296 | isa = PBXVariantGroup; 297 | children = ( 298 | C249F32B1877293D00D417B2 /* en */, 299 | ); 300 | name = InfoPlist.strings; 301 | sourceTree = ""; 302 | }; 303 | C249F3331877293D00D417B2 /* Main.storyboard */ = { 304 | isa = PBXVariantGroup; 305 | children = ( 306 | C249F3341877293D00D417B2 /* Base */, 307 | ); 308 | name = Main.storyboard; 309 | sourceTree = ""; 310 | }; 311 | /* End PBXVariantGroup section */ 312 | 313 | /* Begin XCBuildConfiguration section */ 314 | C249F34E1877293E00D417B2 /* Debug */ = { 315 | isa = XCBuildConfiguration; 316 | buildSettings = { 317 | ALWAYS_SEARCH_USER_PATHS = NO; 318 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 319 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 320 | CLANG_CXX_LIBRARY = "libc++"; 321 | CLANG_ENABLE_MODULES = YES; 322 | CLANG_ENABLE_OBJC_ARC = YES; 323 | CLANG_WARN_BOOL_CONVERSION = YES; 324 | CLANG_WARN_CONSTANT_CONVERSION = YES; 325 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 326 | CLANG_WARN_EMPTY_BODY = YES; 327 | CLANG_WARN_ENUM_CONVERSION = YES; 328 | CLANG_WARN_INT_CONVERSION = YES; 329 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 330 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 331 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 332 | COPY_PHASE_STRIP = NO; 333 | GCC_C_LANGUAGE_STANDARD = gnu99; 334 | GCC_DYNAMIC_NO_PIC = NO; 335 | GCC_OPTIMIZATION_LEVEL = 0; 336 | GCC_PREPROCESSOR_DEFINITIONS = ( 337 | "DEBUG=1", 338 | "$(inherited)", 339 | ); 340 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 341 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 342 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 343 | GCC_WARN_UNDECLARED_SELECTOR = YES; 344 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 345 | GCC_WARN_UNUSED_FUNCTION = YES; 346 | GCC_WARN_UNUSED_VARIABLE = YES; 347 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 348 | ONLY_ACTIVE_ARCH = YES; 349 | SDKROOT = iphoneos; 350 | }; 351 | name = Debug; 352 | }; 353 | C249F34F1877293E00D417B2 /* Release */ = { 354 | isa = XCBuildConfiguration; 355 | buildSettings = { 356 | ALWAYS_SEARCH_USER_PATHS = NO; 357 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 358 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 359 | CLANG_CXX_LIBRARY = "libc++"; 360 | CLANG_ENABLE_MODULES = YES; 361 | CLANG_ENABLE_OBJC_ARC = YES; 362 | CLANG_WARN_BOOL_CONVERSION = YES; 363 | CLANG_WARN_CONSTANT_CONVERSION = YES; 364 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 365 | CLANG_WARN_EMPTY_BODY = YES; 366 | CLANG_WARN_ENUM_CONVERSION = YES; 367 | CLANG_WARN_INT_CONVERSION = YES; 368 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 369 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 370 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 371 | COPY_PHASE_STRIP = YES; 372 | ENABLE_NS_ASSERTIONS = NO; 373 | GCC_C_LANGUAGE_STANDARD = gnu99; 374 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 375 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 376 | GCC_WARN_UNDECLARED_SELECTOR = YES; 377 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 378 | GCC_WARN_UNUSED_FUNCTION = YES; 379 | GCC_WARN_UNUSED_VARIABLE = YES; 380 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 381 | SDKROOT = iphoneos; 382 | VALIDATE_PRODUCT = YES; 383 | }; 384 | name = Release; 385 | }; 386 | C249F3511877293E00D417B2 /* Debug */ = { 387 | isa = XCBuildConfiguration; 388 | buildSettings = { 389 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 390 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 391 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 392 | GCC_PREFIX_HEADER = "ExperimentsManagerSampleApp/ExperimentsManagerSampleApp-Prefix.pch"; 393 | INFOPLIST_FILE = "ExperimentsManagerSampleApp/ExperimentsManagerSampleApp-Info.plist"; 394 | PRODUCT_NAME = "$(TARGET_NAME)"; 395 | WRAPPER_EXTENSION = app; 396 | }; 397 | name = Debug; 398 | }; 399 | C249F3521877293E00D417B2 /* Release */ = { 400 | isa = XCBuildConfiguration; 401 | buildSettings = { 402 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 403 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 404 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 405 | GCC_PREFIX_HEADER = "ExperimentsManagerSampleApp/ExperimentsManagerSampleApp-Prefix.pch"; 406 | INFOPLIST_FILE = "ExperimentsManagerSampleApp/ExperimentsManagerSampleApp-Info.plist"; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | WRAPPER_EXTENSION = app; 409 | }; 410 | name = Release; 411 | }; 412 | C249F3541877293E00D417B2 /* Debug */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 416 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExperimentsManagerSampleApp.app/ExperimentsManagerSampleApp"; 417 | FRAMEWORK_SEARCH_PATHS = ( 418 | "$(SDKROOT)/Developer/Library/Frameworks", 419 | "$(inherited)", 420 | "$(DEVELOPER_FRAMEWORKS_DIR)", 421 | ); 422 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 423 | GCC_PREFIX_HEADER = "ExperimentsMangaerSampleApp/ExperimentsManagerSampleApp-Prefix.pch"; 424 | GCC_PREPROCESSOR_DEFINITIONS = ( 425 | "DEBUG=1", 426 | "$(inherited)", 427 | ); 428 | INFOPLIST_FILE = "ExperimentsMangaerSampleAppTests/ExperimentsMangaerSampleAppTests-Info.plist"; 429 | PRODUCT_NAME = ExperimentsManagerSampleAppTests; 430 | TEST_HOST = "$(BUNDLE_LOADER)"; 431 | WRAPPER_EXTENSION = xctest; 432 | }; 433 | name = Debug; 434 | }; 435 | C249F3551877293E00D417B2 /* Release */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 439 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExperimentsManagerSampleApp.app/ExperimentsManagerSampleApp"; 440 | FRAMEWORK_SEARCH_PATHS = ( 441 | "$(SDKROOT)/Developer/Library/Frameworks", 442 | "$(inherited)", 443 | "$(DEVELOPER_FRAMEWORKS_DIR)", 444 | ); 445 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 446 | GCC_PREFIX_HEADER = "ExperimentsMangaerSampleApp/ExperimentsManagerSampleApp-Prefix.pch"; 447 | INFOPLIST_FILE = "ExperimentsMangaerSampleAppTests/ExperimentsMangaerSampleAppTests-Info.plist"; 448 | PRODUCT_NAME = ExperimentsManagerSampleAppTests; 449 | TEST_HOST = "$(BUNDLE_LOADER)"; 450 | WRAPPER_EXTENSION = xctest; 451 | }; 452 | name = Release; 453 | }; 454 | /* End XCBuildConfiguration section */ 455 | 456 | /* Begin XCConfigurationList section */ 457 | C249F3191877293D00D417B2 /* Build configuration list for PBXProject "ExperimentsManagerSampleApp" */ = { 458 | isa = XCConfigurationList; 459 | buildConfigurations = ( 460 | C249F34E1877293E00D417B2 /* Debug */, 461 | C249F34F1877293E00D417B2 /* Release */, 462 | ); 463 | defaultConfigurationIsVisible = 0; 464 | defaultConfigurationName = Release; 465 | }; 466 | C249F3501877293E00D417B2 /* Build configuration list for PBXNativeTarget "ExperimentsManagerSampleApp" */ = { 467 | isa = XCConfigurationList; 468 | buildConfigurations = ( 469 | C249F3511877293E00D417B2 /* Debug */, 470 | C249F3521877293E00D417B2 /* Release */, 471 | ); 472 | defaultConfigurationIsVisible = 0; 473 | defaultConfigurationName = Release; 474 | }; 475 | C249F3531877293E00D417B2 /* Build configuration list for PBXNativeTarget "ExperimentsManagerSampleAppTests" */ = { 476 | isa = XCConfigurationList; 477 | buildConfigurations = ( 478 | C249F3541877293E00D417B2 /* Debug */, 479 | C249F3551877293E00D417B2 /* Release */, 480 | ); 481 | defaultConfigurationIsVisible = 0; 482 | defaultConfigurationName = Release; 483 | }; 484 | /* End XCConfigurationList section */ 485 | }; 486 | rootObject = C249F3161877293D00D417B2 /* Project object */; 487 | } 488 | --------------------------------------------------------------------------------