Skip to content

Commit

Permalink
Refactor preferences
Browse files Browse the repository at this point in the history
• Many values that are supposed to be temporarily, such as whether
Textual is running on a particular operating system, are no longer set
to defaults database. Instead, they are registered as defaults.
Registered defaults do not save to the database unless their value
changes, which won’t happen for the keys we use it for.
• A migration is performed the first time a user opens the app. If a
user was running Textual 6.0.0, then we remove all keys from the
defaults database that are now temporarily (registered defaults)
• The WebKit2 Web Inspector is now forced to be windowed each time the
app launches. See comment in code for more detail.
• The file RegisteredUserDefaultsRemappedKeys.plist was removed. If
these values have not been remapped by now for users, they probably
won’t be.
  • Loading branch information
emsquared committed Sep 27, 2016
1 parent ea9da44 commit b01fefc
Show file tree
Hide file tree
Showing 8 changed files with 195 additions and 124 deletions.
5 changes: 4 additions & 1 deletion Classes/Headers/TPCPreferencesUserDefaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ TEXTUAL_EXTERN NSString * const TPCPreferencesUserDefaultsDidChangeNotification;
@interface TPCPreferencesUserDefaults : NSUserDefaults
+ (TPCPreferencesUserDefaults *)sharedUserDefaults;

+ (BOOL)keyIsExcludedFromBeingExported:(NSString *)key;
+ (BOOL)keyIsExcludedFromBeingExported:(NSString *)defaultName;
+ (BOOL)keyIsObsolete:(NSString *)defaultName;

- (void)registerDefault:(id <NSCopying>)value forKey:(NSString *)defaultName;
@end

/* Trying to create a new instance of TPCPreferencesUserDefaultsController will
Expand Down
161 changes: 109 additions & 52 deletions Classes/Preferences/TPCPreferences.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

NSString * const TPCPreferencesThemeFontNameMissingLocallyDefaultsKey = @"Theme -> Font Name -> Did Not Exist During Last Sync";

NSUInteger const TPCPreferencesDictionaryVersion = 600;
NSUInteger const TPCPreferencesDictionaryVersion = 602;

@implementation TPCPreferences

Expand Down Expand Up @@ -518,7 +518,7 @@ + (BOOL)invertSidebarColorsPreferenceUserConfigurable

+ (void)setInvertSidebarColorsPreferenceUserConfigurable:(BOOL)invertSidebarColorsPreferenceUserConfigurable
{
[RZUserDefaults() setBool:invertSidebarColorsPreferenceUserConfigurable forKey:@"Theme -> Invert Sidebar Colors Preference Enabled"];
[RZUserDefaults() registerDefault:@(invertSidebarColorsPreferenceUserConfigurable) forKey:@"Theme -> Invert Sidebar Colors Preference Enabled"];
}

+ (void)setInvertSidebarColors:(BOOL)invertSidebarColors
Expand Down Expand Up @@ -618,7 +618,7 @@ + (BOOL)themeChannelViewFontPreferenceUserConfigurable

+ (void)setThemeChannelViewFontPreferenceUserConfigurable:(BOOL)themeChannelViewFontPreferenceUserConfigurable
{
[RZUserDefaults() setBool:themeChannelViewFontPreferenceUserConfigurable forKey:@"Theme -> Channel Font Preference Enabled"];
[RZUserDefaults() registerDefault:@(themeChannelViewFontPreferenceUserConfigurable) forKey:@"Theme -> Channel Font Preference Enabled"];
}

+ (NSString *)themeNicknameFormatDefault
Expand All @@ -638,7 +638,7 @@ + (BOOL)themeNicknameFormatPreferenceUserConfigurable

+ (void)setThemeNicknameFormatPreferenceUserConfigurable:(BOOL)themeNicknameFormatPreferenceUserConfigurable
{
[RZUserDefaults() setBool:themeNicknameFormatPreferenceUserConfigurable forKey:@"Theme -> Nickname Format Preference Enabled"];
[RZUserDefaults() registerDefault:@(themeNicknameFormatPreferenceUserConfigurable) forKey:@"Theme -> Nickname Format Preference Enabled"];
}

+ (NSString *)themeTimestampFormatDefault
Expand All @@ -658,7 +658,7 @@ + (BOOL)themeTimestampFormatPreferenceUserConfigurable

+ (void)setThemeTimestampFormatPreferenceUserConfigurable:(BOOL)themeTimestampFormatPreferenceUserConfigurable
{
[RZUserDefaults() setBool:themeTimestampFormatPreferenceUserConfigurable forKey:@"Theme -> Timestamp Format Preference Enabled"];
[RZUserDefaults() registerDefault:@(themeTimestampFormatPreferenceUserConfigurable) forKey:@"Theme -> Timestamp Format Preference Enabled"];
}

+ (CGFloat)mainWindowTransparency
Expand Down Expand Up @@ -1207,6 +1207,56 @@ + (void)_migrateSparkleConfigurationToVersion601
}
#endif

+ (void)_migratePreferencesToVersion602
{
/* This method removes keys that are obsolete. Obsolete keys include those
that are no longer used by any feature, or keys that should only be stored
temporarily. This method must be called before -registeredDefaults are
invoked, or we would could potentially fuck shit up really bad. */
NSNumber *dictionaryVersion = [RZUserDefaults() objectForKey:@"TPCPreferencesDictionaryVersion"];

