2
This commit is contained in:
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user