├── .gitignore ├── LICENSE ├── PAPreferences.podspec ├── PAPreferences ├── PAPreferences.h ├── PAPreferences.m ├── PAPropertyDescriptor.h └── PAPropertyDescriptor.m ├── PAPreferencesTests └── PAPreferencesTests.m ├── README.md ├── Sample-OSX └── PAPreferencesSampleOSX │ ├── PAPreferencesSampleOSX.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── PAPreferencesSampleOSX │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── PAPreferencesSampleOSX-Info.plist │ ├── PAPreferencesSampleOSX-Prefix.pch │ ├── en.lproj │ │ ├── Credits.rtf │ │ └── InfoPlist.strings │ └── main.m │ └── PAPreferencesSampleOSXTests │ ├── PAPreferencesSampleOSXTests-Info.plist │ └── en.lproj │ └── InfoPlist.strings └── Sample-iOS ├── PAPreferencesSample.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── PAPreferencesSample ├── AppDelegate.h ├── AppDelegate.m ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json ├── Main.storyboard ├── PAPreferencesSample-Info.plist ├── PAPreferencesSample-Prefix.pch ├── Preferences.h ├── Preferences.m ├── ViewController.h ├── ViewController.m ├── en.lproj │ └── InfoPlist.strings └── main.m └── PAPreferencesSampleTests ├── PAPreferencesSampleTests-Info.plist └── en.lproj └── InfoPlist.strings /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | xcuserdata 3 | *.xcuserdata 4 | *.xccheckout 5 | xcshareddata 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Denis Hennessy (Peer Assembly - http://peerassembly.com) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of Peer Assembly, Denis Hennessy nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL PEER ASSEMBLY OR DENIS HENNESSY BE LIABLE FOR ANY 19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /PAPreferences.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "PAPreferences" 3 | s.version = "0.5" 4 | s.summary = "An easy way to store user preferences using NSUserDefaults." 5 | 6 | s.description = <<-DESC 7 | PAPreferences maps `dynamic` properties onto NSUserDefaults getters and setters so that you can access 8 | defaults as if they were regular properties on an object. That object is normally a singleton since you 9 | typically want a single set of preferences for the entire app. 10 | DESC 11 | 12 | s.homepage = "https://github.com/dhennessy/PAPreferences" 13 | s.author = { "Denis Hennessy" => "denis@hennessynet.com" } 14 | s.license = { :type => 'BSD', :file => 'LICENSE' } 15 | 16 | s.ios.deployment_target = '5.0' 17 | s.osx.deployment_target = '10.7' 18 | 19 | s.source = { :git => 'https://github.com/dhennessy/PAPreferences.git', :tag => s.version.to_s } 20 | s.source_files = 'PAPreferences' 21 | s.requires_arc = true 22 | end 23 | -------------------------------------------------------------------------------- /PAPreferences/PAPreferences.h: -------------------------------------------------------------------------------- 1 | // 2 | // PAPreferences.h 3 | // PAPreferencesSample 4 | // 5 | // Created by Denis Hennessy on 16/09/2013. 6 | // Copyright (c) 2013 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString * const PAPreferencesDidChangeNotification; 12 | extern NSString * const PAPreferencesChangedPropertyKey; 13 | 14 | @interface PAPreferences : NSObject { 15 | NSDictionary *_properties; 16 | } 17 | 18 | @property (nonatomic, assign) BOOL shouldAutomaticallySynchronize; 19 | 20 | + (NSString *)defaultsKeyForPropertyName:(NSString *)key; 21 | + (instancetype)sharedInstance; 22 | 23 | - (BOOL)synchronize; 24 | - (NSUserDefaults *)userDefaults; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /PAPreferences/PAPreferences.m: -------------------------------------------------------------------------------- 1 | // 2 | // PAPreferences.m 3 | // PAPreferencesSample 4 | // 5 | // Created by Denis Hennessy on 16/09/2013. 6 | // Copyright (c) 2013 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import "PAPreferences.h" 10 | #import "PAPropertyDescriptor.h" 11 | #include 12 | 13 | NSString * const PAPreferencesDidChangeNotification = @"PAPreferencesDidChangeNotification"; 14 | NSString * const PAPreferencesChangedPropertyKey = @"PAPreferencesChangedPropertyKey"; 15 | 16 | static NSMutableDictionary *_dynamicProperties; 17 | 18 | 19 | NS_INLINE PAPropertyDescriptor * propertyDescriptorForSelector(SEL _cmd) { 20 | NSString *selectorString = NSStringFromSelector(_cmd); 21 | PAPropertyDescriptor *descriptor = _dynamicProperties[selectorString]; 22 | 23 | return descriptor; 24 | } 25 | 26 | NS_INLINE NSString * defaultsKeyForSelector(SEL _cmd) { 27 | PAPropertyDescriptor *descriptor = propertyDescriptorForSelector(_cmd); 28 | NSString *defaultsKey = descriptor.defaultsKey; 29 | 30 | return defaultsKey; 31 | } 32 | 33 | BOOL paprefBoolGetter(id self, SEL _cmd) { 34 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 35 | return [[self userDefaults] boolForKey:defaultsKey]; 36 | } 37 | 38 | void paprefBoolSetter(id self, SEL _cmd, BOOL value) { 39 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 40 | [[self userDefaults] setBool:value forKey:defaultsKey]; 41 | if ([self shouldAutomaticallySynchronize]) { 42 | [self synchronize]; 43 | } 44 | NSDictionary *userInfo = @{PAPreferencesChangedPropertyKey: defaultsKey}; 45 | [[NSNotificationCenter defaultCenter] postNotificationName:PAPreferencesDidChangeNotification object:self userInfo:userInfo]; 46 | } 47 | 48 | double paprefDoubleGetter(id self, SEL _cmd) { 49 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 50 | return [[self userDefaults] doubleForKey:defaultsKey]; 51 | } 52 | 53 | void paprefDoubleSetter(id self, SEL _cmd, double value) { 54 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 55 | [[self userDefaults] setDouble:value forKey:defaultsKey]; 56 | if ([self shouldAutomaticallySynchronize]) { 57 | [self synchronize]; 58 | } 59 | NSDictionary *userInfo = @{PAPreferencesChangedPropertyKey: defaultsKey}; 60 | [[NSNotificationCenter defaultCenter] postNotificationName:PAPreferencesDidChangeNotification object:self userInfo:userInfo]; 61 | } 62 | 63 | float paprefFloatGetter(id self, SEL _cmd) { 64 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 65 | return [[self userDefaults] floatForKey:defaultsKey]; 66 | } 67 | 68 | void paprefFloatSetter(id self, SEL _cmd, float value) { 69 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 70 | [[self userDefaults] setFloat:value forKey:defaultsKey]; 71 | if ([self shouldAutomaticallySynchronize]) { 72 | [self synchronize]; 73 | } 74 | NSDictionary *userInfo = @{PAPreferencesChangedPropertyKey: defaultsKey}; 75 | [[NSNotificationCenter defaultCenter] postNotificationName:PAPreferencesDidChangeNotification object:self userInfo:userInfo]; 76 | } 77 | 78 | NSInteger paprefIntegerGetter(id self, SEL _cmd) { 79 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 80 | return [[self userDefaults] integerForKey:defaultsKey]; 81 | } 82 | 83 | void paprefIntegerSetter(id self, SEL _cmd, NSInteger value) { 84 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 85 | [[self userDefaults] setInteger:value forKey:defaultsKey]; 86 | if ([self shouldAutomaticallySynchronize]) { 87 | [self synchronize]; 88 | } 89 | NSDictionary *userInfo = @{PAPreferencesChangedPropertyKey: defaultsKey}; 90 | [[NSNotificationCenter defaultCenter] postNotificationName:PAPreferencesDidChangeNotification object:self userInfo:userInfo]; 91 | } 92 | 93 | id paprefObjectGetter(id self, SEL _cmd) { 94 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 95 | return [[self userDefaults] objectForKey:defaultsKey]; 96 | } 97 | 98 | void paprefObjectSetter(id self, SEL _cmd, id value) { 99 | #if DEBUG 100 | if ((value != nil) && 101 | ![NSPropertyListSerialization propertyList:value 102 | isValidForFormat:NSPropertyListBinaryFormat_v1_0]) { 103 | // The specific format above is not particularly important. 104 | [NSException raise:NSInvalidArgumentException 105 | format:@"This object is not a valid plist: \n%@.", value]; 106 | } 107 | #endif 108 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 109 | [[self userDefaults] setObject:value forKey:defaultsKey]; 110 | if ([self shouldAutomaticallySynchronize]) { 111 | [self synchronize]; 112 | } 113 | NSDictionary *userInfo = @{PAPreferencesChangedPropertyKey: defaultsKey}; 114 | [[NSNotificationCenter defaultCenter] postNotificationName:PAPreferencesDidChangeNotification object:self userInfo:userInfo]; 115 | } 116 | 117 | NSURL *paprefURLGetter(id self, SEL _cmd) { 118 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 119 | return [[self userDefaults] URLForKey:defaultsKey]; 120 | } 121 | 122 | void paprefURLSetter(id self, SEL _cmd, NSURL *value) { 123 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 124 | [[self userDefaults] setURL:value forKey:defaultsKey]; 125 | if ([self shouldAutomaticallySynchronize]) { 126 | [self synchronize]; 127 | } 128 | [[NSNotificationCenter defaultCenter] postNotificationName:PAPreferencesDidChangeNotification object:self]; 129 | } 130 | 131 | NSArray *paprefArrayGetter(id self, SEL _cmd) { 132 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 133 | return [[self userDefaults] arrayForKey:defaultsKey]; 134 | } 135 | 136 | NSDictionary *paprefDictionaryGetter(id self, SEL _cmd) { 137 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 138 | return [[self userDefaults] dictionaryForKey:defaultsKey]; 139 | } 140 | 141 | NSData *paprefDataGetter(id self, SEL _cmd) { 142 | NSString *defaultsKey = defaultsKeyForSelector(_cmd); 143 | return [[self userDefaults] dataForKey:defaultsKey]; 144 | } 145 | 146 | NSString *paprefStringGetter(id self, SEL _cmd) { 147 | NSString *propertyDescriptorName = defaultsKeyForSelector(_cmd); 148 | return [[self userDefaults] stringForKey:propertyDescriptorName]; 149 | } 150 | 151 | NSDate *paprefDateGetter(id self, SEL _cmd) { 152 | NSString *propertyDescriptorName = defaultsKeyForSelector(_cmd); 153 | return [[self userDefaults] objectForKey:propertyDescriptorName]; 154 | } 155 | 156 | NSNumber *paprefNumberGetter(id self, SEL _cmd) { 157 | NSString *propertyDescriptorName = defaultsKeyForSelector(_cmd); 158 | return [[self userDefaults] objectForKey:propertyDescriptorName]; 159 | } 160 | 161 | id paprefCodableObjectGetter(id self, SEL _cmd) { 162 | id object = nil; 163 | NSData *data = paprefDataGetter(self, _cmd); 164 | if (data) { 165 | object = [NSKeyedUnarchiver unarchiveObjectWithData:data]; 166 | } 167 | return object; 168 | } 169 | 170 | void paprefCodableObjectSetter(id self, SEL _cmd, id value) { 171 | NSData *data = [NSKeyedArchiver archivedDataWithRootObject:value]; 172 | paprefObjectSetter(self, _cmd, data); 173 | } 174 | 175 | 176 | @implementation PAPreferences 177 | 178 | + (instancetype)sharedInstance { 179 | static PAPreferences *_sharedInstance; 180 | static dispatch_once_t onceToken; 181 | dispatch_once(&onceToken, ^{ 182 | _sharedInstance = [[self alloc] init]; 183 | }); 184 | 185 | return _sharedInstance; 186 | } 187 | 188 | + (NSString *)defaultsKeyForPropertyName:(NSString *)key { 189 | return key; 190 | } 191 | 192 | // Cause KVO notifications to be emitted even when the corresponding 193 | // value for key is set in NSUserDefaults directly. 194 | + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key 195 | { 196 | NSSet *result = [super keyPathsForValuesAffectingValueForKey:key]; 197 | 198 | PAPropertyDescriptor *propertyDescriptor = _dynamicProperties[key]; 199 | NSString *defaultsKey = [self defaultsKeyForPropertyName:key]; 200 | if (!propertyDescriptor || !defaultsKey) return result; 201 | 202 | NSString *keyPath = [NSString stringWithFormat:@"userDefaults.%@", defaultsKey]; 203 | return [result setByAddingObject:keyPath]; 204 | } 205 | 206 | - (instancetype)init { 207 | if (self = [super init]) { 208 | _shouldAutomaticallySynchronize = YES; 209 | _dynamicProperties = [[NSMutableDictionary alloc] init]; 210 | unsigned int cProps; 211 | objc_property_t *properties = class_copyPropertyList([self class], &cProps); 212 | for (int i=0; i 10 | 11 | @interface PAPropertyDescriptor : NSObject 12 | 13 | @property (nonatomic, readonly) NSString *defaultsKey; 14 | @property (nonatomic, readonly) BOOL isSetter; 15 | @property (nonatomic, readonly) NSString *type; 16 | 17 | - (id)initWithDefaultsKey:(NSString *)defaultsKey type:(NSString *)type isSetter:(BOOL)isSetter; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /PAPreferences/PAPropertyDescriptor.m: -------------------------------------------------------------------------------- 1 | // 2 | // PAPropertyDescriptor.m 3 | // PAPreferencesSample 4 | // 5 | // Created by Denis Hennessy on 25/01/2014. 6 | // Copyright (c) 2014 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import "PAPropertyDescriptor.h" 10 | 11 | @implementation PAPropertyDescriptor 12 | 13 | - (id)initWithDefaultsKey:(NSString *)defaultsKey type:(NSString *)type isSetter:(BOOL)isSetter { 14 | if (self = [super init]) { 15 | _defaultsKey = defaultsKey; 16 | _type = type; 17 | _isSetter = isSetter; 18 | } 19 | return self; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /PAPreferencesTests/PAPreferencesTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // PAPreferencesTests.m 3 | // PAPreferencesSample 4 | // 5 | // Created by Denis Hennessy on 23/01/2014. 6 | // Copyright (c) 2014 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "PAPreferences.h" 11 | 12 | NSString * const RemappedTitleKey = @"KEY_TITLE"; 13 | 14 | @interface MyPreferences : PAPreferences 15 | @property (nonatomic, assign) NSString *username; 16 | @property (nonatomic, assign) NSArray *kids; 17 | @property (nonatomic, assign) NSDictionary *address; 18 | @property (nonatomic, assign) NSInteger age; 19 | @property (nonatomic, assign) BOOL isOK; 20 | @property (nonatomic, assign, getter=handle, setter=become:) NSString *name; 21 | @property (nonatomic, assign) NSData *data; 22 | @property (nonatomic, assign) float height; 23 | @property (nonatomic, assign) double tilt; 24 | @property (nonatomic, assign) NSURL *site; 25 | @property (nonatomic, assign) NSURLConnection *connection; // Invalid object type 26 | @property (nonatomic, retain) NSString *fruit; // Invalid retain specifier 27 | @property (nonatomic, assign) NSString *title; 28 | @property (nonatomic, assign) NSDate *date; 29 | @property (nonatomic, assign) NSNumber *number; 30 | @property (nonatomic, assign) NSValue *value; 31 | 32 | @property (nonatomic, readonly, assign) NSString *nickname; 33 | @end 34 | 35 | @implementation MyPreferences 36 | @dynamic username; 37 | @dynamic kids; 38 | @dynamic address; 39 | @dynamic age; 40 | @dynamic isOK; 41 | @dynamic name; 42 | @dynamic data; 43 | @dynamic height; 44 | @dynamic tilt; 45 | @dynamic site; 46 | @dynamic connection; 47 | @dynamic fruit; 48 | @dynamic title; 49 | @dynamic number; 50 | @dynamic value; 51 | 52 | - (NSString *)nickname { 53 | return self.username; 54 | } 55 | 56 | + (NSString *)defaultsKeyForPropertyName:(NSString *)name { 57 | if ([name isEqualToString:@"title"]) { 58 | return RemappedTitleKey; 59 | } 60 | return name; 61 | } 62 | 63 | @end 64 | 65 | static void * const PrivateKVOContext = (void*)&PrivateKVOContext; 66 | 67 | @interface PAPreferencesTests : XCTestCase { 68 | BOOL _seenNotification; 69 | NSString *_notificationChangedProperty; 70 | } 71 | 72 | @end 73 | 74 | @implementation PAPreferencesTests 75 | 76 | - (void)testArrayPersistence { 77 | MyPreferences *prefs = [MyPreferences sharedInstance]; 78 | prefs.kids = @[@"jack", @"jill"]; 79 | NSArray *kids = [[NSUserDefaults standardUserDefaults] objectForKey:@"kids"]; 80 | XCTAssertEqual(kids.count, 2); 81 | XCTAssertEqualObjects(kids[0], @"jack"); 82 | XCTAssertEqualObjects(kids[1], @"jill"); 83 | } 84 | 85 | - (void)testArrayRetrieval { 86 | MyPreferences *prefs = [MyPreferences sharedInstance]; 87 | prefs.kids = @[@"jack", @"jill"]; 88 | XCTAssertEqual(prefs.kids.count, 2); 89 | XCTAssertEqualObjects(prefs.kids[0], @"jack"); 90 | XCTAssertEqualObjects(prefs.kids[1], @"jill"); 91 | } 92 | 93 | - (void)testBoolPersistence { 94 | MyPreferences *prefs = [MyPreferences sharedInstance]; 95 | prefs.isOK = YES; 96 | XCTAssertEqual([[NSUserDefaults standardUserDefaults] integerForKey:@"isOK"], YES); 97 | } 98 | 99 | - (void)testBoolRetrieval { 100 | MyPreferences *prefs = [MyPreferences sharedInstance]; 101 | prefs.isOK = YES; 102 | XCTAssertEqual(prefs.isOK, YES); 103 | } 104 | 105 | - (void)testDataPersistence { 106 | MyPreferences *prefs = [MyPreferences sharedInstance]; 107 | NSData *data = [NSData dataWithBytes:"hello" length:5]; 108 | prefs.data = data; 109 | XCTAssertEqualObjects([[NSUserDefaults standardUserDefaults] dataForKey:@"data"], data); 110 | } 111 | 112 | - (void)testDataRetrieval { 113 | MyPreferences *prefs = [MyPreferences sharedInstance]; 114 | NSData *data = [NSData dataWithBytes:"hello" length:5]; 115 | prefs.data = data; 116 | XCTAssertEqualObjects(prefs.data, data); 117 | } 118 | 119 | - (void)testDateRetrieval { 120 | MyPreferences *prefs = [MyPreferences sharedInstance]; 121 | NSDate *date = [NSDate date]; 122 | prefs.date = date; 123 | XCTAssertEqualObjects(prefs.date, date); 124 | } 125 | 126 | - (void)testDictionaryPersistence { 127 | MyPreferences *prefs = [MyPreferences sharedInstance]; 128 | prefs.address = @{@"street": @"Main St", @"city": @"Venice"}; 129 | NSDictionary *address = [[NSUserDefaults standardUserDefaults] objectForKey:@"address"]; 130 | XCTAssertEqual(address.count, 2); 131 | XCTAssertEqualObjects(address[@"street"], @"Main St"); 132 | XCTAssertEqualObjects(address[@"city"], @"Venice"); 133 | } 134 | 135 | - (void)testDictionaryRetrieval { 136 | MyPreferences *prefs = [MyPreferences sharedInstance]; 137 | prefs.address = @{@"street": @"Main St", @"city": @"Venice"}; 138 | XCTAssertEqual(prefs.address.count, 2); 139 | XCTAssertEqualObjects(prefs.address[@"street"], @"Main St"); 140 | XCTAssertEqualObjects(prefs.address[@"city"], @"Venice"); 141 | } 142 | 143 | - (void)testDoubleRetrieval { 144 | MyPreferences *prefs = [MyPreferences sharedInstance]; 145 | prefs.tilt = 0.00000042; 146 | XCTAssertEqualWithAccuracy(prefs.tilt, 0.00000042, DBL_EPSILON); 147 | } 148 | 149 | - (void)testDoublePersistence { 150 | MyPreferences *prefs = [MyPreferences sharedInstance]; 151 | prefs.tilt = 0.00000042; 152 | XCTAssertEqualWithAccuracy([[NSUserDefaults standardUserDefaults] doubleForKey:@"tilt"], 0.00000042, DBL_EPSILON); 153 | } 154 | 155 | - (void)testFloatRetrieval { 156 | MyPreferences *prefs = [MyPreferences sharedInstance]; 157 | prefs.height = 4.2; 158 | XCTAssertEqualWithAccuracy(prefs.height, 4.2f, FLT_EPSILON); 159 | } 160 | 161 | - (void)testFloatPersistence { 162 | MyPreferences *prefs = [MyPreferences sharedInstance]; 163 | prefs.height = 4.2; 164 | XCTAssertEqualWithAccuracy([[NSUserDefaults standardUserDefaults] floatForKey:@"height"], 4.2f, FLT_EPSILON); 165 | } 166 | 167 | - (void)testIntegerPersistence { 168 | MyPreferences *prefs = [MyPreferences sharedInstance]; 169 | prefs.age = 42; 170 | XCTAssertEqual([[NSUserDefaults standardUserDefaults] integerForKey:@"age"], 42); 171 | } 172 | 173 | - (void)testIntegerRetrieval { 174 | MyPreferences *prefs = [MyPreferences sharedInstance]; 175 | prefs.age = 42; 176 | XCTAssertEqual(prefs.age, 42); 177 | } 178 | 179 | - (void)testStringPersistence { 180 | MyPreferences *prefs = [MyPreferences sharedInstance]; 181 | prefs.username = @"alice"; 182 | XCTAssertEqualObjects([[NSUserDefaults standardUserDefaults] objectForKey:@"username"], @"alice"); 183 | } 184 | 185 | - (void)testDefaultsKeyForPropertyNamePersistence { 186 | MyPreferences *prefs = [MyPreferences sharedInstance]; 187 | prefs.title = @"yesterday"; 188 | XCTAssertEqualObjects([[NSUserDefaults standardUserDefaults] objectForKey:RemappedTitleKey], @"yesterday"); 189 | } 190 | 191 | - (void)testDefaultsKeyForPropertyNameRetrieval { 192 | MyPreferences *prefs = [MyPreferences sharedInstance]; 193 | [[NSUserDefaults standardUserDefaults] setObject:@"yesterday" forKey:RemappedTitleKey]; 194 | XCTAssertEqualObjects(prefs.title, @"yesterday"); 195 | } 196 | 197 | - (void)testStringRetrieval { 198 | MyPreferences *prefs = [MyPreferences sharedInstance]; 199 | prefs.username = @"alice"; 200 | XCTAssertEqualObjects(prefs.username, @"alice"); 201 | } 202 | 203 | - (void)testAccessViaInstanceMethod { 204 | MyPreferences *prefs = [MyPreferences sharedInstance]; 205 | [[NSUserDefaults standardUserDefaults] setObject:@"bob" forKey:@"username"]; 206 | XCTAssertEqualObjects(prefs.nickname, @"bob"); 207 | } 208 | 209 | - (void)testNumberPersistence { 210 | MyPreferences *prefs = [MyPreferences sharedInstance]; 211 | NSNumber *number = @(23); 212 | prefs.number = number; 213 | NSNumber *defaultsNumber = [[NSUserDefaults standardUserDefaults] objectForKey:@"number"]; 214 | XCTAssertEqualObjects(defaultsNumber, number); 215 | } 216 | 217 | - (void)testNumberRetrieval { 218 | MyPreferences *prefs = [MyPreferences sharedInstance]; 219 | NSNumber *number = @(23); 220 | prefs.number = number; 221 | XCTAssertEqualObjects(prefs.number, number); 222 | } 223 | 224 | - (void)testUrlPersistence { 225 | MyPreferences *prefs = [MyPreferences sharedInstance]; 226 | prefs.site = [NSURL URLWithString:@"http://apple.com"]; 227 | XCTAssertEqualObjects([[NSUserDefaults standardUserDefaults] URLForKey:@"site"], [NSURL URLWithString:@"http://apple.com"]); 228 | } 229 | 230 | - (void)testUrlRetrieval { 231 | MyPreferences *prefs = [MyPreferences sharedInstance]; 232 | prefs.site = [NSURL URLWithString:@"http://apple.com"]; 233 | XCTAssertEqualObjects(prefs.site, [NSURL URLWithString:@"http://apple.com"]); 234 | } 235 | 236 | - (void)testCodableObjectPersistence { 237 | MyPreferences *prefs = [MyPreferences sharedInstance]; 238 | NSValue *value = [NSValue valueWithRange:NSMakeRange(0, 1)]; 239 | prefs.value = value; 240 | NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:@"value"]; 241 | XCTAssertEqualObjects([NSKeyedUnarchiver unarchiveObjectWithData:data], value); 242 | } 243 | 244 | - (void)testCodableObjectRetrieval { 245 | MyPreferences *prefs = [MyPreferences sharedInstance]; 246 | NSValue *value = [NSValue valueWithRange:NSMakeRange(0, 1)]; 247 | prefs.value = value; 248 | XCTAssertEqualObjects(prefs.value, value); 249 | } 250 | 251 | 252 | - (void)testPropertyRemoval { 253 | MyPreferences *prefs = [MyPreferences sharedInstance]; 254 | prefs.username = @"alice"; 255 | XCTAssertEqualObjects(prefs.username, @"alice"); 256 | prefs.username = nil; 257 | XCTAssertNil(prefs.username); 258 | XCTAssertNil([[NSUserDefaults standardUserDefaults] objectForKey:@"username"]); 259 | } 260 | 261 | - (void)testCustomSetter { 262 | MyPreferences *prefs = [MyPreferences sharedInstance]; 263 | [prefs become:@"joe"]; 264 | XCTAssertEqualObjects([[NSUserDefaults standardUserDefaults] objectForKey:@"name"], @"joe"); 265 | } 266 | 267 | - (void)testCustomGetter { 268 | MyPreferences *prefs = [MyPreferences sharedInstance]; 269 | [prefs become:@"joe"]; 270 | XCTAssertEqualObjects(prefs.handle, @"joe"); 271 | } 272 | 273 | - (void)testValidRespondsToSelector { 274 | id prefs = [MyPreferences sharedInstance]; 275 | XCTAssertTrue([prefs respondsToSelector:@selector(setUsername:)]); 276 | } 277 | 278 | - (void)testInvalidRespondsToSelector { 279 | id prefs = [MyPreferences sharedInstance]; 280 | #pragma clang diagnostic push 281 | #pragma clang diagnostic ignored "-Wundeclared-selector" 282 | XCTAssertFalse([prefs respondsToSelector:@selector(setBogus:)]); 283 | #pragma clang diagnostic pop 284 | } 285 | 286 | - (void)testInvalidRetain { 287 | id prefs = [MyPreferences sharedInstance]; 288 | XCTAssertFalse([prefs respondsToSelector:@selector(setFruit:)]); 289 | } 290 | 291 | - (void)testInvalidType { 292 | id prefs = [MyPreferences sharedInstance]; 293 | XCTAssertFalse([prefs respondsToSelector:@selector(setConnection:)]); 294 | } 295 | 296 | - (void)testNotificationPosted { 297 | _seenNotification = NO; 298 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:PAPreferencesDidChangeNotification object:nil]; 299 | MyPreferences *prefs = [MyPreferences sharedInstance]; 300 | prefs.username = @"bill"; 301 | XCTAssertTrue(_seenNotification); 302 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 303 | } 304 | 305 | - (void)testNotificationUserInfo { 306 | _notificationChangedProperty = nil; 307 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:PAPreferencesDidChangeNotification object:nil]; 308 | MyPreferences *prefs = [MyPreferences sharedInstance]; 309 | prefs.username = @"jack"; 310 | XCTAssertTrue([_notificationChangedProperty isEqualToString:@"username"]); 311 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 312 | } 313 | 314 | - (void)testAutoSynchronizeDefaultsOn { 315 | MyPreferences *prefs = [MyPreferences sharedInstance]; 316 | XCTAssertTrue(prefs.shouldAutomaticallySynchronize); 317 | } 318 | 319 | - (void)handleNotification:(NSNotification *)notification { 320 | _seenNotification = YES; 321 | _notificationChangedProperty = notification.userInfo[PAPreferencesChangedPropertyKey]; 322 | } 323 | 324 | #if DEBUG 325 | - (void)testInvalidPropertyListPersistence { 326 | MyPreferences *prefs = [MyPreferences sharedInstance]; 327 | NSValue *value = [NSValue valueWithRange:NSMakeRange(0, 1)]; 328 | XCTAssertThrowsSpecificNamed(prefs.address = @{@"dictKey": value}, NSException, NSInvalidArgumentException); 329 | } 330 | #endif 331 | 332 | #pragma mark - KVO 333 | 334 | - (void)testObserveArrayValue { 335 | MyPreferences *prefs = [MyPreferences sharedInstance]; 336 | 337 | prefs.kids = @[@"tom", @"jerry"]; 338 | 339 | [prefs addObserver:self 340 | forKeyPath:@"kids" 341 | options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 342 | context:PrivateKVOContext]; 343 | 344 | prefs.kids = @[@"jack", @"jill"]; 345 | } 346 | 347 | - (void)testObserveBoolValue { 348 | MyPreferences *prefs = [MyPreferences sharedInstance]; 349 | prefs.isOK = NO; 350 | 351 | [prefs addObserver:self 352 | forKeyPath:@"isOK" 353 | options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 354 | context:PrivateKVOContext]; 355 | 356 | prefs.isOK = YES; 357 | } 358 | 359 | - (void)testObserveDataValue { 360 | MyPreferences *prefs = [MyPreferences sharedInstance]; 361 | prefs.isOK = NO; 362 | 363 | [prefs addObserver:self 364 | forKeyPath:@"data" 365 | options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 366 | context:PrivateKVOContext]; 367 | 368 | prefs.data = [NSData dataWithBytes:"hello" length:5]; 369 | } 370 | 371 | - (void)testObserveDictionaryValue { 372 | MyPreferences *prefs = [MyPreferences sharedInstance]; 373 | 374 | prefs.address = @{@"street": @"Main St", @"city": @"Disneyland"}; 375 | 376 | [prefs addObserver:self 377 | forKeyPath:@"address" 378 | options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 379 | context:PrivateKVOContext]; 380 | 381 | prefs.address = @{@"street": @"Main St", @"city": @"Venice"}; 382 | } 383 | 384 | - (void)testObserveDoubleValue { 385 | MyPreferences *prefs = [MyPreferences sharedInstance]; 386 | 387 | prefs.tilt = 0.00000041; 388 | 389 | [prefs addObserver:self 390 | forKeyPath:@"tilt" 391 | options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 392 | context:PrivateKVOContext]; 393 | 394 | prefs.tilt = 0.00000042; 395 | } 396 | 397 | - (void)testObserveFloatValue { 398 | MyPreferences *prefs = [MyPreferences sharedInstance]; 399 | 400 | prefs.height = 4.1; 401 | 402 | [prefs addObserver:self 403 | forKeyPath:@"height" 404 | options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 405 | context:PrivateKVOContext]; 406 | 407 | prefs.height = 4.2; 408 | } 409 | 410 | - (void)testObserveIntegerValue { 411 | MyPreferences *prefs = [MyPreferences sharedInstance]; 412 | 413 | prefs.age = 41; 414 | 415 | [prefs addObserver:self 416 | forKeyPath:@"age" 417 | options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 418 | context:PrivateKVOContext]; 419 | 420 | prefs.age = 42; 421 | } 422 | 423 | - (void)testObserveStringValue { 424 | MyPreferences *prefs = [MyPreferences sharedInstance]; 425 | 426 | prefs.username = @"alice"; 427 | 428 | [prefs addObserver:self 429 | forKeyPath:@"username" 430 | options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 431 | context:PrivateKVOContext]; 432 | 433 | prefs.username = @"bob"; 434 | } 435 | 436 | - (void)testObserveNumberValue { 437 | MyPreferences *prefs = [MyPreferences sharedInstance]; 438 | 439 | prefs.number = @1; 440 | 441 | [prefs addObserver:self 442 | forKeyPath:@"number" 443 | options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 444 | context:PrivateKVOContext]; 445 | 446 | prefs.number = @2; 447 | } 448 | 449 | - (void)testObserveURLValue { 450 | MyPreferences *prefs = [MyPreferences sharedInstance]; 451 | 452 | prefs.site = [NSURL URLWithString:@"http://google.com"]; 453 | 454 | [prefs addObserver:self 455 | forKeyPath:@"site" 456 | options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 457 | context:PrivateKVOContext]; 458 | 459 | prefs.site = [NSURL URLWithString:@"http://apple.com"]; 460 | } 461 | 462 | - (void)testObserveCodableObject { 463 | MyPreferences *prefs = [MyPreferences sharedInstance]; 464 | 465 | prefs.value = [NSValue valueWithRange:NSMakeRange(0, 1)]; 466 | 467 | [prefs addObserver:self 468 | forKeyPath:@"value" 469 | options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew 470 | context:PrivateKVOContext]; 471 | 472 | prefs.value = [NSValue valueWithRange:NSMakeRange(0, 2)]; 473 | } 474 | 475 | - (void)observeValueForKeyPath:(NSString *)keyPath 476 | ofObject:(id)object 477 | change:(NSDictionary *)change 478 | context:(void *)context { 479 | 480 | if (context == PrivateKVOContext) { 481 | id oldValue = change[NSKeyValueChangeOldKey]; 482 | id newValue = change[NSKeyValueChangeNewKey]; 483 | 484 | if ([keyPath isEqualToString:@"kids"]) { 485 | XCTAssertEqualObjects(oldValue[0], @"tom"); 486 | XCTAssertEqualObjects(oldValue[1], @"jerry"); 487 | XCTAssertEqualObjects(newValue[0], @"jack"); 488 | XCTAssertEqualObjects(newValue[1], @"jill"); 489 | } else if ([keyPath isEqualToString:@"data"]) { 490 | XCTAssertEqualObjects(newValue, [NSData dataWithBytes:"hello" length:5]); 491 | } else if ([keyPath isEqualToString:@"username"]) { 492 | XCTAssertEqualObjects(oldValue, @"alice"); 493 | XCTAssertEqualObjects(newValue, @"bob"); 494 | } else if ([keyPath isEqualToString:@"isOK"]) { 495 | XCTAssertFalse([oldValue boolValue]); 496 | XCTAssertTrue([newValue boolValue]); 497 | } else if ([keyPath isEqualToString:@"number"]) { 498 | XCTAssertEqualObjects(oldValue, @1); 499 | XCTAssertEqualObjects(newValue, @2); 500 | } else if ([keyPath isEqualToString:@"address"]) { 501 | XCTAssertEqualObjects(oldValue[@"street"], @"Main St"); 502 | XCTAssertEqualObjects(oldValue[@"city"], @"Disneyland"); 503 | XCTAssertEqualObjects(newValue[@"street"], @"Main St"); 504 | XCTAssertEqualObjects(newValue[@"city"], @"Venice"); 505 | } else if ([keyPath isEqualToString:@"tilt"]) { 506 | XCTAssertEqualWithAccuracy([oldValue doubleValue], 0.00000041, DBL_EPSILON); 507 | XCTAssertEqualWithAccuracy([newValue doubleValue], 0.00000042, DBL_EPSILON); 508 | } else if ([keyPath isEqualToString:@"height"]) { 509 | XCTAssertEqualWithAccuracy([oldValue floatValue], 4.1f, FLT_EPSILON); 510 | XCTAssertEqualWithAccuracy([newValue floatValue], 4.2f, FLT_EPSILON); 511 | } else if ([keyPath isEqualToString:@"age"]) { 512 | XCTAssertEqual([oldValue integerValue], 41); 513 | XCTAssertEqual([newValue integerValue], 42); 514 | } else if ([keyPath isEqualToString:@"username"]) { 515 | XCTAssertEqualObjects(oldValue, nil); 516 | XCTAssertEqualObjects(newValue, @"alice"); 517 | } else if ([keyPath isEqualToString:@"site"]) { 518 | XCTAssertEqualObjects(oldValue, [NSURL URLWithString:@"http://google.com"]); 519 | XCTAssertEqualObjects(newValue, [NSURL URLWithString:@"http://apple.com"]); 520 | } else if ([keyPath isEqualToString:@"value"]) { 521 | XCTAssertEqualObjects(oldValue, [NSValue valueWithRange:NSMakeRange(0, 1)]); 522 | XCTAssertEqualObjects(newValue, [NSValue valueWithRange:NSMakeRange(0, 2)]); 523 | } 524 | } else { 525 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 526 | } 527 | } 528 | 529 | @end 530 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PAPreferences 2 | 3 | An easy way to store user preferences using NSUserDefaults. 4 | 5 | PAPreferences maps `dynamic` properties onto NSUserDefaults getters and setters so that you can access defaults as if they were regular properties on an object. That object is normally a singleton since you typically want a single set of preferences for the entire app. 6 | 7 | Works with iOS and OSX. 8 | 9 | ## Adding PAPreferences to your project 10 | 11 | The simplest way to add PAPreferences to your project is to use [CocoaPods](http://cocoapods.org). 12 | Add the following line to your Podfile: 13 | 14 | ``` 15 | pod 'PAPreferences' 16 | ``` 17 | 18 | If you'd prefer to manually integrate it, just copy `PAPreferences/*.{m,h}` into your project. 19 | 20 | ## Creating a Preferences Singleton 21 | 22 | First, create a subclass of PAPreferences with properties for your settings (note that all properties should have an `assign` storage specifier): 23 | 24 | ```objective-c 25 | #import "PAPreferences.h" 26 | 27 | @interface MyPreferences : PAPreferences 28 | @property (nonatomic, assign) NSString *theme; 29 | @property (nonatomic, assign) NSArray *favorites; 30 | @property (nonatomic, assign) BOOL hasSeenIntro; 31 | @end 32 | ``` 33 | 34 | In the implementation file, mark each property as dynamic: 35 | 36 | ```objective-c 37 | #import "MyPreferences.h" 38 | 39 | @implementation MyPreferences 40 | @dynamic theme; 41 | @dynamic favorites; 42 | @dynamic hasSeenIntro; 43 | @end 44 | ``` 45 | 46 | ## Accessing the Preferences 47 | 48 | The `sharedInstance` class method can be used to access the singleton: 49 | 50 | ```objective-c 51 | if (![MyPreferences sharedInstance].hasSeenIntro) { 52 | // ... 53 | [MyPreferences sharedInstance].hasSeenIntro = YES; 54 | } 55 | ``` 56 | 57 | ## Supported Property Types 58 | 59 | PAPreferences supports the following property types: 60 | 61 | * NSInteger 62 | * NSString 63 | * NSArray 64 | * NSDictionary 65 | * NSURL 66 | * NSData 67 | * NSDate 68 | * NSNumber 69 | * BOOL 70 | * float 71 | * double 72 | * Classes conforming to the NSCoding protocol (including NSSecureCoding). 73 | 74 | While you can set mutable values for the properties, you will currently get immutable copies back. Just like when using NSUserDefaults directly. 75 | 76 | ## Updating UI when Preferences change 77 | 78 | Whenever a change is made to a property, a `PAPreferencesDidChangeNotification` notification is posted (with its object set to the PAPreferences subclass). 79 | 80 | The class is Key-Value-Coding compliant, so you can register as an observer for an individual property, and be notified when it changes using the standard KVO mechanism. 81 | 82 | ## How It Works 83 | 84 | When a property is first accessed, that selector is mapped to a method that interacts with the NSUserDefaults class. For example, this line: 85 | 86 | ```objective-c 87 | hasSeenIntro = YES; 88 | ``` 89 | 90 | expands to a call to a method that behaves like this: 91 | 92 | ```objective-c 93 | - (void)setHasSeenIntro:(BOOL)value { 94 | [[NSUserDefaults standardUserDefaults] setBool:value forKey:@"hasSeenIntro"]; 95 | [[NSNotificationCenter defaultCenter] postNotificationName:PAPreferencesDidChangeNotification object:self]; 96 | } 97 | ``` 98 | 99 | ## Setting Defaults 100 | 101 | I prefer to set defaults in code, rather than using the traditional NSUserDefaults method. This is also a useful mechanism to set the first and last version installed for your app. Having these values lets you easily migrate existing users as you add new preferences. Here's an example from my Focus Time app: 102 | 103 | - (id)init { 104 | if (self = [super init]) { 105 | NSString *version = [NSBundle mainBundle].infoDictionary[@"CFBundleShortVersionString"]; 106 | if (self.firstVersionInstalled == nil) { 107 | self.firstVersionInstalled = version; 108 | 109 | // Set defaults for new users 110 | self.pomodoroLength = 1500; 111 | self.workStartSound = @"alert_gentle_jingle"; 112 | self.workEndSound = @"alert_ring"; 113 | // . . . 114 | } 115 | 116 | self.lastVersionInstalled = version; 117 | } 118 | return self; 119 | } 120 | 121 | ## Working with iOS Extensions 122 | 123 | To share settings between your host app and extension on iOS, you have to use an instance of NSUserDefaults created with `initWithSuiteName:` rather than `standardUserDefaults`. To support this, a subclass of PAPreferences can provide a `userDefaults` implementation of override the default. This is also a good way of caching the instance. Here's a sample implementation: 124 | 125 | ```objective-c 126 | - (NSUserDefaults *)userDefaults { 127 | static NSUserDefaults *_cachedDefaults; 128 | static dispatch_once_t onceToken; 129 | dispatch_once(&onceToken, ^{ 130 | _cachedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.peerassembly.myapp"]; 131 | }); 132 | 133 | return _cachedDefaults; 134 | } 135 | ``` 136 | 137 | 138 | ## Samples 139 | 140 | The best examples of how to use the library are in the unit tests - `PAPreferencesTests.m`. However, there's a simple example preferences file also included in the iOS sample. 141 | 142 | ## Troubleshooting 143 | If you define a property but then forget to add the `@dynamic` line to its implementation file, then everything will appear to work but in reality the compiler is creating an in-memory storage for the property (as if you'd used a `@synthesize` line. These properties won't get saved to NSUserDefaults. To protect against this, it's good practice to wrap your class declaration with: 144 | 145 | ```objective-c 146 | #pragma clang diagnostic push 147 | #pragma clang diagnostic error "-Wobjc-missing-property-synthesis" 148 | ``` 149 | 150 | and 151 | 152 | ```objective-c 153 | #pragma clang diagnostic pop 154 | ``` 155 | 156 | ## Changelog 157 | 158 | ### 0.5 159 | * Add the defaults keys to a userInfo dictionary that is passed to NSNotification when a property changes (thanks Jacob Rhoda) 160 | * Fix obscure crash in optimized code build (thanks YuanMing.Zhang) 161 | * When Code Generation Optimization Level is set in the Xcode project and the user also defines a custom getter selector name for the PAPreferences subclass's property, it will crash!!! This is because when the getter name is assigned a new value, the name will release immediately due to the code optimization. 162 | 163 | Thanks to @zyuanming and @jadar for pull requests. 164 | 165 | ### 0.4 166 | * Add ability to specify an instance of NSUserDefaults other than the standard one. This is important if you're sharing settings with an Extension on iOS 167 | * Fix iOS sample so that unit tests could run on both iOS and OSX (previously, the tests failed on iOS because the initial view controller accessed the singleton before the test ran). 168 | 169 | ### 0.3 170 | * Added automatic synchronize call each time a property is updated (along with a flag to turn it off) 171 | * Add support for NSDate and NSCoding 172 | * Refactor getter & setter helper functions 173 | 174 | Thanks to @Janx2 and @creatd for pull requests. 175 | 176 | ### 0.2 177 | * Remove bogus warning for unsupported types 178 | 179 | ### 0.1 180 | * Initial release 181 | 182 | ## Contact 183 | 184 | To hear about updates to this and other libraries follow me on Twitter ([@denishennessy](http://twitter.com/denishennessy)) or App.net ([@denishennessy](http://alpha.app.net/denishennessy)). 185 | 186 | If you encounter a bug or just thought of a terrific new feature, then opening a github issue is probably the best way to share it. 187 | Actually, the best way is to send me a pull request... 188 | 189 | For anything else, email always works: [denis@peerassembly.com](mailto:denis@peerassembly.com) 190 | 191 | ## License 192 | 193 | ``` 194 | Copyright (c) 2014, Denis Hennessy (Peer Assembly - http://peerassembly.com) 195 | All rights reserved. 196 | 197 | Redistribution and use in source and binary forms, with or without 198 | modification, are permitted provided that the following conditions are met: 199 | * Redistributions of source code must retain the above copyright 200 | notice, this list of conditions and the following disclaimer. 201 | * Redistributions in binary form must reproduce the above copyright 202 | notice, this list of conditions and the following disclaimer in the 203 | documentation and/or other materials provided with the distribution. 204 | * Neither the name of Peer Assembly, Denis Hennessy nor the 205 | names of its contributors may be used to endorse or promote products 206 | derived from this software without specific prior written permission. 207 | 208 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 209 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 210 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 211 | DISCLAIMED. IN NO EVENT SHALL PEER ASSEMBLY OR DENIS HENNESSY BE LIABLE FOR ANY 212 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 213 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 214 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 215 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 216 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 217 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 218 | ``` 219 | 220 | -------------------------------------------------------------------------------- /Sample-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 928DC8B518EF48D600D0AC26 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 928DC8B418EF48D600D0AC26 /* Cocoa.framework */; }; 11 | 928DC8BF18EF48D600D0AC26 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 928DC8BD18EF48D600D0AC26 /* InfoPlist.strings */; }; 12 | 928DC8C118EF48D600D0AC26 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 928DC8C018EF48D600D0AC26 /* main.m */; }; 13 | 928DC8C518EF48D600D0AC26 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 928DC8C318EF48D600D0AC26 /* Credits.rtf */; }; 14 | 928DC8C818EF48D600D0AC26 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 928DC8C718EF48D600D0AC26 /* AppDelegate.m */; }; 15 | 928DC8CB18EF48D600D0AC26 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 928DC8C918EF48D600D0AC26 /* MainMenu.xib */; }; 16 | 928DC8CD18EF48D600D0AC26 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 928DC8CC18EF48D600D0AC26 /* Images.xcassets */; }; 17 | 928DC8D418EF48D600D0AC26 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 928DC8D318EF48D600D0AC26 /* XCTest.framework */; }; 18 | 928DC8D518EF48D600D0AC26 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 928DC8B418EF48D600D0AC26 /* Cocoa.framework */; }; 19 | 928DC8DD18EF48D600D0AC26 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 928DC8DB18EF48D600D0AC26 /* InfoPlist.strings */; }; 20 | 928DC8ED18EF491F00D0AC26 /* PAPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 928DC8EA18EF491F00D0AC26 /* PAPreferences.m */; }; 21 | 928DC8EE18EF491F00D0AC26 /* PAPropertyDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 928DC8EC18EF491F00D0AC26 /* PAPropertyDescriptor.m */; }; 22 | 928DC8F318EF49D000D0AC26 /* PAPreferencesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 928DC8F218EF49D000D0AC26 /* PAPreferencesTests.m */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | 928DC8D618EF48D600D0AC26 /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = 928DC8A918EF48D600D0AC26 /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = 928DC8B018EF48D600D0AC26; 31 | remoteInfo = PAPreferencesSampleOSX; 32 | }; 33 | /* End PBXContainerItemProxy section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 928DC8B118EF48D600D0AC26 /* PAPreferencesSampleOSX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PAPreferencesSampleOSX.app; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 928DC8B418EF48D600D0AC26 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 38 | 928DC8B718EF48D600D0AC26 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; 39 | 928DC8B818EF48D600D0AC26 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; 40 | 928DC8B918EF48D600D0AC26 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 41 | 928DC8BC18EF48D600D0AC26 /* PAPreferencesSampleOSX-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PAPreferencesSampleOSX-Info.plist"; sourceTree = ""; }; 42 | 928DC8BE18EF48D600D0AC26 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 43 | 928DC8C018EF48D600D0AC26 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 44 | 928DC8C218EF48D600D0AC26 /* PAPreferencesSampleOSX-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PAPreferencesSampleOSX-Prefix.pch"; sourceTree = ""; }; 45 | 928DC8C418EF48D600D0AC26 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; 46 | 928DC8C618EF48D600D0AC26 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 47 | 928DC8C718EF48D600D0AC26 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 48 | 928DC8CA18EF48D600D0AC26 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 49 | 928DC8CC18EF48D600D0AC26 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 50 | 928DC8D218EF48D600D0AC26 /* PAPreferencesSampleOSXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PAPreferencesSampleOSXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 928DC8D318EF48D600D0AC26 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 52 | 928DC8DA18EF48D600D0AC26 /* PAPreferencesSampleOSXTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PAPreferencesSampleOSXTests-Info.plist"; sourceTree = ""; }; 53 | 928DC8DC18EF48D600D0AC26 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 54 | 928DC8E918EF491F00D0AC26 /* PAPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PAPreferences.h; path = ../../../PAPreferences/PAPreferences.h; sourceTree = ""; }; 55 | 928DC8EA18EF491F00D0AC26 /* PAPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PAPreferences.m; path = ../../../PAPreferences/PAPreferences.m; sourceTree = ""; }; 56 | 928DC8EB18EF491F00D0AC26 /* PAPropertyDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PAPropertyDescriptor.h; path = ../../../PAPreferences/PAPropertyDescriptor.h; sourceTree = ""; }; 57 | 928DC8EC18EF491F00D0AC26 /* PAPropertyDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PAPropertyDescriptor.m; path = ../../../PAPreferences/PAPropertyDescriptor.m; sourceTree = ""; }; 58 | 928DC8F218EF49D000D0AC26 /* PAPreferencesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PAPreferencesTests.m; path = ../../../PAPreferencesTests/PAPreferencesTests.m; sourceTree = ""; }; 59 | /* End PBXFileReference section */ 60 | 61 | /* Begin PBXFrameworksBuildPhase section */ 62 | 928DC8AE18EF48D600D0AC26 /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | 928DC8B518EF48D600D0AC26 /* Cocoa.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | 928DC8CF18EF48D600D0AC26 /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | 928DC8D518EF48D600D0AC26 /* Cocoa.framework in Frameworks */, 75 | 928DC8D418EF48D600D0AC26 /* XCTest.framework in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXFrameworksBuildPhase section */ 80 | 81 | /* Begin PBXGroup section */ 82 | 928DC8A818EF48D600D0AC26 = { 83 | isa = PBXGroup; 84 | children = ( 85 | 928DC8BA18EF48D600D0AC26 /* PAPreferencesSampleOSX */, 86 | 928DC8D818EF48D600D0AC26 /* PAPreferencesTests */, 87 | 928DC8B318EF48D600D0AC26 /* Frameworks */, 88 | 928DC8B218EF48D600D0AC26 /* Products */, 89 | ); 90 | sourceTree = ""; 91 | usesTabs = 0; 92 | }; 93 | 928DC8B218EF48D600D0AC26 /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 928DC8B118EF48D600D0AC26 /* PAPreferencesSampleOSX.app */, 97 | 928DC8D218EF48D600D0AC26 /* PAPreferencesSampleOSXTests.xctest */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 928DC8B318EF48D600D0AC26 /* Frameworks */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 928DC8B418EF48D600D0AC26 /* Cocoa.framework */, 106 | 928DC8D318EF48D600D0AC26 /* XCTest.framework */, 107 | 928DC8B618EF48D600D0AC26 /* Other Frameworks */, 108 | ); 109 | name = Frameworks; 110 | sourceTree = ""; 111 | }; 112 | 928DC8B618EF48D600D0AC26 /* Other Frameworks */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 928DC8B718EF48D600D0AC26 /* AppKit.framework */, 116 | 928DC8B818EF48D600D0AC26 /* CoreData.framework */, 117 | 928DC8B918EF48D600D0AC26 /* Foundation.framework */, 118 | ); 119 | name = "Other Frameworks"; 120 | sourceTree = ""; 121 | }; 122 | 928DC8BA18EF48D600D0AC26 /* PAPreferencesSampleOSX */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 928DC8E818EF490E00D0AC26 /* PAPreferences */, 126 | 928DC8C618EF48D600D0AC26 /* AppDelegate.h */, 127 | 928DC8C718EF48D600D0AC26 /* AppDelegate.m */, 128 | 928DC8C918EF48D600D0AC26 /* MainMenu.xib */, 129 | 928DC8CC18EF48D600D0AC26 /* Images.xcassets */, 130 | 928DC8BB18EF48D600D0AC26 /* Supporting Files */, 131 | ); 132 | path = PAPreferencesSampleOSX; 133 | sourceTree = ""; 134 | }; 135 | 928DC8BB18EF48D600D0AC26 /* Supporting Files */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 928DC8BC18EF48D600D0AC26 /* PAPreferencesSampleOSX-Info.plist */, 139 | 928DC8BD18EF48D600D0AC26 /* InfoPlist.strings */, 140 | 928DC8C018EF48D600D0AC26 /* main.m */, 141 | 928DC8C218EF48D600D0AC26 /* PAPreferencesSampleOSX-Prefix.pch */, 142 | 928DC8C318EF48D600D0AC26 /* Credits.rtf */, 143 | ); 144 | name = "Supporting Files"; 145 | sourceTree = ""; 146 | }; 147 | 928DC8D818EF48D600D0AC26 /* PAPreferencesTests */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 928DC8F218EF49D000D0AC26 /* PAPreferencesTests.m */, 151 | 928DC8D918EF48D600D0AC26 /* Supporting Files */, 152 | ); 153 | name = PAPreferencesTests; 154 | path = PAPreferencesSampleOSXTests; 155 | sourceTree = ""; 156 | }; 157 | 928DC8D918EF48D600D0AC26 /* Supporting Files */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 928DC8DA18EF48D600D0AC26 /* PAPreferencesSampleOSXTests-Info.plist */, 161 | 928DC8DB18EF48D600D0AC26 /* InfoPlist.strings */, 162 | ); 163 | name = "Supporting Files"; 164 | sourceTree = ""; 165 | }; 166 | 928DC8E818EF490E00D0AC26 /* PAPreferences */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 928DC8E918EF491F00D0AC26 /* PAPreferences.h */, 170 | 928DC8EA18EF491F00D0AC26 /* PAPreferences.m */, 171 | 928DC8EB18EF491F00D0AC26 /* PAPropertyDescriptor.h */, 172 | 928DC8EC18EF491F00D0AC26 /* PAPropertyDescriptor.m */, 173 | ); 174 | name = PAPreferences; 175 | sourceTree = ""; 176 | }; 177 | /* End PBXGroup section */ 178 | 179 | /* Begin PBXNativeTarget section */ 180 | 928DC8B018EF48D600D0AC26 /* PAPreferencesSampleOSX */ = { 181 | isa = PBXNativeTarget; 182 | buildConfigurationList = 928DC8E218EF48D600D0AC26 /* Build configuration list for PBXNativeTarget "PAPreferencesSampleOSX" */; 183 | buildPhases = ( 184 | 928DC8AD18EF48D600D0AC26 /* Sources */, 185 | 928DC8AE18EF48D600D0AC26 /* Frameworks */, 186 | 928DC8AF18EF48D600D0AC26 /* Resources */, 187 | ); 188 | buildRules = ( 189 | ); 190 | dependencies = ( 191 | ); 192 | name = PAPreferencesSampleOSX; 193 | productName = PAPreferencesSampleOSX; 194 | productReference = 928DC8B118EF48D600D0AC26 /* PAPreferencesSampleOSX.app */; 195 | productType = "com.apple.product-type.application"; 196 | }; 197 | 928DC8D118EF48D600D0AC26 /* PAPreferencesSampleOSXTests */ = { 198 | isa = PBXNativeTarget; 199 | buildConfigurationList = 928DC8E518EF48D600D0AC26 /* Build configuration list for PBXNativeTarget "PAPreferencesSampleOSXTests" */; 200 | buildPhases = ( 201 | 928DC8CE18EF48D600D0AC26 /* Sources */, 202 | 928DC8CF18EF48D600D0AC26 /* Frameworks */, 203 | 928DC8D018EF48D600D0AC26 /* Resources */, 204 | ); 205 | buildRules = ( 206 | ); 207 | dependencies = ( 208 | 928DC8D718EF48D600D0AC26 /* PBXTargetDependency */, 209 | ); 210 | name = PAPreferencesSampleOSXTests; 211 | productName = PAPreferencesSampleOSXTests; 212 | productReference = 928DC8D218EF48D600D0AC26 /* PAPreferencesSampleOSXTests.xctest */; 213 | productType = "com.apple.product-type.bundle.unit-test"; 214 | }; 215 | /* End PBXNativeTarget section */ 216 | 217 | /* Begin PBXProject section */ 218 | 928DC8A918EF48D600D0AC26 /* Project object */ = { 219 | isa = PBXProject; 220 | attributes = { 221 | LastUpgradeCheck = 0730; 222 | ORGANIZATIONNAME = "Denis Hennessy"; 223 | TargetAttributes = { 224 | 928DC8D118EF48D600D0AC26 = { 225 | TestTargetID = 928DC8B018EF48D600D0AC26; 226 | }; 227 | }; 228 | }; 229 | buildConfigurationList = 928DC8AC18EF48D600D0AC26 /* Build configuration list for PBXProject "PAPreferencesSampleOSX" */; 230 | compatibilityVersion = "Xcode 3.2"; 231 | developmentRegion = English; 232 | hasScannedForEncodings = 0; 233 | knownRegions = ( 234 | en, 235 | Base, 236 | ); 237 | mainGroup = 928DC8A818EF48D600D0AC26; 238 | productRefGroup = 928DC8B218EF48D600D0AC26 /* Products */; 239 | projectDirPath = ""; 240 | projectRoot = ""; 241 | targets = ( 242 | 928DC8B018EF48D600D0AC26 /* PAPreferencesSampleOSX */, 243 | 928DC8D118EF48D600D0AC26 /* PAPreferencesSampleOSXTests */, 244 | ); 245 | }; 246 | /* End PBXProject section */ 247 | 248 | /* Begin PBXResourcesBuildPhase section */ 249 | 928DC8AF18EF48D600D0AC26 /* Resources */ = { 250 | isa = PBXResourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | 928DC8BF18EF48D600D0AC26 /* InfoPlist.strings in Resources */, 254 | 928DC8CD18EF48D600D0AC26 /* Images.xcassets in Resources */, 255 | 928DC8C518EF48D600D0AC26 /* Credits.rtf in Resources */, 256 | 928DC8CB18EF48D600D0AC26 /* MainMenu.xib in Resources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | 928DC8D018EF48D600D0AC26 /* Resources */ = { 261 | isa = PBXResourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | 928DC8DD18EF48D600D0AC26 /* InfoPlist.strings in Resources */, 265 | ); 266 | runOnlyForDeploymentPostprocessing = 0; 267 | }; 268 | /* End PBXResourcesBuildPhase section */ 269 | 270 | /* Begin PBXSourcesBuildPhase section */ 271 | 928DC8AD18EF48D600D0AC26 /* Sources */ = { 272 | isa = PBXSourcesBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | 928DC8ED18EF491F00D0AC26 /* PAPreferences.m in Sources */, 276 | 928DC8C818EF48D600D0AC26 /* AppDelegate.m in Sources */, 277 | 928DC8EE18EF491F00D0AC26 /* PAPropertyDescriptor.m in Sources */, 278 | 928DC8C118EF48D600D0AC26 /* main.m in Sources */, 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | 928DC8CE18EF48D600D0AC26 /* Sources */ = { 283 | isa = PBXSourcesBuildPhase; 284 | buildActionMask = 2147483647; 285 | files = ( 286 | 928DC8F318EF49D000D0AC26 /* PAPreferencesTests.m in Sources */, 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | /* End PBXSourcesBuildPhase section */ 291 | 292 | /* Begin PBXTargetDependency section */ 293 | 928DC8D718EF48D600D0AC26 /* PBXTargetDependency */ = { 294 | isa = PBXTargetDependency; 295 | target = 928DC8B018EF48D600D0AC26 /* PAPreferencesSampleOSX */; 296 | targetProxy = 928DC8D618EF48D600D0AC26 /* PBXContainerItemProxy */; 297 | }; 298 | /* End PBXTargetDependency section */ 299 | 300 | /* Begin PBXVariantGroup section */ 301 | 928DC8BD18EF48D600D0AC26 /* InfoPlist.strings */ = { 302 | isa = PBXVariantGroup; 303 | children = ( 304 | 928DC8BE18EF48D600D0AC26 /* en */, 305 | ); 306 | name = InfoPlist.strings; 307 | sourceTree = ""; 308 | }; 309 | 928DC8C318EF48D600D0AC26 /* Credits.rtf */ = { 310 | isa = PBXVariantGroup; 311 | children = ( 312 | 928DC8C418EF48D600D0AC26 /* en */, 313 | ); 314 | name = Credits.rtf; 315 | sourceTree = ""; 316 | }; 317 | 928DC8C918EF48D600D0AC26 /* MainMenu.xib */ = { 318 | isa = PBXVariantGroup; 319 | children = ( 320 | 928DC8CA18EF48D600D0AC26 /* Base */, 321 | ); 322 | name = MainMenu.xib; 323 | sourceTree = ""; 324 | }; 325 | 928DC8DB18EF48D600D0AC26 /* InfoPlist.strings */ = { 326 | isa = PBXVariantGroup; 327 | children = ( 328 | 928DC8DC18EF48D600D0AC26 /* en */, 329 | ); 330 | name = InfoPlist.strings; 331 | sourceTree = ""; 332 | }; 333 | /* End PBXVariantGroup section */ 334 | 335 | /* Begin XCBuildConfiguration section */ 336 | 928DC8E018EF48D600D0AC26 /* Debug */ = { 337 | isa = XCBuildConfiguration; 338 | buildSettings = { 339 | ALWAYS_SEARCH_USER_PATHS = NO; 340 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 341 | CLANG_CXX_LIBRARY = "libc++"; 342 | CLANG_ENABLE_MODULES = YES; 343 | CLANG_ENABLE_OBJC_ARC = YES; 344 | CLANG_WARN_BOOL_CONVERSION = YES; 345 | CLANG_WARN_CONSTANT_CONVERSION = YES; 346 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 347 | CLANG_WARN_EMPTY_BODY = YES; 348 | CLANG_WARN_ENUM_CONVERSION = YES; 349 | CLANG_WARN_INT_CONVERSION = YES; 350 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 351 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 352 | COPY_PHASE_STRIP = NO; 353 | ENABLE_TESTABILITY = YES; 354 | GCC_C_LANGUAGE_STANDARD = gnu99; 355 | GCC_DYNAMIC_NO_PIC = NO; 356 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 357 | GCC_OPTIMIZATION_LEVEL = 0; 358 | GCC_PREPROCESSOR_DEFINITIONS = ( 359 | "DEBUG=1", 360 | "$(inherited)", 361 | ); 362 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 363 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 364 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 365 | GCC_WARN_UNDECLARED_SELECTOR = YES; 366 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 367 | GCC_WARN_UNUSED_FUNCTION = YES; 368 | GCC_WARN_UNUSED_VARIABLE = YES; 369 | MACOSX_DEPLOYMENT_TARGET = 10.9; 370 | ONLY_ACTIVE_ARCH = YES; 371 | SDKROOT = macosx; 372 | }; 373 | name = Debug; 374 | }; 375 | 928DC8E118EF48D600D0AC26 /* Release */ = { 376 | isa = XCBuildConfiguration; 377 | buildSettings = { 378 | ALWAYS_SEARCH_USER_PATHS = NO; 379 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 380 | CLANG_CXX_LIBRARY = "libc++"; 381 | CLANG_ENABLE_MODULES = YES; 382 | CLANG_ENABLE_OBJC_ARC = YES; 383 | CLANG_WARN_BOOL_CONVERSION = YES; 384 | CLANG_WARN_CONSTANT_CONVERSION = YES; 385 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 386 | CLANG_WARN_EMPTY_BODY = YES; 387 | CLANG_WARN_ENUM_CONVERSION = YES; 388 | CLANG_WARN_INT_CONVERSION = YES; 389 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 390 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 391 | COPY_PHASE_STRIP = YES; 392 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 393 | ENABLE_NS_ASSERTIONS = NO; 394 | GCC_C_LANGUAGE_STANDARD = gnu99; 395 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 396 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 397 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 398 | GCC_WARN_UNDECLARED_SELECTOR = YES; 399 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 400 | GCC_WARN_UNUSED_FUNCTION = YES; 401 | GCC_WARN_UNUSED_VARIABLE = YES; 402 | MACOSX_DEPLOYMENT_TARGET = 10.9; 403 | SDKROOT = macosx; 404 | }; 405 | name = Release; 406 | }; 407 | 928DC8E318EF48D600D0AC26 /* Debug */ = { 408 | isa = XCBuildConfiguration; 409 | buildSettings = { 410 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 411 | COMBINE_HIDPI_IMAGES = YES; 412 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 413 | GCC_PREFIX_HEADER = "PAPreferencesSampleOSX/PAPreferencesSampleOSX-Prefix.pch"; 414 | INFOPLIST_FILE = "PAPreferencesSampleOSX/PAPreferencesSampleOSX-Info.plist"; 415 | PRODUCT_BUNDLE_IDENTIFIER = "com.peerassembly.${PRODUCT_NAME:rfc1034identifier}"; 416 | PRODUCT_NAME = "$(TARGET_NAME)"; 417 | WRAPPER_EXTENSION = app; 418 | }; 419 | name = Debug; 420 | }; 421 | 928DC8E418EF48D600D0AC26 /* Release */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 425 | COMBINE_HIDPI_IMAGES = YES; 426 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 427 | GCC_PREFIX_HEADER = "PAPreferencesSampleOSX/PAPreferencesSampleOSX-Prefix.pch"; 428 | INFOPLIST_FILE = "PAPreferencesSampleOSX/PAPreferencesSampleOSX-Info.plist"; 429 | PRODUCT_BUNDLE_IDENTIFIER = "com.peerassembly.${PRODUCT_NAME:rfc1034identifier}"; 430 | PRODUCT_NAME = "$(TARGET_NAME)"; 431 | WRAPPER_EXTENSION = app; 432 | }; 433 | name = Release; 434 | }; 435 | 928DC8E618EF48D600D0AC26 /* Debug */ = { 436 | isa = XCBuildConfiguration; 437 | buildSettings = { 438 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/PAPreferencesSampleOSX.app/Contents/MacOS/PAPreferencesSampleOSX"; 439 | COMBINE_HIDPI_IMAGES = YES; 440 | FRAMEWORK_SEARCH_PATHS = ( 441 | "$(DEVELOPER_FRAMEWORKS_DIR)", 442 | "$(inherited)", 443 | ); 444 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 445 | GCC_PREFIX_HEADER = "PAPreferencesSampleOSX/PAPreferencesSampleOSX-Prefix.pch"; 446 | GCC_PREPROCESSOR_DEFINITIONS = ( 447 | "DEBUG=1", 448 | "$(inherited)", 449 | ); 450 | INFOPLIST_FILE = "PAPreferencesSampleOSXTests/PAPreferencesSampleOSXTests-Info.plist"; 451 | PRODUCT_BUNDLE_IDENTIFIER = "com.peerassembly.${PRODUCT_NAME:rfc1034identifier}"; 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | TEST_HOST = "$(BUNDLE_LOADER)"; 454 | WRAPPER_EXTENSION = xctest; 455 | }; 456 | name = Debug; 457 | }; 458 | 928DC8E718EF48D600D0AC26 /* Release */ = { 459 | isa = XCBuildConfiguration; 460 | buildSettings = { 461 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/PAPreferencesSampleOSX.app/Contents/MacOS/PAPreferencesSampleOSX"; 462 | COMBINE_HIDPI_IMAGES = YES; 463 | FRAMEWORK_SEARCH_PATHS = ( 464 | "$(DEVELOPER_FRAMEWORKS_DIR)", 465 | "$(inherited)", 466 | ); 467 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 468 | GCC_PREFIX_HEADER = "PAPreferencesSampleOSX/PAPreferencesSampleOSX-Prefix.pch"; 469 | INFOPLIST_FILE = "PAPreferencesSampleOSXTests/PAPreferencesSampleOSXTests-Info.plist"; 470 | PRODUCT_BUNDLE_IDENTIFIER = "com.peerassembly.${PRODUCT_NAME:rfc1034identifier}"; 471 | PRODUCT_NAME = "$(TARGET_NAME)"; 472 | TEST_HOST = "$(BUNDLE_LOADER)"; 473 | WRAPPER_EXTENSION = xctest; 474 | }; 475 | name = Release; 476 | }; 477 | /* End XCBuildConfiguration section */ 478 | 479 | /* Begin XCConfigurationList section */ 480 | 928DC8AC18EF48D600D0AC26 /* Build configuration list for PBXProject "PAPreferencesSampleOSX" */ = { 481 | isa = XCConfigurationList; 482 | buildConfigurations = ( 483 | 928DC8E018EF48D600D0AC26 /* Debug */, 484 | 928DC8E118EF48D600D0AC26 /* Release */, 485 | ); 486 | defaultConfigurationIsVisible = 0; 487 | defaultConfigurationName = Release; 488 | }; 489 | 928DC8E218EF48D600D0AC26 /* Build configuration list for PBXNativeTarget "PAPreferencesSampleOSX" */ = { 490 | isa = XCConfigurationList; 491 | buildConfigurations = ( 492 | 928DC8E318EF48D600D0AC26 /* Debug */, 493 | 928DC8E418EF48D600D0AC26 /* Release */, 494 | ); 495 | defaultConfigurationIsVisible = 0; 496 | defaultConfigurationName = Release; 497 | }; 498 | 928DC8E518EF48D600D0AC26 /* Build configuration list for PBXNativeTarget "PAPreferencesSampleOSXTests" */ = { 499 | isa = XCConfigurationList; 500 | buildConfigurations = ( 501 | 928DC8E618EF48D600D0AC26 /* Debug */, 502 | 928DC8E718EF48D600D0AC26 /* Release */, 503 | ); 504 | defaultConfigurationIsVisible = 0; 505 | defaultConfigurationName = Release; 506 | }; 507 | /* End XCConfigurationList section */ 508 | }; 509 | rootObject = 928DC8A918EF48D600D0AC26 /* Project object */; 510 | } 511 | -------------------------------------------------------------------------------- /Sample-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Sample-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PAPreferencesSampleOSX 4 | // 5 | // Created by Denis Hennessy on 04/04/2014. 6 | // Copyright (c) 2014 Denis Hennessy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : NSObject 12 | 13 | @property (assign) IBOutlet NSWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Sample-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PAPreferencesSampleOSX 4 | // 5 | // Created by Denis Hennessy on 04/04/2014. 6 | // Copyright (c) 2014 Denis Hennessy. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 14 | { 15 | // Insert code here to initialize your application 16 | } 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /Sample-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 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 | 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 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | Default 539 | 540 | 541 | 542 | 543 | 544 | 545 | Left to Right 546 | 547 | 548 | 549 | 550 | 551 | 552 | Right to Left 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | Default 564 | 565 | 566 | 567 | 568 | 569 | 570 | Left to Right 571 | 572 | 573 | 574 | 575 | 576 | 577 | Right to Left 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | -------------------------------------------------------------------------------- /Sample-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Sample-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 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 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2014 Denis Hennessy. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /Sample-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX-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-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /Sample-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Sample-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSX/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PAPreferencesSampleOSX 4 | // 5 | // Created by Denis Hennessy on 04/04/2014. 6 | // Copyright (c) 2014 Denis Hennessy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) 12 | { 13 | return NSApplicationMain(argc, argv); 14 | } 15 | -------------------------------------------------------------------------------- /Sample-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSXTests/PAPreferencesSampleOSXTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 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-OSX/PAPreferencesSampleOSX/PAPreferencesSampleOSXTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 920B71D117E798080074D2E5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 920B71D017E798080074D2E5 /* Foundation.framework */; }; 11 | 920B71D317E798080074D2E5 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 920B71D217E798080074D2E5 /* CoreGraphics.framework */; }; 12 | 920B71D517E798080074D2E5 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 920B71D417E798080074D2E5 /* UIKit.framework */; }; 13 | 920B71DB17E798080074D2E5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 920B71D917E798080074D2E5 /* InfoPlist.strings */; }; 14 | 920B71DD17E798080074D2E5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 920B71DC17E798080074D2E5 /* main.m */; }; 15 | 920B71E117E798080074D2E5 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 920B71E017E798080074D2E5 /* AppDelegate.m */; }; 16 | 920B71E317E798080074D2E5 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 920B71E217E798080074D2E5 /* Images.xcassets */; }; 17 | 920B71EA17E798080074D2E5 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 920B71E917E798080074D2E5 /* XCTest.framework */; }; 18 | 920B71EB17E798080074D2E5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 920B71D017E798080074D2E5 /* Foundation.framework */; }; 19 | 920B71EC17E798080074D2E5 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 920B71D417E798080074D2E5 /* UIKit.framework */; }; 20 | 920B71F417E798080074D2E5 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 920B71F217E798080074D2E5 /* InfoPlist.strings */; }; 21 | 9220B485190FA354006432F4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9220B482190FA354006432F4 /* Main.storyboard */; }; 22 | 9220B486190FA354006432F4 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9220B484190FA354006432F4 /* ViewController.m */; }; 23 | 9220B489190FA536006432F4 /* Preferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 9220B488190FA536006432F4 /* Preferences.m */; }; 24 | 928DC8A618EF487600D0AC26 /* PAPreferences.m in Sources */ = {isa = PBXBuildFile; fileRef = 928DC8A318EF487600D0AC26 /* PAPreferences.m */; }; 25 | 928DC8A718EF487600D0AC26 /* PAPropertyDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 928DC8A518EF487600D0AC26 /* PAPropertyDescriptor.m */; }; 26 | 928DC8F118EF498C00D0AC26 /* PAPreferencesTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 928DC8EF18EF497A00D0AC26 /* PAPreferencesTests.m */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 920B71ED17E798080074D2E5 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 920B71C517E798080074D2E5 /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 920B71CC17E798080074D2E5; 35 | remoteInfo = PAPreferencesSample; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 920B71CD17E798080074D2E5 /* PAPreferencesSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PAPreferencesSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 920B71D017E798080074D2E5 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 42 | 920B71D217E798080074D2E5 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 43 | 920B71D417E798080074D2E5 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 44 | 920B71D817E798080074D2E5 /* PAPreferencesSample-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PAPreferencesSample-Info.plist"; sourceTree = ""; }; 45 | 920B71DA17E798080074D2E5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 46 | 920B71DC17E798080074D2E5 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 47 | 920B71DE17E798080074D2E5 /* PAPreferencesSample-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PAPreferencesSample-Prefix.pch"; sourceTree = ""; }; 48 | 920B71DF17E798080074D2E5 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 49 | 920B71E017E798080074D2E5 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 50 | 920B71E217E798080074D2E5 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 51 | 920B71E817E798080074D2E5 /* PAPreferencesSampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PAPreferencesSampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | 920B71E917E798080074D2E5 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 53 | 920B71F117E798080074D2E5 /* PAPreferencesSampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PAPreferencesSampleTests-Info.plist"; sourceTree = ""; }; 54 | 920B71F317E798080074D2E5 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 55 | 9220B482190FA354006432F4 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 56 | 9220B483190FA354006432F4 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 57 | 9220B484190FA354006432F4 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 58 | 9220B487190FA536006432F4 /* Preferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Preferences.h; sourceTree = ""; }; 59 | 9220B488190FA536006432F4 /* Preferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Preferences.m; sourceTree = ""; }; 60 | 928DC8A218EF487600D0AC26 /* PAPreferences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PAPreferences.h; path = ../../PAPreferences/PAPreferences.h; sourceTree = ""; }; 61 | 928DC8A318EF487600D0AC26 /* PAPreferences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PAPreferences.m; path = ../../PAPreferences/PAPreferences.m; sourceTree = ""; }; 62 | 928DC8A418EF487600D0AC26 /* PAPropertyDescriptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PAPropertyDescriptor.h; path = ../../PAPreferences/PAPropertyDescriptor.h; sourceTree = ""; }; 63 | 928DC8A518EF487600D0AC26 /* PAPropertyDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PAPropertyDescriptor.m; path = ../../PAPreferences/PAPropertyDescriptor.m; sourceTree = ""; }; 64 | 928DC8EF18EF497A00D0AC26 /* PAPreferencesTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PAPreferencesTests.m; path = ../../PAPreferencesTests/PAPreferencesTests.m; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | 920B71CA17E798080074D2E5 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | 920B71D317E798080074D2E5 /* CoreGraphics.framework in Frameworks */, 73 | 920B71D517E798080074D2E5 /* UIKit.framework in Frameworks */, 74 | 920B71D117E798080074D2E5 /* Foundation.framework in Frameworks */, 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | 920B71E517E798080074D2E5 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | 920B71EA17E798080074D2E5 /* XCTest.framework in Frameworks */, 83 | 920B71EC17E798080074D2E5 /* UIKit.framework in Frameworks */, 84 | 920B71EB17E798080074D2E5 /* Foundation.framework in Frameworks */, 85 | ); 86 | runOnlyForDeploymentPostprocessing = 0; 87 | }; 88 | /* End PBXFrameworksBuildPhase section */ 89 | 90 | /* Begin PBXGroup section */ 91 | 920B71C417E798080074D2E5 = { 92 | isa = PBXGroup; 93 | children = ( 94 | 920B71D617E798080074D2E5 /* PAPreferencesSample */, 95 | 920B71EF17E798080074D2E5 /* PAPreferencesTests */, 96 | 920B71CF17E798080074D2E5 /* Frameworks */, 97 | 920B71CE17E798080074D2E5 /* Products */, 98 | ); 99 | sourceTree = ""; 100 | }; 101 | 920B71CE17E798080074D2E5 /* Products */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 920B71CD17E798080074D2E5 /* PAPreferencesSample.app */, 105 | 920B71E817E798080074D2E5 /* PAPreferencesSampleTests.xctest */, 106 | ); 107 | name = Products; 108 | sourceTree = ""; 109 | }; 110 | 920B71CF17E798080074D2E5 /* Frameworks */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 920B71D017E798080074D2E5 /* Foundation.framework */, 114 | 920B71D217E798080074D2E5 /* CoreGraphics.framework */, 115 | 920B71D417E798080074D2E5 /* UIKit.framework */, 116 | 920B71E917E798080074D2E5 /* XCTest.framework */, 117 | ); 118 | name = Frameworks; 119 | sourceTree = ""; 120 | }; 121 | 920B71D617E798080074D2E5 /* PAPreferencesSample */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 928DC8A118EF47A200D0AC26 /* PAPreferences */, 125 | 920B71DF17E798080074D2E5 /* AppDelegate.h */, 126 | 920B71E017E798080074D2E5 /* AppDelegate.m */, 127 | 9220B482190FA354006432F4 /* Main.storyboard */, 128 | 9220B487190FA536006432F4 /* Preferences.h */, 129 | 9220B488190FA536006432F4 /* Preferences.m */, 130 | 9220B483190FA354006432F4 /* ViewController.h */, 131 | 9220B484190FA354006432F4 /* ViewController.m */, 132 | 920B71E217E798080074D2E5 /* Images.xcassets */, 133 | 920B71D717E798080074D2E5 /* Supporting Files */, 134 | ); 135 | path = PAPreferencesSample; 136 | sourceTree = ""; 137 | }; 138 | 920B71D717E798080074D2E5 /* Supporting Files */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 920B71D817E798080074D2E5 /* PAPreferencesSample-Info.plist */, 142 | 920B71D917E798080074D2E5 /* InfoPlist.strings */, 143 | 920B71DC17E798080074D2E5 /* main.m */, 144 | 920B71DE17E798080074D2E5 /* PAPreferencesSample-Prefix.pch */, 145 | ); 146 | name = "Supporting Files"; 147 | sourceTree = ""; 148 | }; 149 | 920B71EF17E798080074D2E5 /* PAPreferencesTests */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 928DC8EF18EF497A00D0AC26 /* PAPreferencesTests.m */, 153 | 920B71F017E798080074D2E5 /* Supporting Files */, 154 | ); 155 | name = PAPreferencesTests; 156 | path = PAPreferencesSampleTests; 157 | sourceTree = ""; 158 | }; 159 | 920B71F017E798080074D2E5 /* Supporting Files */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | 920B71F117E798080074D2E5 /* PAPreferencesSampleTests-Info.plist */, 163 | 920B71F217E798080074D2E5 /* InfoPlist.strings */, 164 | ); 165 | name = "Supporting Files"; 166 | sourceTree = ""; 167 | }; 168 | 928DC8A118EF47A200D0AC26 /* PAPreferences */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | 928DC8A218EF487600D0AC26 /* PAPreferences.h */, 172 | 928DC8A318EF487600D0AC26 /* PAPreferences.m */, 173 | 928DC8A418EF487600D0AC26 /* PAPropertyDescriptor.h */, 174 | 928DC8A518EF487600D0AC26 /* PAPropertyDescriptor.m */, 175 | ); 176 | name = PAPreferences; 177 | sourceTree = ""; 178 | }; 179 | /* End PBXGroup section */ 180 | 181 | /* Begin PBXNativeTarget section */ 182 | 920B71CC17E798080074D2E5 /* PAPreferencesSample */ = { 183 | isa = PBXNativeTarget; 184 | buildConfigurationList = 920B71F917E798080074D2E5 /* Build configuration list for PBXNativeTarget "PAPreferencesSample" */; 185 | buildPhases = ( 186 | 920B71C917E798080074D2E5 /* Sources */, 187 | 920B71CA17E798080074D2E5 /* Frameworks */, 188 | 920B71CB17E798080074D2E5 /* Resources */, 189 | ); 190 | buildRules = ( 191 | ); 192 | dependencies = ( 193 | ); 194 | name = PAPreferencesSample; 195 | productName = PAPreferencesSample; 196 | productReference = 920B71CD17E798080074D2E5 /* PAPreferencesSample.app */; 197 | productType = "com.apple.product-type.application"; 198 | }; 199 | 920B71E717E798080074D2E5 /* PAPreferencesSampleTests */ = { 200 | isa = PBXNativeTarget; 201 | buildConfigurationList = 920B71FC17E798080074D2E5 /* Build configuration list for PBXNativeTarget "PAPreferencesSampleTests" */; 202 | buildPhases = ( 203 | 920B71E417E798080074D2E5 /* Sources */, 204 | 920B71E517E798080074D2E5 /* Frameworks */, 205 | 920B71E617E798080074D2E5 /* Resources */, 206 | ); 207 | buildRules = ( 208 | ); 209 | dependencies = ( 210 | 920B71EE17E798080074D2E5 /* PBXTargetDependency */, 211 | ); 212 | name = PAPreferencesSampleTests; 213 | productName = PAPreferencesSampleTests; 214 | productReference = 920B71E817E798080074D2E5 /* PAPreferencesSampleTests.xctest */; 215 | productType = "com.apple.product-type.bundle.unit-test"; 216 | }; 217 | /* End PBXNativeTarget section */ 218 | 219 | /* Begin PBXProject section */ 220 | 920B71C517E798080074D2E5 /* Project object */ = { 221 | isa = PBXProject; 222 | attributes = { 223 | LastUpgradeCheck = 0510; 224 | ORGANIZATIONNAME = "Peer Assembly"; 225 | TargetAttributes = { 226 | 920B71E717E798080074D2E5 = { 227 | TestTargetID = 920B71CC17E798080074D2E5; 228 | }; 229 | }; 230 | }; 231 | buildConfigurationList = 920B71C817E798080074D2E5 /* Build configuration list for PBXProject "PAPreferencesSample" */; 232 | compatibilityVersion = "Xcode 3.2"; 233 | developmentRegion = English; 234 | hasScannedForEncodings = 0; 235 | knownRegions = ( 236 | en, 237 | ); 238 | mainGroup = 920B71C417E798080074D2E5; 239 | productRefGroup = 920B71CE17E798080074D2E5 /* Products */; 240 | projectDirPath = ""; 241 | projectRoot = ""; 242 | targets = ( 243 | 920B71CC17E798080074D2E5 /* PAPreferencesSample */, 244 | 920B71E717E798080074D2E5 /* PAPreferencesSampleTests */, 245 | ); 246 | }; 247 | /* End PBXProject section */ 248 | 249 | /* Begin PBXResourcesBuildPhase section */ 250 | 920B71CB17E798080074D2E5 /* Resources */ = { 251 | isa = PBXResourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | 9220B485190FA354006432F4 /* Main.storyboard in Resources */, 255 | 920B71DB17E798080074D2E5 /* InfoPlist.strings in Resources */, 256 | 920B71E317E798080074D2E5 /* Images.xcassets in Resources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | 920B71E617E798080074D2E5 /* Resources */ = { 261 | isa = PBXResourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | 920B71F417E798080074D2E5 /* InfoPlist.strings in Resources */, 265 | ); 266 | runOnlyForDeploymentPostprocessing = 0; 267 | }; 268 | /* End PBXResourcesBuildPhase section */ 269 | 270 | /* Begin PBXSourcesBuildPhase section */ 271 | 920B71C917E798080074D2E5 /* Sources */ = { 272 | isa = PBXSourcesBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | 920B71E117E798080074D2E5 /* AppDelegate.m in Sources */, 276 | 928DC8A718EF487600D0AC26 /* PAPropertyDescriptor.m in Sources */, 277 | 9220B489190FA536006432F4 /* Preferences.m in Sources */, 278 | 928DC8A618EF487600D0AC26 /* PAPreferences.m in Sources */, 279 | 920B71DD17E798080074D2E5 /* main.m in Sources */, 280 | 9220B486190FA354006432F4 /* ViewController.m in Sources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | 920B71E417E798080074D2E5 /* Sources */ = { 285 | isa = PBXSourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 928DC8F118EF498C00D0AC26 /* PAPreferencesTests.m in Sources */, 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | /* End PBXSourcesBuildPhase section */ 293 | 294 | /* Begin PBXTargetDependency section */ 295 | 920B71EE17E798080074D2E5 /* PBXTargetDependency */ = { 296 | isa = PBXTargetDependency; 297 | target = 920B71CC17E798080074D2E5 /* PAPreferencesSample */; 298 | targetProxy = 920B71ED17E798080074D2E5 /* PBXContainerItemProxy */; 299 | }; 300 | /* End PBXTargetDependency section */ 301 | 302 | /* Begin PBXVariantGroup section */ 303 | 920B71D917E798080074D2E5 /* InfoPlist.strings */ = { 304 | isa = PBXVariantGroup; 305 | children = ( 306 | 920B71DA17E798080074D2E5 /* en */, 307 | ); 308 | name = InfoPlist.strings; 309 | sourceTree = ""; 310 | }; 311 | 920B71F217E798080074D2E5 /* InfoPlist.strings */ = { 312 | isa = PBXVariantGroup; 313 | children = ( 314 | 920B71F317E798080074D2E5 /* en */, 315 | ); 316 | name = InfoPlist.strings; 317 | sourceTree = ""; 318 | }; 319 | /* End PBXVariantGroup section */ 320 | 321 | /* Begin XCBuildConfiguration section */ 322 | 920B71F717E798080074D2E5 /* Debug */ = { 323 | isa = XCBuildConfiguration; 324 | buildSettings = { 325 | ALWAYS_SEARCH_USER_PATHS = NO; 326 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 327 | CLANG_CXX_LIBRARY = "libc++"; 328 | CLANG_ENABLE_MODULES = YES; 329 | CLANG_ENABLE_OBJC_ARC = YES; 330 | CLANG_WARN_BOOL_CONVERSION = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INT_CONVERSION = YES; 336 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 337 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 338 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 339 | COPY_PHASE_STRIP = NO; 340 | GCC_C_LANGUAGE_STANDARD = gnu99; 341 | GCC_DYNAMIC_NO_PIC = NO; 342 | GCC_OPTIMIZATION_LEVEL = 0; 343 | GCC_PREPROCESSOR_DEFINITIONS = ( 344 | "DEBUG=1", 345 | "$(inherited)", 346 | ); 347 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 348 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 349 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 350 | GCC_WARN_UNDECLARED_SELECTOR = YES; 351 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 352 | GCC_WARN_UNUSED_FUNCTION = YES; 353 | GCC_WARN_UNUSED_VARIABLE = YES; 354 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 355 | ONLY_ACTIVE_ARCH = YES; 356 | SDKROOT = iphoneos; 357 | }; 358 | name = Debug; 359 | }; 360 | 920B71F817E798080074D2E5 /* Release */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ALWAYS_SEARCH_USER_PATHS = NO; 364 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 365 | CLANG_CXX_LIBRARY = "libc++"; 366 | CLANG_ENABLE_MODULES = YES; 367 | CLANG_ENABLE_OBJC_ARC = YES; 368 | CLANG_WARN_BOOL_CONVERSION = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 371 | CLANG_WARN_EMPTY_BODY = YES; 372 | CLANG_WARN_ENUM_CONVERSION = YES; 373 | CLANG_WARN_INT_CONVERSION = YES; 374 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 375 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 376 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 377 | COPY_PHASE_STRIP = YES; 378 | ENABLE_NS_ASSERTIONS = NO; 379 | GCC_C_LANGUAGE_STANDARD = gnu99; 380 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 381 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 382 | GCC_WARN_UNDECLARED_SELECTOR = YES; 383 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 384 | GCC_WARN_UNUSED_FUNCTION = YES; 385 | GCC_WARN_UNUSED_VARIABLE = YES; 386 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 387 | SDKROOT = iphoneos; 388 | VALIDATE_PRODUCT = YES; 389 | }; 390 | name = Release; 391 | }; 392 | 920B71FA17E798080074D2E5 /* Debug */ = { 393 | isa = XCBuildConfiguration; 394 | buildSettings = { 395 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 396 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 397 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 398 | GCC_PREFIX_HEADER = "PAPreferencesSample/PAPreferencesSample-Prefix.pch"; 399 | INFOPLIST_FILE = "PAPreferencesSample/PAPreferencesSample-Info.plist"; 400 | PRODUCT_NAME = "$(TARGET_NAME)"; 401 | WRAPPER_EXTENSION = app; 402 | }; 403 | name = Debug; 404 | }; 405 | 920B71FB17E798080074D2E5 /* Release */ = { 406 | isa = XCBuildConfiguration; 407 | buildSettings = { 408 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 409 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 410 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 411 | GCC_PREFIX_HEADER = "PAPreferencesSample/PAPreferencesSample-Prefix.pch"; 412 | INFOPLIST_FILE = "PAPreferencesSample/PAPreferencesSample-Info.plist"; 413 | PRODUCT_NAME = "$(TARGET_NAME)"; 414 | WRAPPER_EXTENSION = app; 415 | }; 416 | name = Release; 417 | }; 418 | 920B71FD17E798080074D2E5 /* Debug */ = { 419 | isa = XCBuildConfiguration; 420 | buildSettings = { 421 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/PAPreferencesSample.app/PAPreferencesSample"; 422 | FRAMEWORK_SEARCH_PATHS = ( 423 | "$(SDKROOT)/Developer/Library/Frameworks", 424 | "$(inherited)", 425 | "$(DEVELOPER_FRAMEWORKS_DIR)", 426 | ); 427 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 428 | GCC_PREFIX_HEADER = "PAPreferencesSample/PAPreferencesSample-Prefix.pch"; 429 | GCC_PREPROCESSOR_DEFINITIONS = ( 430 | "DEBUG=1", 431 | "$(inherited)", 432 | ); 433 | INFOPLIST_FILE = "PAPreferencesSampleTests/PAPreferencesSampleTests-Info.plist"; 434 | PRODUCT_NAME = "$(TARGET_NAME)"; 435 | TEST_HOST = "$(BUNDLE_LOADER)"; 436 | WRAPPER_EXTENSION = xctest; 437 | }; 438 | name = Debug; 439 | }; 440 | 920B71FE17E798080074D2E5 /* Release */ = { 441 | isa = XCBuildConfiguration; 442 | buildSettings = { 443 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/PAPreferencesSample.app/PAPreferencesSample"; 444 | FRAMEWORK_SEARCH_PATHS = ( 445 | "$(SDKROOT)/Developer/Library/Frameworks", 446 | "$(inherited)", 447 | "$(DEVELOPER_FRAMEWORKS_DIR)", 448 | ); 449 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 450 | GCC_PREFIX_HEADER = "PAPreferencesSample/PAPreferencesSample-Prefix.pch"; 451 | INFOPLIST_FILE = "PAPreferencesSampleTests/PAPreferencesSampleTests-Info.plist"; 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | TEST_HOST = "$(BUNDLE_LOADER)"; 454 | WRAPPER_EXTENSION = xctest; 455 | }; 456 | name = Release; 457 | }; 458 | /* End XCBuildConfiguration section */ 459 | 460 | /* Begin XCConfigurationList section */ 461 | 920B71C817E798080074D2E5 /* Build configuration list for PBXProject "PAPreferencesSample" */ = { 462 | isa = XCConfigurationList; 463 | buildConfigurations = ( 464 | 920B71F717E798080074D2E5 /* Debug */, 465 | 920B71F817E798080074D2E5 /* Release */, 466 | ); 467 | defaultConfigurationIsVisible = 0; 468 | defaultConfigurationName = Release; 469 | }; 470 | 920B71F917E798080074D2E5 /* Build configuration list for PBXNativeTarget "PAPreferencesSample" */ = { 471 | isa = XCConfigurationList; 472 | buildConfigurations = ( 473 | 920B71FA17E798080074D2E5 /* Debug */, 474 | 920B71FB17E798080074D2E5 /* Release */, 475 | ); 476 | defaultConfigurationIsVisible = 0; 477 | defaultConfigurationName = Release; 478 | }; 479 | 920B71FC17E798080074D2E5 /* Build configuration list for PBXNativeTarget "PAPreferencesSampleTests" */ = { 480 | isa = XCConfigurationList; 481 | buildConfigurations = ( 482 | 920B71FD17E798080074D2E5 /* Debug */, 483 | 920B71FE17E798080074D2E5 /* Release */, 484 | ); 485 | defaultConfigurationIsVisible = 0; 486 | defaultConfigurationName = Release; 487 | }; 488 | /* End XCConfigurationList section */ 489 | }; 490 | rootObject = 920B71C517E798080074D2E5 /* Project object */; 491 | } 492 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PAPreferencesSample 4 | // 5 | // Created by Denis Hennessy on 16/09/2013. 6 | // Copyright (c) 2013 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PAPreferencesSample 4 | // 5 | // Created by Denis Hennessy on 16/09/2013. 6 | // Copyright (c) 2013 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 14 | 15 | // self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 16 | // // Override point for customization after application launch. 17 | // self.window.backgroundColor = [UIColor whiteColor]; 18 | // [self.window makeKeyAndVisible]; 19 | return YES; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/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-iOS/PAPreferencesSample/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 | } -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 35 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/PAPreferencesSample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.peerassembly.${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 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/PAPreferencesSample-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_3_0 10 | #warning "This project uses features only available in iOS SDK 3.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/Preferences.h: -------------------------------------------------------------------------------- 1 | // 2 | // Preferences.h 3 | // PAPreferencesSample 4 | // 5 | // Created by Denis Hennessy on 29/04/2014. 6 | // Copyright (c) 2014 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import "PAPreferences.h" 10 | 11 | // Ensure we get an error if we forget to add @dynamic for each property 12 | #pragma clang diagnostic push 13 | #pragma clang diagnostic error "-Wobjc-missing-property-synthesis" 14 | 15 | @interface Preferences : PAPreferences 16 | 17 | @property (nonatomic, assign) BOOL hasSeenIntro; 18 | @property (nonatomic, assign) NSInteger pressCount; 19 | 20 | @end 21 | 22 | #pragma clang diagnostic pop 23 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/Preferences.m: -------------------------------------------------------------------------------- 1 | // 2 | // Preferences.m 3 | // PAPreferencesSample 4 | // 5 | // Created by Denis Hennessy on 29/04/2014. 6 | // Copyright (c) 2014 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import "Preferences.h" 10 | 11 | @implementation Preferences 12 | 13 | @dynamic hasSeenIntro; 14 | @dynamic pressCount; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // SingleView 4 | // 5 | // Created by Denis Hennessy on 29/04/2014. 6 | // Copyright (c) 2014 Denis Hennessy. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // SingleView 4 | // 5 | // Created by Denis Hennessy on 29/04/2014. 6 | // Copyright (c) 2014 Denis Hennessy. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "Preferences.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (weak, nonatomic) IBOutlet UILabel *progressLabel; 15 | 16 | @end 17 | 18 | @implementation ViewController 19 | 20 | - (IBAction)buttonTapped:(id)sender { 21 | [Preferences sharedInstance].pressCount = [Preferences sharedInstance].pressCount + 1; 22 | [self configureMessage]; 23 | } 24 | 25 | - (void)configureMessage { 26 | _progressLabel.hidden = NO; 27 | _progressLabel.text = [NSString stringWithFormat:@"Button has been pressed %ld times", (long)[Preferences sharedInstance].pressCount]; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PAPreferencesSample 4 | // 5 | // Created by Denis Hennessy on 16/09/2013. 6 | // Copyright (c) 2013 Peer Assembly. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Sample-iOS/PAPreferencesSampleTests/PAPreferencesSampleTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.peerassembly.${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-iOS/PAPreferencesSampleTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | --------------------------------------------------------------------------------