if (dictionaryVersion.integerValue != 600) {
return;
}

NSDictionary *dictionaryContents = RZUserDefaults().dictionaryRepresentation;

[dictionaryContents enumerateKeysAndObjectsUsingBlock:^(NSString *key, id object, BOOL *stop) {
if ([TPCPreferencesUserDefaults keyIsObsolete:key]) {
[RZUserDefaults() removeObjectForKey:key];
}
}];
}

#pragma mark -
#pragma mark Dynamic Defaults

+ (void)registerWebKit2DynamicDefaults
{
/* The WebKit2 Web Inspector cannot work attached to Textual's main window.
Whoes fault this is isn't clear, but I do not have time to take a deep
look at it at this time. To fix it temporarily, we always force it as
window. To prevent the user breaking Textual by attaching it, we force
reset the default here, every run. */

[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"__WebInspectorPageGroupLevel1__.WebKit2InspectorStartsAttached"];
}

+ (void)registerPreferencesDictionaryVersion
{
/* We do not allow Textual to register a version lower than what is
already set so that if the user opens an older version, we do not
perform migrations more than once. Probably would have been smart
to do this from the beginning. */
NSNumber *dictionaryVersion = [RZUserDefaults() objectForKey:@"TPCPreferencesDictionaryVersion"];

if (dictionaryVersion.integerValue >= TPCPreferencesDictionaryVersion) {
return;
}

[RZUserDefaults() setUnsignedInteger:TPCPreferencesDictionaryVersion forKey:@"TPCPreferencesDictionaryVersion"];
}

#pragma mark -
#pragma mark Initialization

