This commit is contained in:
2025-11-20 15:35:22 +08:00
parent c27fd099f6
commit b23927968f

View File

@@ -3,14 +3,13 @@
// //
#import "KBSkinManager.h" #import "KBSkinManager.h"
#import <Security/Security.h>
#import "KBConfig.h" #import "KBConfig.h"
NSString * const KBSkinDidChangeNotification = @"KBSkinDidChangeNotification"; NSString * const KBSkinDidChangeNotification = @"KBSkinDidChangeNotification";
NSString * const KBDarwinSkinChanged = @"com.loveKey.nyx.skin.changed"; NSString * const KBDarwinSkinChanged = @"com.loveKey.nyx.skin.changed";
static NSString * const kKBSkinService = @"com.loveKey.nyx.skin"; // Keychain service // 使 App Group NSUserDefaults Keychain
static NSString * const kKBSkinAccount = @"current"; // Keychain account static NSString * const kKBSkinThemeStoreKey = @"KBSkinThemeCurrent";
@implementation KBSkinTheme @implementation KBSkinTheme
@@ -81,8 +80,8 @@ static NSString * const kKBSkinAccount = @"current"; // Keychain ac
- (instancetype)init { - (instancetype)init {
if (self = [super init]) { if (self = [super init]) {
KBSkinTheme *t = [self p_loadFromKeychain]; KBSkinTheme *t = [self p_loadFromStore];
// Keychain App Group / 退 // App Group / 退
if (!t || ![self.class kb_hasAssetsForSkinId:t.skinId]) { if (!t || ![self.class kb_hasAssetsForSkinId:t.skinId]) {
t = [self.class defaultTheme]; t = [self.class defaultTheme];
} }
@@ -100,7 +99,7 @@ static NSString * const kKBSkinAccount = @"current"; // Keychain ac
static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) { static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
KBSkinManager *self = (__bridge KBSkinManager *)observer; KBSkinManager *self = (__bridge KBSkinManager *)observer;
[self p_reloadFromKeychainAndBroadcast:YES]; [self p_reloadFromStoreAndBroadcast:YES];
} }
- (void)dealloc { - (void)dealloc {
@@ -134,13 +133,17 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
- (BOOL)applyTheme:(KBSkinTheme *)theme { - (BOOL)applyTheme:(KBSkinTheme *)theme {
if (!theme) return NO; if (!theme) return NO;
if ([self p_saveToKeychain:theme]) { // App Group 使
self.current = theme; [self p_saveToStore:theme];
[[NSNotificationCenter defaultCenter] postNotificationName:KBSkinDidChangeNotification object:nil]; // 广
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge CFStringRef)KBDarwinSkinChanged, NULL, NULL, true); self.current = theme;
return YES; [[NSNotificationCenter defaultCenter] postNotificationName:KBSkinDidChangeNotification object:nil];
} CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(),
return NO; (__bridge CFStringRef)KBDarwinSkinChanged,
NULL,
NULL,
true);
return YES;
} }
- (void)resetToDefault { - (void)resetToDefault {
@@ -329,44 +332,40 @@ static void KBSkinDarwinCallback(CFNotificationCenterRef center, void *observer,
return t; return t;
} }
#pragma mark - Keychain #pragma mark - Persistence (App Group)
- (NSMutableDictionary *)baseKCQuery { /// App Group NSUserDefaults
NSMutableDictionary *q = [@{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, - (BOOL)p_saveToStore:(KBSkinTheme *)theme {
(__bridge id)kSecAttrService: kKBSkinService, if (!theme) return NO;
(__bridge id)kSecAttrAccount: kKBSkinAccount } mutableCopy];
q[(__bridge id)kSecAttrAccessGroup] = KB_KEYCHAIN_ACCESS_GROUP;
return q;
}
- (BOOL)p_saveToKeychain:(KBSkinTheme *)theme {
NSError *err = nil; NSError *err = nil;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:theme requiringSecureCoding:YES error:&err]; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:theme
requiringSecureCoding:YES
error:&err];
if (err || data.length == 0) return NO; if (err || data.length == 0) return NO;
NSMutableDictionary *q = [self baseKCQuery]; NSUserDefaults *ud = [[NSUserDefaults alloc] initWithSuiteName:AppGroup];
SecItemDelete((__bridge CFDictionaryRef)q); if (!ud) return NO;
q[(__bridge id)kSecValueData] = data; [ud setObject:data forKey:kKBSkinThemeStoreKey];
q[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; return [ud synchronize];
OSStatus st = SecItemAdd((__bridge CFDictionaryRef)q, NULL);
return (st == errSecSuccess);
} }
- (KBSkinTheme *)p_loadFromKeychain { /// App Group NSUserDefaults
NSMutableDictionary *q = [self baseKCQuery]; - (KBSkinTheme *)p_loadFromStore {
q[(__bridge id)kSecReturnData] = @YES; NSUserDefaults *ud = [[NSUserDefaults alloc] initWithSuiteName:AppGroup];
q[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne; if (!ud) return nil;
CFTypeRef dataRef = NULL; OSStatus st = SecItemCopyMatching((__bridge CFDictionaryRef)q, &dataRef); NSData *data = [ud objectForKey:kKBSkinThemeStoreKey];
if (st != errSecSuccess || !dataRef) return nil; if (![data isKindOfClass:NSData.class] || data.length == 0) return nil;
NSData *data = (__bridge_transfer NSData *)dataRef;
if (data.length == 0) return nil;
@try { @try {
KBSkinTheme *t = [NSKeyedUnarchiver unarchivedObjectOfClass:KBSkinTheme.class fromData:data error:NULL]; KBSkinTheme *t = [NSKeyedUnarchiver unarchivedObjectOfClass:KBSkinTheme.class
fromData:data
error:NULL];
return t; return t;
} @catch (__unused NSException *e) { return nil; } } @catch (__unused NSException *e) {
return nil;
}
} }
- (void)p_reloadFromKeychainAndBroadcast:(BOOL)broadcast { - (void)p_reloadFromStoreAndBroadcast:(BOOL)broadcast {
KBSkinTheme *t = [self p_loadFromKeychain]; KBSkinTheme *t = [self p_loadFromStore];
if (!t || ![self.class kb_hasAssetsForSkinId:t.skinId]) { if (!t || ![self.class kb_hasAssetsForSkinId:t.skinId]) {
t = [self.class defaultTheme]; t = [self.class defaultTheme];
} }