See, settings is a concept in your application (storing and using a UI textSettings), which is better to design with abstraction from the actual implementation (persisting in .plists, databases et cetera) in mind.
All those individual values are grouped together by a single concept, and thus can be represented by a class in your project. This class "wraps up" implementation details related to the concept. This logic becomes isolated from other parts of your program, and when you need to change it, your changes are confined to a single entity, ergo every change is less likely to break the rest of your code.
This class can be implemented like that:
/////////////
// TextSettings.h
#import <Foundation/Foundation.h>
@interface TextSettings : NSObject <NSCoding>
@property (nonatomic, strong) UIColor *mainColor;
@property (nonatomic, copy) UIFont *font;
+ (instancetype)defaultTextSettings;
@end
/////////////
// TextSettings.m
@implementation TextSettings
+ (instancetype)defaultTextSettings
{
TextSettings *textSettings = [[TextSettings alloc] init];
textSettings.font = [UIFont systemFontOfSize:14.0f];
textSettings.mainColor = [UIColor whiteColor];
return textSettings;
}
#pragma mark - NSCoding
// Read more about NSCoding on: http://nshipster.com/nscoding/
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
_font = [aDecoder decodeObjectForKey:@"_font"];
_mainColor = [aDecoder decodeObjectForKey:@"_mainColor"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.font forKey:@"_font"];
[aCoder encodeObject:self.mainColor forKey:@"_mainColor"];
}
#pragma mark - Equality
- (BOOL)isEqual:(id)object
{
if (object == nil) {
return NO;
}
if ([object isKindOfClass:[self class]] == NO) {
return NO;
}
TextSettings *otherTextSettings = object;
return [self.font isEqual:otherTextSettings.font] && [self.mainColor isEqual:otherTextSettings.mainColor];
}
// You must override -hash if you override -isEqual
- (NSUInteger)hash
{
return self.class.hash ^ self.font.hash ^ self.mainColor.hash;
}
@end
When you have such an object you can:
- easily test them for equality
- easily archive and unarchive (serialize and deserialize) them
Equality testing
// TextSettings *myTextSettings
if ([myTextSettings isEqual:[TextSettings defaultTextSettings]]) {
// User didn't change the textSettings...
}
else {
// User changed the textSettings!
}
Serialization/deserialization
// In a controller responsible for displaying something according to textSettings.
// textSettings (self.textSettings) is a @property in this controller.
- (void)saveCurrentTextSettings
{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.textSettings];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"currentTextSettings"];
}
- (void)loadTextSettings
{
NSData *data = [[NSUserDefaults standardUserDefaults] dataForKey:@"currentTextSettings"];
self.textSettings = [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
That's pretty much it.
When you want to add new fields to TextSettings
, you must (1) declare them as @properties, (2) add checks to -isEqual:
and -hash
implementations and (3) add unarchiving/archiving code to -encodeWithCoder
and -initWithCoder
.
That's too much! you may say, but I'd say no – it is hardly an overkill. Definitely better than searching and comparing individual values in NSUserDefaults
.
UPD:
To use it as just plain settings:
// Called when user chooses new value
- (void)userDidChooseFont:(UIFont *)font
{
self.textSettings.font = font;
[self saveTextSettings];
}
- (void)saveTextSettings
{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.textSettings];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"textSettings"];
}
NSUserDefaults
is not a database nor a replacement for a DataModel and persistence. – zaphplist
for this. I would just create a data model that represents a theme, and have it return specific values for each of the theme-specific preferences. Then have a central place where you query the current theme for the specified values, and saves them toNSUserDefaults
. Multipleplist
files seems overkill for this problem. – Craig Otis