Expand All @@ -1218,6 +1268,58 @@ + (void)_migrateSparkleConfigurationToVersion601
+ (void)registerDynamicDefaults
{
[self _populateDefaultNickname];

[self registerWebKit2DynamicDefaults];

NSMutableDictionary *dynamicDefaults = [NSMutableDictionary dictionary];

[dynamicDefaults setBool:[TPCApplicationInfo sandboxEnabled] forKey:@"Security -> Sandbox Enabled"];

[dynamicDefaults setBool:[XRSystemInformation isUsingOSXMountainLionOrLater] forKey:@"System -> Running Mac OS Mountain Lion Or Newer"];
[dynamicDefaults setBool:[XRSystemInformation isUsingOSXMavericksOrLater] forKey:@"System -> Running Mac OS Mavericks Or Newer"];
[dynamicDefaults setBool:[XRSystemInformation isUsingOSXYosemiteOrLater] forKey:@"System -> Running Mac OS Yosemite Or Newer"];
[dynamicDefaults setBool:[XRSystemInformation isUsingOSXElCapitanOrLater] forKey:@"System -> Running Mac OS El Capitan Or Newer"];
[dynamicDefaults setBool:[XRSystemInformation isUsingOSXSierraOrLater] forKey:@"System -> Running Mac OS Sierra Or Newer"];

#if TEXTUAL_BUILT_WITH_HOCKEYAPP_SDK_ENABLED == 1
[dynamicDefaults setBool:YES forKey:@"System -> 3rd-party Services -> Built with HockeyApp Framework"];
#else
[dynamicDefaults setBool:NO forKey:@"System -> 3rd-party Services -> Built with HockeyApp Framework"];
#endif

#if TEXTUAL_HOCKEYAPP_SDK_METRICS_ENABLED == 1
[dynamicDefaults setBool:YES forKey:@"System -> 3rd-party Services -> Built with HockeyApp Framework Metrics"];
#else
[dynamicDefaults setBool:NO forKey:@"System -> 3rd-party Services -> Built with HockeyApp Framework Metrics"];
#endif

#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
[dynamicDefaults setBool:YES forKey:@"System -> 3rd-party Services -> Built with Sparkle Framework"];
#else
[dynamicDefaults setBool:NO forKey:@"System -> 3rd-party Services -> Built with Sparkle Framework"];
#endif

#if TEXTUAL_BUILT_WITH_ICLOUD_SUPPORT == 1
[dynamicDefaults setBool:YES forKey:@"System -> Built with iCloud Support"];
#else
[dynamicDefaults setBool:NO forKey:@"System -> Built with iCloud Support"];
#endif

#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 1
[dynamicDefaults setBool:YES forKey:@"System -> Built with License Manager Backend"];
#else
[dynamicDefaults setBool:NO forKey:@"System -> Built with License Manager Backend"];
#endif

#if TEXTUAL_BUILT_WITH_ADVANCED_ENCRYPTION == 1
[dynamicDefaults setBool:YES forKey:@"System -> Built with Off-the-Record Messaging Support"];
#else
[dynamicDefaults setBool:NO forKey:@"System -> Built with Off-the-Record Messaging Support"];
#endif

[RZUserDefaults() registerDefaults:dynamicDefaults];

[TPCPreferences registerPreferencesDictionaryVersion];
}

+ (void)registerDefaults
Expand Down Expand Up @@ -1245,6 +1347,8 @@ + (void)initPreferences
[TPCPreferencesUserDefaults migrateKeyValuesAwayFromGroupContainer];
#endif

[TPCPreferences _migratePreferencesToVersion602];

[TPCPreferences registerDefaults];

[TPCPreferences _migrateWorldControllerToVersion600];
Expand All @@ -1260,53 +1364,6 @@ + (void)initPreferences

[TPCPreferences _loadExcludeKeywords];
[TPCPreferences _loadMatchKeywords];

/* Sandbox Check */
[RZUserDefaults() setBool:[TPCApplicationInfo sandboxEnabled] forKey:@"Security -> Sandbox Enabled"];

[RZUserDefaults() setBool:[XRSystemInformation isUsingOSXMountainLionOrLater] forKey:@"System -> Running Mac OS Mountain Lion Or Newer"];
[RZUserDefaults() setBool:[XRSystemInformation isUsingOSXMavericksOrLater] forKey:@"System -> Running Mac OS Mavericks Or Newer"];
[RZUserDefaults() setBool:[XRSystemInformation isUsingOSXYosemiteOrLater] forKey:@"System -> Running Mac OS Yosemite Or Newer"];
[RZUserDefaults() setBool:[XRSystemInformation isUsingOSXElCapitanOrLater] forKey:@"System -> Running Mac OS El Capitan Or Newer"];
[RZUserDefaults() setBool:[XRSystemInformation isUsingOSXSierraOrLater] forKey:@"System -> Running Mac OS Sierra Or Newer"];

#if TEXTUAL_BUILT_WITH_HOCKEYAPP_SDK_ENABLED == 1
[RZUserDefaults() setBool:YES forKey:@"System -> 3rd-party Services -> Built with HockeyApp Framework"];
#else
[RZUserDefaults() setBool:NO forKey:@"System -> 3rd-party Services -> Built with HockeyApp Framework"];
#endif

#if TEXTUAL_HOCKEYAPP_SDK_METRICS_ENABLED == 1
[RZUserDefaults() setBool:YES forKey:@"System -> 3rd-party Services -> Built with HockeyApp Framework Metrics"];
#else
[RZUserDefaults() setBool:NO forKey:@"System -> 3rd-party Services -> Built with HockeyApp Framework Metrics"];
#endif

#if TEXTUAL_BUILT_WITH_SPARKLE_ENABLED == 1
[RZUserDefaults() setBool:YES forKey:@"System -> 3rd-party Services -> Built with Sparkle Framework"];
#else
[RZUserDefaults() setBool:NO forKey:@"System -> 3rd-party Services -> Built with Sparkle Framework"];
#endif

#if TEXTUAL_BUILT_WITH_ICLOUD_SUPPORT == 1
[RZUserDefaults() setBool:YES forKey:@"System -> Built with iCloud Support"];
#else
[RZUserDefaults() setBool:NO forKey:@"System -> Built with iCloud Support"];
#endif

#if TEXTUAL_BUILT_WITH_LICENSE_MANAGER == 1
[RZUserDefaults() setBool:YES forKey:@"System -> Built with License Manager Backend"];
#else
[RZUserDefaults() setBool:NO forKey:@"System -> Built with License Manager Backend"];
#endif

#if TEXTUAL_BUILT_WITH_ADVANCED_ENCRYPTION == 1
[RZUserDefaults() setBool:YES forKey:@"System -> Built with Off-the-Record Messaging Support"];
#else
[RZUserDefaults() setBool:NO forKey:@"System -> Built with Off-the-Record Messaging Support"];
#endif

[RZUserDefaults() setUnsignedInteger:TPCPreferencesDictionaryVersion forKey:@"TPCPreferencesDictionaryVersion"];
}

#pragma mark -
Expand Down
82 changes: 65 additions & 17 deletions Classes/Preferences/TPCPreferencesUserDefaults.m
Original file line number Diff line number Diff line change
Expand Up @@ -197,9 +197,40 @@ - (void)removeObjectForKey:(NSString *)defaultName
[self setObject:nil forKey:defaultName];
}

+ (BOOL)keyIsExcludedFromBeingExported:(NSString *)key
- (void)registerDefault:(id <NSCopying>)value forKey:(NSString *)defaultName
{
NSParameterAssert(key != nil);
NSParameterAssert(value != nil);
NSParameterAssert(defaultName != nil);

[self registerDefaults:@{defaultName : value}];
}

+ (BOOL)key:(NSString *)defaultName1 matchesKey:(NSString *)defaultName2 usingMatchingPattern:(NSString *)matchingPattern
{
NSParameterAssert(defaultName1 != nil);
NSParameterAssert(defaultName2 != nil);
NSParameterAssert(matchingPattern != nil);

if ([matchingPattern isEqualToString:@"="]) {
if ([defaultName1 isEqualToString:defaultName2] == NO) {
return NO;
}
} else if ([matchingPattern isEqualToString:@"PREFIX"]) {
if ([defaultName1 hasPrefix:defaultName2] == NO) {
return NO;
}
} else if ([matchingPattern isEqualToString:@"SUFFIX"]) {
if ([defaultName1 hasSuffix:defaultName2] == NO) {
return NO;
}
}

return YES;
}

+ (BOOL)keyIsExcludedFromBeingExported:(NSString *)defaultName
{
NSParameterAssert(defaultName != nil);

static NSDictionary<NSString *, NSString *> *cachedValues = nil;

Expand All @@ -216,23 +247,40 @@ + (BOOL)keyIsExcludedFromBeingExported:(NSString *)key
__block BOOL returnValue = NO;

[cachedValues enumerateKeysAndObjectsUsingBlock:^(NSString *cachedKey, NSString *cachedObject, BOOL *stop) {
if ([cachedObject isEqualToString:@"="]) {
if ([key isEqualToString:cachedKey] == NO) {
return;
}
} else if ([cachedObject isEqualToString:@"PREFIX"]) {
if ([key hasPrefix:cachedKey] == NO) {
return;
}
} else if ([cachedObject isEqualToString:@"SUFFIX"]) {
if ([key hasSuffix:cachedKey] == NO) {
return;
}
if ([self key:defaultName matchesKey:cachedKey usingMatchingPattern:cachedObject] == NO) {
*stop = YES;

returnValue = YES;
}
}];

return returnValue;
}

*stop = YES;

returnValue = YES;
+ (BOOL)keyIsObsolete:(NSString *)defaultName
{
NSParameterAssert(defaultName != nil);

static NSDictionary<NSString *, NSString *> *cachedValues = nil;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{
NSDictionary *staticValues =
[TPCResourceManager loadContentsOfPropertyListInResources:@"StaticStore"];

cachedValues =
[staticValues dictionaryForKey:@"TPCPreferencesUserDefaults Obsolete Keys"];
});

__block BOOL returnValue = NO;

[cachedValues enumerateKeysAndObjectsUsingBlock:^(NSString *cachedKey, NSString *cachedObject, BOOL *stop) {
if ([self key:defaultName matchesKey:cachedKey usingMatchingPattern:cachedObject]) {
*stop = YES;

returnValue = YES;
}
}];

return returnValue;
Expand Down
16 changes: 0 additions & 16 deletions Classes/Preferences/TPCPreferencesUserDefaultsMigrate.m
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,6 @@ + (void)migrateFileFromPath:(NSString *)sourcePath
/* Retrieve values from property list */
NSDictionary<NSString *, id> *preferencesToMigrate = nil;

NSDictionary<NSString *, NSString *> *preferencesKeysRemapped = nil;

if (sourcePathExists) {
/* Create a backup of the source */
if ([TPCPreferencesUserDefaults createBackupOfPath:sourcePath] == NO) {
Expand All @@ -133,14 +131,6 @@ + (void)migrateFileFromPath:(NSString *)sourcePath
/* Retrieve values from property list. */
preferencesToMigrate = [NSDictionary dictionaryWithContentsOfFile:sourcePath];

preferencesKeysRemapped = [TPCResourceManager loadContentsOfPropertyListInResources:@"RegisteredUserDefaultsRemappedKeys"];

if (preferencesToMigrate == nil || preferencesKeysRemapped == nil) {
LogToConsoleError("'preferencesToMigrate' or 'preferencesKeysRemapped' is nil")

return;
}

/* We delete the existing group container preferences file and
replace it with a symbolic link. Doing this way ensures that the
new path (non-sandboxed path) can be accessed by the Mac App Store
Expand Down Expand Up @@ -208,12 +198,6 @@ new path (non-sandboxed path) can be accessed by the Mac App Store
}

[preferencesToMigrate enumerateKeysAndObjectsUsingBlock:^(NSString *key, id object, BOOL *stop) {
NSString *keyRemapped = preferencesKeysRemapped[key];

if (keyRemapped) {
key = keyRemapped;
}

[RZUserDefaults() setObject:object forKey:key postNotification:NO];
}];
}
Expand Down
Loading

0 comments on commit b01fefc

Please sign in to comment.