diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..eda6b7e --- /dev/null +++ b/.clang-format @@ -0,0 +1,14 @@ +BasedOnStyle: LLVM +IndentWidth: 4 +ObjCBlockIndentWidth: 4 +UseTab: Never +ColumnLimit: 100 +AccessModifierOffset: -4 +AllowShortBlocksOnASingleLine: Empty +AllowShortFunctionsOnASingleLine: Empty +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Empty +AllowShortLoopsOnASingleLine: false +AlignConsecutiveAssignments: Consecutive \ No newline at end of file diff --git a/Headers/Fonts.h b/Headers/Fonts.h index d2f1f78..bbded1f 100644 --- a/Headers/Fonts.h +++ b/Headers/Fonts.h @@ -2,5 +2,4 @@ #import extern NSMutableDictionary *fontMap; -void patchFonts(NSDictionary *mainFonts, - NSString *fontDefName); \ No newline at end of file +void patchFonts(NSDictionary *mainFonts, NSString *fontDefName); \ No newline at end of file diff --git a/Headers/RCTCxxBridge.h b/Headers/RCTCxxBridge.h index 51134c3..14458e6 100644 --- a/Headers/RCTCxxBridge.h +++ b/Headers/RCTCxxBridge.h @@ -1,5 +1,3 @@ @interface RCTCxxBridge : NSObject -- (void)executeApplicationScript:(NSData *)script - url:(NSURL *)url - async:(BOOL)async; +- (void)executeApplicationScript:(NSData *)script url:(NSURL *)url async:(BOOL)async; @end \ No newline at end of file diff --git a/Headers/Theme.h b/Headers/Theme.h index da92361..e1c1d1d 100644 --- a/Headers/Theme.h +++ b/Headers/Theme.h @@ -1,6 +1,5 @@ #import #import -void swizzleDCDThemeColor( - NSDictionary *> *semanticColors); +void swizzleDCDThemeColor(NSDictionary *> *semanticColors); void swizzleUIColor(NSDictionary *rawColors); \ No newline at end of file diff --git a/Sources/Fonts.x b/Sources/Fonts.x index 3ca1fef..3fd06b0 100644 --- a/Sources/Fonts.x +++ b/Sources/Fonts.x @@ -10,178 +10,168 @@ NSMutableDictionary *fontMap; %hook UIFont + (UIFont *)fontWithName:(NSString *)name size:(CGFloat)size { - NSString *replacementName = fontMap[name]; - if (replacementName) { - UIFontDescriptor *replacementDescriptor = - [UIFontDescriptor fontDescriptorWithName:replacementName size:size]; - UIFontDescriptor *fallbackDescriptor = - [replacementDescriptor fontDescriptorByAddingAttributes:@{ - UIFontDescriptorNameAttribute : @[ name ] - }]; - UIFontDescriptor *finalDescriptor = - [replacementDescriptor fontDescriptorByAddingAttributes:@{ - UIFontDescriptorCascadeListAttribute : @[ fallbackDescriptor ] - }]; - - return [UIFont fontWithDescriptor:finalDescriptor size:size]; - } - return %orig; + NSString *replacementName = fontMap[name]; + if (replacementName) { + UIFontDescriptor *replacementDescriptor = + [UIFontDescriptor fontDescriptorWithName:replacementName size:size]; + UIFontDescriptor *fallbackDescriptor = + [replacementDescriptor fontDescriptorByAddingAttributes:@{ + UIFontDescriptorNameAttribute : @[ name ] + }]; + UIFontDescriptor *finalDescriptor = + [replacementDescriptor fontDescriptorByAddingAttributes:@{ + UIFontDescriptorCascadeListAttribute : @[ fallbackDescriptor ] + }]; + + return [UIFont fontWithDescriptor:finalDescriptor size:size]; + } + return %orig; } -+ (UIFont *)fontWithDescriptor:(UIFontDescriptor *)descriptor - size:(CGFloat)size { - NSString *replacementName = fontMap[descriptor.postscriptName]; - if (replacementName) { - UIFontDescriptor *replacementDescriptor = - [UIFontDescriptor fontDescriptorWithName:replacementName size:size]; - UIFontDescriptor *finalDescriptor = - [replacementDescriptor fontDescriptorByAddingAttributes:@{ - UIFontDescriptorCascadeListAttribute : @[ descriptor ] - }]; - - return [UIFont fontWithDescriptor:finalDescriptor size:size]; - } - return %orig; ++ (UIFont *)fontWithDescriptor:(UIFontDescriptor *)descriptor size:(CGFloat)size { + NSString *replacementName = fontMap[descriptor.postscriptName]; + if (replacementName) { + UIFontDescriptor *replacementDescriptor = + [UIFontDescriptor fontDescriptorWithName:replacementName size:size]; + UIFontDescriptor *finalDescriptor = + [replacementDescriptor fontDescriptorByAddingAttributes:@{ + UIFontDescriptorCascadeListAttribute : @[ descriptor ] + }]; + + return [UIFont fontWithDescriptor:finalDescriptor size:size]; + } + return %orig; } + (UIFont *)systemFontOfSize:(CGFloat)size { - NSString *replacementName = fontMap[@"systemFont"]; - if (replacementName) { - return [UIFont fontWithName:replacementName size:size]; - } - return %orig; + NSString *replacementName = fontMap[@"systemFont"]; + if (replacementName) { + return [UIFont fontWithName:replacementName size:size]; + } + return %orig; } + (UIFont *)preferredFontForTextStyle:(UIFontTextStyle)style { - NSString *replacementName = fontMap[@"systemFont"]; - if (replacementName) { - return [UIFont fontWithName:replacementName size:[UIFont systemFontSize]]; - } - return %orig; + NSString *replacementName = fontMap[@"systemFont"]; + if (replacementName) { + return [UIFont fontWithName:replacementName size:[UIFont systemFontSize]]; + } + return %orig; } %end -void patchFonts(NSDictionary *mainFonts, - NSString *fontDefName) { - BunnyLog(@"patchFonts called with fonts: %@ and def name: %@", mainFonts, - fontDefName); - - if (!fontMap) { - BunnyLog(@"Creating new fontMap"); - fontMap = [NSMutableDictionary dictionary]; - } - - NSString *fontJson = [NSString - stringWithContentsOfURL:[getPyoncordDirectory() - URLByAppendingPathComponent:@"fonts.json"] - encoding:NSUTF8StringEncoding - error:nil]; - if (fontJson) { - BunnyLog(@"Found existing fonts.json: %@", fontJson); - } - - for (NSString *fontName in mainFonts) { - NSString *url = mainFonts[fontName]; - BunnyLog(@"Replacing font %@ with URL: %@", fontName, url); - - NSURL *fontURL = [NSURL URLWithString:url]; - NSString *fontExtension = fontURL.pathExtension; - - NSURL *fontCachePath = [[[getPyoncordDirectory() - URLByAppendingPathComponent:@"downloads" - isDirectory:YES] URLByAppendingPathComponent:@"fonts" - isDirectory:YES] - URLByAppendingPathComponent:fontDefName - isDirectory:YES]; - - fontCachePath = [fontCachePath - URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", - fontName, - fontExtension]]; - - NSURL *parentDir = [fontCachePath URLByDeletingLastPathComponent]; - if (![[NSFileManager defaultManager] fileExistsAtPath:parentDir.path]) { - BunnyLog(@"Creating parent directory: %@", parentDir.path); - [[NSFileManager defaultManager] createDirectoryAtURL:parentDir - withIntermediateDirectories:YES - attributes:nil - error:nil]; +void patchFonts(NSDictionary *mainFonts, NSString *fontDefName) { + BunnyLog(@"patchFonts called with fonts: %@ and def name: %@", mainFonts, fontDefName); + + if (!fontMap) { + BunnyLog(@"Creating new fontMap"); + fontMap = [NSMutableDictionary dictionary]; } - if (![[NSFileManager defaultManager] fileExistsAtPath:fontCachePath.path]) { - BunnyLog(@"Downloading font %@ from %@", fontName, url); - NSData *data = [NSData dataWithContentsOfURL:fontURL]; - if (data) { - BunnyLog(@"Writing font data to: %@", fontCachePath.path); - [data writeToURL:fontCachePath atomically:YES]; - } + NSString *fontJson = [NSString + stringWithContentsOfURL:[getPyoncordDirectory() URLByAppendingPathComponent:@"fonts.json"] + encoding:NSUTF8StringEncoding + error:nil]; + if (fontJson) { + BunnyLog(@"Found existing fonts.json: %@", fontJson); } - NSData *fontData = [NSData dataWithContentsOfURL:fontCachePath]; - if (fontData) { - BunnyLog(@"Registering font %@ with provider", fontName); - CGDataProviderRef provider = - CGDataProviderCreateWithCFData((__bridge CFDataRef)fontData); - CGFontRef font = CGFontCreateWithDataProvider(provider); - - if (font) { - CFStringRef postScriptName = CGFontCopyPostScriptName(font); - - CTFontRef existingFont = CTFontCreateWithName(postScriptName, 0, NULL); - if (existingFont) { - CFErrorRef unregisterError = NULL; - if (!CTFontManagerUnregisterGraphicsFont(font, &unregisterError)) { - BunnyLog(@"Failed to deregister font %@: %@", - (__bridge NSString *)postScriptName, - unregisterError - ? (__bridge NSString *)CFErrorCopyDescription( - unregisterError) - : @"Unknown error"); - if (unregisterError) - CFRelease(unregisterError); - } - CFRelease(existingFont); + for (NSString *fontName in mainFonts) { + NSString *url = mainFonts[fontName]; + BunnyLog(@"Replacing font %@ with URL: %@", fontName, url); + + NSURL *fontURL = [NSURL URLWithString:url]; + NSString *fontExtension = fontURL.pathExtension; + + NSURL *fontCachePath = [[[getPyoncordDirectory() URLByAppendingPathComponent:@"downloads" + isDirectory:YES] + URLByAppendingPathComponent:@"fonts" + isDirectory:YES] URLByAppendingPathComponent:fontDefName + isDirectory:YES]; + + fontCachePath = [fontCachePath + URLByAppendingPathComponent:[NSString + stringWithFormat:@"%@.%@", fontName, fontExtension]]; + + NSURL *parentDir = [fontCachePath URLByDeletingLastPathComponent]; + if (![[NSFileManager defaultManager] fileExistsAtPath:parentDir.path]) { + BunnyLog(@"Creating parent directory: %@", parentDir.path); + [[NSFileManager defaultManager] createDirectoryAtURL:parentDir + withIntermediateDirectories:YES + attributes:nil + error:nil]; } - CFErrorRef error = NULL; - if (CTFontManagerRegisterGraphicsFont(font, &error)) { - fontMap[fontName] = (__bridge NSString *)postScriptName; - BunnyLog(@"Successfully registered font %@ to %@", fontName, - (__bridge NSString *)postScriptName); - - NSError *jsonError; - NSData *jsonData = - [NSJSONSerialization dataWithJSONObject:fontMap - options:0 - error:&jsonError]; - if (!jsonError) { - [jsonData - writeToURL:[getPyoncordDirectory() - URLByAppendingPathComponent:@"fontMap.json"] - atomically:YES]; - } - } else { - NSString *errorDesc = - error ? (__bridge NSString *)CFErrorCopyDescription(error) - : @"Unknown error"; - BunnyLog(@"Failed to register font %@: %@", fontName, errorDesc); - if (error) - CFRelease(error); + if (![[NSFileManager defaultManager] fileExistsAtPath:fontCachePath.path]) { + BunnyLog(@"Downloading font %@ from %@", fontName, url); + NSData *data = [NSData dataWithContentsOfURL:fontURL]; + if (data) { + BunnyLog(@"Writing font data to: %@", fontCachePath.path); + [data writeToURL:fontCachePath atomically:YES]; + } } - CFRelease(postScriptName); - CFRelease(font); - } - CGDataProviderRelease(provider); + NSData *fontData = [NSData dataWithContentsOfURL:fontCachePath]; + if (fontData) { + BunnyLog(@"Registering font %@ with provider", fontName); + CGDataProviderRef provider = + CGDataProviderCreateWithCFData((__bridge CFDataRef)fontData); + CGFontRef font = CGFontCreateWithDataProvider(provider); + + if (font) { + CFStringRef postScriptName = CGFontCopyPostScriptName(font); + + CTFontRef existingFont = CTFontCreateWithName(postScriptName, 0, NULL); + if (existingFont) { + CFErrorRef unregisterError = NULL; + if (!CTFontManagerUnregisterGraphicsFont(font, &unregisterError)) { + BunnyLog(@"Failed to deregister font %@: %@", + (__bridge NSString *)postScriptName, + unregisterError + ? (__bridge NSString *)CFErrorCopyDescription(unregisterError) + : @"Unknown error"); + if (unregisterError) + CFRelease(unregisterError); + } + CFRelease(existingFont); + } + + CFErrorRef error = NULL; + if (CTFontManagerRegisterGraphicsFont(font, &error)) { + fontMap[fontName] = (__bridge NSString *)postScriptName; + BunnyLog(@"Successfully registered font %@ to %@", fontName, + (__bridge NSString *)postScriptName); + + NSError *jsonError; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:fontMap + options:0 + error:&jsonError]; + if (!jsonError) { + [jsonData writeToURL:[getPyoncordDirectory() + URLByAppendingPathComponent:@"fontMap.json"] + atomically:YES]; + } + } else { + NSString *errorDesc = error ? (__bridge NSString *)CFErrorCopyDescription(error) + : @"Unknown error"; + BunnyLog(@"Failed to register font %@: %@", fontName, errorDesc); + if (error) + CFRelease(error); + } + + CFRelease(postScriptName); + CFRelease(font); + } + CGDataProviderRelease(provider); + } } - } } %ctor { - @autoreleasepool { - fontMap = [NSMutableDictionary dictionary]; - BunnyLog(@"Font hooks initialized"); - %init; - } + @autoreleasepool { + fontMap = [NSMutableDictionary dictionary]; + BunnyLog(@"Font hooks initialized"); + %init; + } } diff --git a/Sources/LoaderConfig.m b/Sources/LoaderConfig.m index 693764d..3a8612e 100644 --- a/Sources/LoaderConfig.m +++ b/Sources/LoaderConfig.m @@ -5,107 +5,93 @@ @implementation LoaderConfig - (instancetype)init { - self = [super init]; - if (self) { - self.customLoadUrlEnabled = NO; - self.customLoadUrl = - [NSURL URLWithString:@"http://localhost:4040/bunny.js"]; - } - return self; + self = [super init]; + if (self) { + self.customLoadUrlEnabled = NO; + self.customLoadUrl = [NSURL URLWithString:@"http://localhost:4040/bunny.js"]; + } + return self; } - (BOOL)loadConfig { - NSURL *loaderConfigUrl = - [getPyoncordDirectory() URLByAppendingPathComponent:@"loader.json"]; - BunnyLog(@"Attempting to load config from: %@", loaderConfigUrl.path); - - if ([[NSFileManager defaultManager] fileExistsAtPath:loaderConfigUrl.path]) { - NSError *error = nil; - NSData *data = [NSData dataWithContentsOfURL:loaderConfigUrl]; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data - options:0 - error:&error]; - - if (error) { - BunnyLog(@"Error parsing loader config: %@", error); - return NO; - } + NSURL *loaderConfigUrl = [getPyoncordDirectory() URLByAppendingPathComponent:@"loader.json"]; + BunnyLog(@"Attempting to load config from: %@", loaderConfigUrl.path); + + if ([[NSFileManager defaultManager] fileExistsAtPath:loaderConfigUrl.path]) { + NSError *error = nil; + NSData *data = [NSData dataWithContentsOfURL:loaderConfigUrl]; + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; - if (json) { - NSDictionary *customLoadUrl = json[@"customLoadUrl"]; - if (customLoadUrl) { - self.customLoadUrlEnabled = [customLoadUrl[@"enabled"] boolValue]; - NSString *urlString = customLoadUrl[@"url"]; - if (urlString) { - self.customLoadUrl = [NSURL URLWithString:urlString]; + if (error) { + BunnyLog(@"Error parsing loader config: %@", error); + return NO; } - } - BunnyLog(@"Loader config loaded - Custom URL %@: %@", - self.customLoadUrlEnabled ? @"enabled" : @"disabled", - self.customLoadUrl.absoluteString); - return YES; + if (json) { + NSDictionary *customLoadUrl = json[@"customLoadUrl"]; + if (customLoadUrl) { + self.customLoadUrlEnabled = [customLoadUrl[@"enabled"] boolValue]; + NSString *urlString = customLoadUrl[@"url"]; + if (urlString) { + self.customLoadUrl = [NSURL URLWithString:urlString]; + } + } + + BunnyLog(@"Loader config loaded - Custom URL %@: %@", + self.customLoadUrlEnabled ? @"enabled" : @"disabled", + self.customLoadUrl.absoluteString); + return YES; + } } - } - BunnyLog(@"Using default loader config: %@", - self.customLoadUrl.absoluteString); - return NO; + BunnyLog(@"Using default loader config: %@", self.customLoadUrl.absoluteString); + return NO; } + (instancetype)defaultConfig { - LoaderConfig *config = [[LoaderConfig alloc] init]; - config.customLoadUrlEnabled = NO; - config.customLoadUrl = - [NSURL URLWithString:@"http://localhost:4040/bunny.js"]; - return config; + LoaderConfig *config = [[LoaderConfig alloc] init]; + config.customLoadUrlEnabled = NO; + config.customLoadUrl = [NSURL URLWithString:@"http://localhost:4040/bunny.js"]; + return config; } + (instancetype)getLoaderConfig { - BunnyLog(@"Getting loader config"); - - NSURL *loaderConfigUrl = - [getPyoncordDirectory() URLByAppendingPathComponent:@"loader.json"]; - - if ([[NSFileManager defaultManager] fileExistsAtPath:loaderConfigUrl.path]) { - NSError *error = nil; - NSData *data = [NSData dataWithContentsOfURL:loaderConfigUrl]; - NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data - options:0 - error:&error]; - - if (json && !error) { - LoaderConfig *config = [[LoaderConfig alloc] init]; - NSDictionary *customLoadUrl = json[@"customLoadUrl"]; - if (customLoadUrl) { - config.customLoadUrlEnabled = [customLoadUrl[@"enabled"] boolValue]; - NSString *urlString = customLoadUrl[@"url"]; - if (urlString) { - config.customLoadUrl = [NSURL URLWithString:urlString]; + BunnyLog(@"Getting loader config"); + + NSURL *loaderConfigUrl = [getPyoncordDirectory() URLByAppendingPathComponent:@"loader.json"]; + + if ([[NSFileManager defaultManager] fileExistsAtPath:loaderConfigUrl.path]) { + NSError *error = nil; + NSData *data = [NSData dataWithContentsOfURL:loaderConfigUrl]; + NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; + + if (json && !error) { + LoaderConfig *config = [[LoaderConfig alloc] init]; + NSDictionary *customLoadUrl = json[@"customLoadUrl"]; + if (customLoadUrl) { + config.customLoadUrlEnabled = [customLoadUrl[@"enabled"] boolValue]; + NSString *urlString = customLoadUrl[@"url"]; + if (urlString) { + config.customLoadUrl = [NSURL URLWithString:urlString]; + } + } + return config; } - } - return config; } - } - BunnyLog(@"Couldn't get loader config"); - return [LoaderConfig defaultConfig]; + BunnyLog(@"Couldn't get loader config"); + return [LoaderConfig defaultConfig]; } - (BOOL)saveConfig { - NSURL *loaderConfigUrl = - [getPyoncordDirectory() URLByAppendingPathComponent:@"loader.json"]; - NSDictionary *json = @{ - @"customLoadUrl" : @{ - @"enabled" : @(self.customLoadUrlEnabled), - @"url" : self.customLoadUrl.absoluteString - } - }; - - NSData *data = [NSJSONSerialization dataWithJSONObject:json - options:0 - error:nil]; - return [data writeToURL:loaderConfigUrl atomically:YES]; + NSURL *loaderConfigUrl = [getPyoncordDirectory() URLByAppendingPathComponent:@"loader.json"]; + NSDictionary *json = @{ + @"customLoadUrl" : + @{@"enabled" : @(self.customLoadUrlEnabled), @"url" : self.customLoadUrl.absoluteString} + }; + + NSData *data = [NSJSONSerialization dataWithJSONObject:json options:0 error:nil]; + return [data writeToURL:loaderConfigUrl atomically:YES]; } @end \ No newline at end of file diff --git a/Sources/Sideloading.x b/Sources/Sideloading.x index a6fb206..8907d00 100644 --- a/Sources/Sideloading.x +++ b/Sources/Sideloading.x @@ -7,97 +7,92 @@ #define DISCORD_NAME @"Discord" static NSString *getAccessGroupID(void) { - NSDictionary *query = @{ - (__bridge NSString *) - kSecClass : (__bridge NSString *)kSecClassGenericPassword, - (__bridge NSString *)kSecAttrAccount : @"bundleSeedID", - (__bridge NSString *)kSecAttrService : @"", - (__bridge NSString *)kSecReturnAttributes : @YES - }; - - CFDictionaryRef result = NULL; - OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, - (CFTypeRef *)&result); - - if (status == errSecItemNotFound) { - status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result); - } - - if (status != errSecSuccess) - return nil; - - NSString *accessGroup = [(__bridge NSDictionary *)result - objectForKey:(__bridge NSString *)kSecAttrAccessGroup]; - if (result) - CFRelease(result); - - return accessGroup; + NSDictionary *query = @{ + (__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword, + (__bridge NSString *)kSecAttrAccount : @"bundleSeedID", + (__bridge NSString *)kSecAttrService : @"", + (__bridge NSString *)kSecReturnAttributes : @YES + }; + + CFDictionaryRef result = NULL; + OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result); + + if (status == errSecItemNotFound) { + status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result); + } + + if (status != errSecSuccess) + return nil; + + NSString *accessGroup = + [(__bridge NSDictionary *)result objectForKey:(__bridge NSString *)kSecAttrAccessGroup]; + if (result) + CFRelease(result); + + return accessGroup; } static BOOL isSelfCall(void) { - NSArray *address = [NSThread callStackReturnAddresses]; - Dl_info info = {0}; - if (dladdr((void *)[address[2] longLongValue], &info) == 0) - return NO; - NSString *path = [NSString stringWithUTF8String:info.dli_fname]; - return [path hasPrefix:NSBundle.mainBundle.bundlePath]; + NSArray *address = [NSThread callStackReturnAddresses]; + Dl_info info = {0}; + if (dladdr((void *)[address[2] longLongValue], &info) == 0) + return NO; + NSString *path = [NSString stringWithUTF8String:info.dli_fname]; + return [path hasPrefix:NSBundle.mainBundle.bundlePath]; } %group Sideloading %hook NSBundle - (NSString *)bundleIdentifier { - return isSelfCall() ? DISCORD_BUNDLE_ID : %orig; + return isSelfCall() ? DISCORD_BUNDLE_ID : %orig; } - (NSDictionary *)infoDictionary { - if (!isSelfCall()) - return %orig; - - NSMutableDictionary *info = [%orig mutableCopy]; - info[@"CFBundleIdentifier"] = DISCORD_BUNDLE_ID; - info[@"CFBundleDisplayName"] = DISCORD_NAME; - info[@"CFBundleName"] = DISCORD_NAME; - return info; + if (!isSelfCall()) + return %orig; + + NSMutableDictionary *info = [%orig mutableCopy]; + info[@"CFBundleIdentifier"] = DISCORD_BUNDLE_ID; + info[@"CFBundleDisplayName"] = DISCORD_NAME; + info[@"CFBundleName"] = DISCORD_NAME; + return info; } - (id)objectForInfoDictionaryKey:(NSString *)key { - if (!isSelfCall()) - return %orig; + if (!isSelfCall()) + return %orig; - if ([key isEqualToString:@"CFBundleIdentifier"]) - return DISCORD_BUNDLE_ID; - if ([key isEqualToString:@"CFBundleDisplayName"] || - [key isEqualToString:@"CFBundleName"]) - return DISCORD_NAME; - return %orig; + if ([key isEqualToString:@"CFBundleIdentifier"]) + return DISCORD_BUNDLE_ID; + if ([key isEqualToString:@"CFBundleDisplayName"] || [key isEqualToString:@"CFBundleName"]) + return DISCORD_NAME; + return %orig; } %end %hook NSFileManager -- (NSURL *)containerURLForSecurityApplicationGroupIdentifier: - (NSString *)groupIdentifier { - BunnyLog(@"containerURLForSecurityApplicationGroupIdentifier called! %@", - groupIdentifier ?: @"nil"); - - NSArray *paths = [self URLsForDirectory:NSDocumentDirectory - inDomains:NSUserDomainMask]; - NSURL *lastPath = [paths lastObject]; - return [lastPath URLByAppendingPathComponent:@"AppGroup"]; +- (NSURL *)containerURLForSecurityApplicationGroupIdentifier:(NSString *)groupIdentifier { + BunnyLog(@"containerURLForSecurityApplicationGroupIdentifier called! %@", + groupIdentifier ?: @"nil"); + + NSArray *paths = [self URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; + NSURL *lastPath = [paths lastObject]; + return [lastPath URLByAppendingPathComponent:@"AppGroup"]; } %end %hook UIPasteboard - (NSString *)_accessGroup { - return getAccessGroupID(); + return getAccessGroupID(); } %end %end %ctor { - BOOL isAppStoreApp = [[NSFileManager defaultManager] - fileExistsAtPath:[[NSBundle mainBundle] appStoreReceiptURL].path]; - if (!isAppStoreApp) - %init(Sideloading); + BOOL isAppStoreApp = [[NSFileManager defaultManager] + fileExistsAtPath:[[NSBundle mainBundle] appStoreReceiptURL].path]; + if (!isAppStoreApp) + %init(Sideloading); } diff --git a/Sources/Theme.m b/Sources/Theme.m index ca4f792..e897918 100644 --- a/Sources/Theme.m +++ b/Sources/Theme.m @@ -3,82 +3,78 @@ #import "Utils.h" #import -void swizzleDCDThemeColor( - NSDictionary *> *semanticColors) { - BunnyLog(@"Swizzling DCDThemeColor"); - - Class DCDTheme = NSClassFromString(@"DCDTheme"); - Class dcdThemeTarget = object_getClass(DCDTheme); - - SEL themeIndexSelector = NSSelectorFromString(@"themeIndex"); - Method themeIndexMethod = - class_getClassMethod(dcdThemeTarget, themeIndexSelector); - IMP themeIndexImpl = method_getImplementation(themeIndexMethod); - int (*themeIndex)(id, SEL) = (int (*)(id, SEL))themeIndexImpl; - - Class DCDThemeColor = NSClassFromString(@"DCDThemeColor"); - Class target = object_getClass(DCDThemeColor); - - unsigned int methodCount; - Method *methods = class_copyMethodList(target, &methodCount); - - for (unsigned int i = 0; i < methodCount; i++) { - Method method = methods[i]; - SEL selector = method_getName(method); - NSString *methodName = NSStringFromSelector(selector); - - NSArray *semanticColor = semanticColors[methodName]; - if (semanticColor) { - BunnyLog(@"Swizzling %@", methodName); - - IMP originalImpl = method_getImplementation(method); - UIColor *(*original)(id, SEL) = (UIColor * (*)(id, SEL)) originalImpl; - - id block = ^UIColor *(id self) { - int themeIndexVal = themeIndex(dcdThemeTarget, themeIndexSelector); - if (semanticColor.count - 1 >= themeIndexVal) { - UIColor *semanticUIColor = hexToUIColor(semanticColor[themeIndexVal]); - if (semanticUIColor) { - return semanticUIColor; - } +void swizzleDCDThemeColor(NSDictionary *> *semanticColors) { + BunnyLog(@"Swizzling DCDThemeColor"); + + Class DCDTheme = NSClassFromString(@"DCDTheme"); + Class dcdThemeTarget = object_getClass(DCDTheme); + + SEL themeIndexSelector = NSSelectorFromString(@"themeIndex"); + Method themeIndexMethod = class_getClassMethod(dcdThemeTarget, themeIndexSelector); + IMP themeIndexImpl = method_getImplementation(themeIndexMethod); + int (*themeIndex)(id, SEL) = (int (*)(id, SEL))themeIndexImpl; + + Class DCDThemeColor = NSClassFromString(@"DCDThemeColor"); + Class target = object_getClass(DCDThemeColor); + + unsigned int methodCount; + Method *methods = class_copyMethodList(target, &methodCount); + + for (unsigned int i = 0; i < methodCount; i++) { + Method method = methods[i]; + SEL selector = method_getName(method); + NSString *methodName = NSStringFromSelector(selector); + + NSArray *semanticColor = semanticColors[methodName]; + if (semanticColor) { + BunnyLog(@"Swizzling %@", methodName); + + IMP originalImpl = method_getImplementation(method); + UIColor *(*original)(id, SEL) = (UIColor * (*)(id, SEL)) originalImpl; + + id block = ^UIColor *(id self) { + int themeIndexVal = themeIndex(dcdThemeTarget, themeIndexSelector); + if (semanticColor.count - 1 >= themeIndexVal) { + UIColor *semanticUIColor = hexToUIColor(semanticColor[themeIndexVal]); + if (semanticUIColor) { + return semanticUIColor; + } + } + return original(target, selector); + }; + + IMP newImpl = imp_implementationWithBlock(block); + method_setImplementation(method, newImpl); } - return original(target, selector); - }; - - IMP newImpl = imp_implementationWithBlock(block); - method_setImplementation(method, newImpl); } - } - free(methods); + free(methods); } void swizzleUIColor(NSDictionary *rawColors) { - BunnyLog(@"Swizzling UIColor"); + BunnyLog(@"Swizzling UIColor"); - Class UIColorClass = NSClassFromString(@"UIColor"); - Class target = object_getClass(UIColorClass); + Class UIColorClass = NSClassFromString(@"UIColor"); + Class target = object_getClass(UIColorClass); - unsigned int methodCount; - Method *methods = class_copyMethodList(target, &methodCount); + unsigned int methodCount; + Method *methods = class_copyMethodList(target, &methodCount); - for (unsigned int i = 0; i < methodCount; i++) { - Method method = methods[i]; - SEL selector = method_getName(method); - NSString *methodName = NSStringFromSelector(selector); + for (unsigned int i = 0; i < methodCount; i++) { + Method method = methods[i]; + SEL selector = method_getName(method); + NSString *methodName = NSStringFromSelector(selector); - NSString *rawColor = rawColors[methodName]; - if (rawColor) { - BunnyLog(@"Swizzling %@", methodName); + NSString *rawColor = rawColors[methodName]; + if (rawColor) { + BunnyLog(@"Swizzling %@", methodName); - id block = ^UIColor *(id self) { - return hexToUIColor(rawColor); - }; + id block = ^UIColor *(id self) { return hexToUIColor(rawColor); }; - IMP newImpl = imp_implementationWithBlock(block); - method_setImplementation(method, newImpl); + IMP newImpl = imp_implementationWithBlock(block); + method_setImplementation(method, newImpl); + } } - } - free(methods); + free(methods); } \ No newline at end of file diff --git a/Sources/Tweak.x b/Sources/Tweak.x index ed79a0a..e8a1287 100644 --- a/Sources/Tweak.x +++ b/Sources/Tweak.x @@ -14,203 +14,183 @@ static LoaderConfig *loaderConfig; %hook RCTCxxBridge -- (void)executeApplicationScript:(NSData *)script - url:(NSURL *)url - async:(BOOL)async { - if (![url.absoluteString containsString:@"main.jsbundle"]) { - return %orig; - } - - NSBundle *bunnyPatchesBundle = - [NSBundle bundleWithPath:bunnyPatchesBundlePath]; - if (!bunnyPatchesBundle) { - BunnyLog(@"Failed to load BunnyPatches bundle from path: %@", - bunnyPatchesBundlePath); - showErrorAlert( - @"Loader Error", - @"Failed to initialize mod loader. Please reinstall the tweak."); - return %orig; - } - - NSURL *patchPath = [bunnyPatchesBundle URLForResource:@"payload-base" - withExtension:@"js"]; - if (!patchPath) { - BunnyLog(@"Failed to find payload-base.js in bundle"); - showErrorAlert( - @"Loader Error", - @"Failed to initialize mod loader. Please reinstall the tweak."); - return %orig; - } - - NSData *patchData = [NSData dataWithContentsOfURL:patchPath]; - BunnyLog(@"Injecting loader"); - %orig(patchData, source, YES); - - __block NSData *bundle = [NSData - dataWithContentsOfURL:[pyoncordDirectory - URLByAppendingPathComponent:@"bundle.js"]]; - - dispatch_group_t group = dispatch_group_create(); - dispatch_group_enter(group); - - NSURL *bundleUrl; - if (loaderConfig.customLoadUrlEnabled && loaderConfig.customLoadUrl) { - bundleUrl = loaderConfig.customLoadUrl; - BunnyLog(@"Using custom load URL: %@", bundleUrl.absoluteString); - } else { - bundleUrl = [NSURL URLWithString:@"https://raw.githubusercontent.com/" - @"bunny-mod/builds/main/bunny.min.js"]; - BunnyLog(@"Using default bundle URL: %@", bundleUrl.absoluteString); - } - - NSMutableURLRequest *bundleRequest = [NSMutableURLRequest - requestWithURL:bundleUrl - cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData - timeoutInterval:3.0]; - - NSString *bundleEtag = [NSString - stringWithContentsOfURL:[pyoncordDirectory - URLByAppendingPathComponent:@"etag.txt"] - encoding:NSUTF8StringEncoding - error:nil]; - if (bundleEtag && bundle) { - [bundleRequest setValue:bundleEtag forHTTPHeaderField:@"If-None-Match"]; - } - - NSURLSession *session = - [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration - defaultSessionConfiguration]]; - [[session - dataTaskWithRequest:bundleRequest - completionHandler:^(NSData *data, NSURLResponse *response, - NSError *error) { - if ([response isKindOfClass:[NSHTTPURLResponse class]]) { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - if (httpResponse.statusCode == 200) { - bundle = data; - [bundle writeToURL:[pyoncordDirectory - URLByAppendingPathComponent:@"bundle.js"] - atomically:YES]; - - NSString *etag = - [httpResponse.allHeaderFields objectForKey:@"Etag"]; - if (etag) { - [etag writeToURL:[pyoncordDirectory - URLByAppendingPathComponent:@"etag.txt"] - atomically:YES - encoding:NSUTF8StringEncoding - error:nil]; +- (void)executeApplicationScript:(NSData *)script url:(NSURL *)url async:(BOOL)async { + if (![url.absoluteString containsString:@"main.jsbundle"]) { + return %orig; + } + + NSBundle *bunnyPatchesBundle = [NSBundle bundleWithPath:bunnyPatchesBundlePath]; + if (!bunnyPatchesBundle) { + BunnyLog(@"Failed to load BunnyPatches bundle from path: %@", bunnyPatchesBundlePath); + showErrorAlert(@"Loader Error", + @"Failed to initialize mod loader. Please reinstall the tweak."); + return %orig; + } + + NSURL *patchPath = [bunnyPatchesBundle URLForResource:@"payload-base" withExtension:@"js"]; + if (!patchPath) { + BunnyLog(@"Failed to find payload-base.js in bundle"); + showErrorAlert(@"Loader Error", + @"Failed to initialize mod loader. Please reinstall the tweak."); + return %orig; + } + + NSData *patchData = [NSData dataWithContentsOfURL:patchPath]; + BunnyLog(@"Injecting loader"); + %orig(patchData, source, YES); + + __block NSData *bundle = + [NSData dataWithContentsOfURL:[pyoncordDirectory URLByAppendingPathComponent:@"bundle.js"]]; + + dispatch_group_t group = dispatch_group_create(); + dispatch_group_enter(group); + + NSURL *bundleUrl; + if (loaderConfig.customLoadUrlEnabled && loaderConfig.customLoadUrl) { + bundleUrl = loaderConfig.customLoadUrl; + BunnyLog(@"Using custom load URL: %@", bundleUrl.absoluteString); + } else { + bundleUrl = [NSURL URLWithString:@"https://raw.githubusercontent.com/" + @"bunny-mod/builds/main/bunny.min.js"]; + BunnyLog(@"Using default bundle URL: %@", bundleUrl.absoluteString); + } + + NSMutableURLRequest *bundleRequest = + [NSMutableURLRequest requestWithURL:bundleUrl + cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData + timeoutInterval:3.0]; + + NSString *bundleEtag = [NSString + stringWithContentsOfURL:[pyoncordDirectory URLByAppendingPathComponent:@"etag.txt"] + encoding:NSUTF8StringEncoding + error:nil]; + if (bundleEtag && bundle) { + [bundleRequest setValue:bundleEtag forHTTPHeaderField:@"If-None-Match"]; + } + + NSURLSession *session = [NSURLSession + sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; + [[session + dataTaskWithRequest:bundleRequest + completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; + if (httpResponse.statusCode == 200) { + bundle = data; + [bundle + writeToURL:[pyoncordDirectory URLByAppendingPathComponent:@"bundle.js"] + atomically:YES]; + + NSString *etag = [httpResponse.allHeaderFields objectForKey:@"Etag"]; + if (etag) { + [etag + writeToURL:[pyoncordDirectory URLByAppendingPathComponent:@"etag.txt"] + atomically:YES + encoding:NSUTF8StringEncoding + error:nil]; + } + } } - } - } - dispatch_group_leave(group); - }] resume]; - - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - - NSString *themeString = [NSString - stringWithContentsOfURL: - [pyoncordDirectory URLByAppendingPathComponent:@"current-theme.json"] - encoding:NSUTF8StringEncoding - error:nil]; - if (themeString) { - NSString *jsCode = - [NSString stringWithFormat:@"globalThis.__PYON_LOADER__.storedTheme=%@", - themeString]; - %orig([jsCode dataUsingEncoding:NSUTF8StringEncoding], source, - async); - } - - NSData *fontData = [NSData - dataWithContentsOfURL:[pyoncordDirectory - URLByAppendingPathComponent:@"fonts.json"]]; - if (fontData) { - NSError *jsonError; - NSDictionary *fontDict = - [NSJSONSerialization JSONObjectWithData:fontData - options:0 - error:&jsonError]; - if (!jsonError && fontDict[@"main"]) { - BunnyLog(@"Found font configuration, applying..."); - patchFonts(fontDict[@"main"], fontDict[@"name"]); + dispatch_group_leave(group); + }] resume]; + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + + NSString *themeString = + [NSString stringWithContentsOfURL:[pyoncordDirectory + URLByAppendingPathComponent:@"current-theme.json"] + encoding:NSUTF8StringEncoding + error:nil]; + if (themeString) { + NSString *jsCode = + [NSString stringWithFormat:@"globalThis.__PYON_LOADER__.storedTheme=%@", themeString]; + %orig([jsCode dataUsingEncoding:NSUTF8StringEncoding], source, async); } - } - - if (bundle) { - BunnyLog(@"Executing JS bundle"); - %orig(bundle, source, async); - } - - NSURL *preloadsDirectory = - [pyoncordDirectory URLByAppendingPathComponent:@"preloads"]; - if ([[NSFileManager defaultManager] - fileExistsAtPath:preloadsDirectory.path]) { - NSError *error = nil; - NSArray *contents = [[NSFileManager defaultManager] - contentsOfDirectoryAtURL:preloadsDirectory - includingPropertiesForKeys:nil - options:0 - error:&error]; - if (!error) { - for (NSURL *fileURL in contents) { - if ([[fileURL pathExtension] isEqualToString:@"js"]) { - BunnyLog(@"Executing preload JS file %@", fileURL.absoluteString); - NSData *data = [NSData dataWithContentsOfURL:fileURL]; - if (data) { - %orig(data, source, async); - } + + NSData *fontData = [NSData + dataWithContentsOfURL:[pyoncordDirectory URLByAppendingPathComponent:@"fonts.json"]]; + if (fontData) { + NSError *jsonError; + NSDictionary *fontDict = [NSJSONSerialization JSONObjectWithData:fontData + options:0 + error:&jsonError]; + if (!jsonError && fontDict[@"main"]) { + BunnyLog(@"Found font configuration, applying..."); + patchFonts(fontDict[@"main"], fontDict[@"name"]); } - } - } else { - BunnyLog(@"Error reading contents of preloads directory"); } - } - %orig(script, url, async); + if (bundle) { + BunnyLog(@"Executing JS bundle"); + %orig(bundle, source, async); + } + + NSURL *preloadsDirectory = [pyoncordDirectory URLByAppendingPathComponent:@"preloads"]; + if ([[NSFileManager defaultManager] fileExistsAtPath:preloadsDirectory.path]) { + NSError *error = nil; + NSArray *contents = + [[NSFileManager defaultManager] contentsOfDirectoryAtURL:preloadsDirectory + includingPropertiesForKeys:nil + options:0 + error:&error]; + if (!error) { + for (NSURL *fileURL in contents) { + if ([[fileURL pathExtension] isEqualToString:@"js"]) { + BunnyLog(@"Executing preload JS file %@", fileURL.absoluteString); + NSData *data = [NSData dataWithContentsOfURL:fileURL]; + if (data) { + %orig(data, source, async); + } + } + } + } else { + BunnyLog(@"Error reading contents of preloads directory"); + } + } + + %orig(script, url, async); } %end %ctor { - @autoreleasepool { - source = [NSURL URLWithString:@"bunny"]; - - NSString *install_prefix = @"/var/jb"; - isJailbroken = - [[NSFileManager defaultManager] fileExistsAtPath:install_prefix]; - - NSString *bundlePath = - [NSString stringWithFormat: - @"%@/Library/Application Support/BunnyResources.bundle", - install_prefix]; - BunnyLog(@"Is jailbroken: %d", isJailbroken); - BunnyLog(@"Bundle path for jailbroken: %@", bundlePath); - - NSString *jailedPath = [[NSBundle mainBundle].bundleURL.path - stringByAppendingPathComponent:@"BunnyResources.bundle"]; - BunnyLog(@"Bundle path for jailed: %@", jailedPath); - - bunnyPatchesBundlePath = isJailbroken ? bundlePath : jailedPath; - BunnyLog(@"Selected bundle path: %@", bunnyPatchesBundlePath); - - BOOL bundleExists = [[NSFileManager defaultManager] - fileExistsAtPath:bunnyPatchesBundlePath]; - BunnyLog(@"Bundle exists at path: %d", bundleExists); - - NSError *error = nil; - NSArray *bundleContents = [[NSFileManager defaultManager] - contentsOfDirectoryAtPath:bunnyPatchesBundlePath - error:&error]; - if (error) { - BunnyLog(@"Error listing bundle contents: %@", error); - } else { - BunnyLog(@"Bundle contents: %@", bundleContents); - } + @autoreleasepool { + source = [NSURL URLWithString:@"bunny"]; + + NSString *install_prefix = @"/var/jb"; + isJailbroken = [[NSFileManager defaultManager] fileExistsAtPath:install_prefix]; + + NSString *bundlePath = + [NSString stringWithFormat:@"%@/Library/Application Support/BunnyResources.bundle", + install_prefix]; + BunnyLog(@"Is jailbroken: %d", isJailbroken); + BunnyLog(@"Bundle path for jailbroken: %@", bundlePath); + + NSString *jailedPath = [[NSBundle mainBundle].bundleURL.path + stringByAppendingPathComponent:@"BunnyResources.bundle"]; + BunnyLog(@"Bundle path for jailed: %@", jailedPath); + + bunnyPatchesBundlePath = isJailbroken ? bundlePath : jailedPath; + BunnyLog(@"Selected bundle path: %@", bunnyPatchesBundlePath); + + BOOL bundleExists = + [[NSFileManager defaultManager] fileExistsAtPath:bunnyPatchesBundlePath]; + BunnyLog(@"Bundle exists at path: %d", bundleExists); + + NSError *error = nil; + NSArray *bundleContents = + [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bunnyPatchesBundlePath + error:&error]; + if (error) { + BunnyLog(@"Error listing bundle contents: %@", error); + } else { + BunnyLog(@"Bundle contents: %@", bundleContents); + } - pyoncordDirectory = getPyoncordDirectory(); - loaderConfig = [[LoaderConfig alloc] init]; - [loaderConfig loadConfig]; + pyoncordDirectory = getPyoncordDirectory(); + loaderConfig = [[LoaderConfig alloc] init]; + [loaderConfig loadConfig]; - %init; - } + %init; + } } diff --git a/Sources/Utils.m b/Sources/Utils.m index 911a46f..bf4d274 100644 --- a/Sources/Utils.m +++ b/Sources/Utils.m @@ -1,75 +1,70 @@ #import "Utils.h" NSURL *getPyoncordDirectory(void) { - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSURL *documentDirectoryURL = - [[fileManager URLsForDirectory:NSDocumentDirectory - inDomains:NSUserDomainMask] lastObject]; + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSURL *documentDirectoryURL = [[fileManager URLsForDirectory:NSDocumentDirectory + inDomains:NSUserDomainMask] lastObject]; - NSURL *pyoncordFolderURL = - [documentDirectoryURL URLByAppendingPathComponent:@"pyoncord"]; + NSURL *pyoncordFolderURL = [documentDirectoryURL URLByAppendingPathComponent:@"pyoncord"]; - if (![fileManager fileExistsAtPath:pyoncordFolderURL.path]) { - [fileManager createDirectoryAtURL:pyoncordFolderURL - withIntermediateDirectories:YES - attributes:nil - error:nil]; - } + if (![fileManager fileExistsAtPath:pyoncordFolderURL.path]) { + [fileManager createDirectoryAtURL:pyoncordFolderURL + withIntermediateDirectories:YES + attributes:nil + error:nil]; + } - return pyoncordFolderURL; + return pyoncordFolderURL; } UIColor *hexToUIColor(NSString *hex) { - if (![hex hasPrefix:@"#"]) { - return nil; - } + if (![hex hasPrefix:@"#"]) { + return nil; + } - NSString *hexColor = [hex substringFromIndex:1]; - if (hexColor.length == 6) { - hexColor = [hexColor stringByAppendingString:@"ff"]; - } + NSString *hexColor = [hex substringFromIndex:1]; + if (hexColor.length == 6) { + hexColor = [hexColor stringByAppendingString:@"ff"]; + } - if (hexColor.length == 8) { - unsigned int hexNumber; - NSScanner *scanner = [NSScanner scannerWithString:hexColor]; - if ([scanner scanHexInt:&hexNumber]) { - CGFloat r = ((hexNumber & 0xFF000000) >> 24) / 255.0; - CGFloat g = ((hexNumber & 0x00FF0000) >> 16) / 255.0; - CGFloat b = ((hexNumber & 0x0000FF00) >> 8) / 255.0; - CGFloat a = (hexNumber & 0x000000FF) / 255.0; + if (hexColor.length == 8) { + unsigned int hexNumber; + NSScanner *scanner = [NSScanner scannerWithString:hexColor]; + if ([scanner scanHexInt:&hexNumber]) { + CGFloat r = ((hexNumber & 0xFF000000) >> 24) / 255.0; + CGFloat g = ((hexNumber & 0x00FF0000) >> 16) / 255.0; + CGFloat b = ((hexNumber & 0x0000FF00) >> 8) / 255.0; + CGFloat a = (hexNumber & 0x000000FF) / 255.0; - return [UIColor colorWithRed:r green:g blue:b alpha:a]; + return [UIColor colorWithRed:r green:g blue:b alpha:a]; + } } - } - return nil; + return nil; } void showErrorAlert(NSString *title, NSString *message) { - dispatch_async(dispatch_get_main_queue(), ^{ - UIAlertController *alert = [UIAlertController - alertControllerWithTitle:title - message:message - preferredStyle:UIAlertControllerStyleAlert]; + dispatch_async(dispatch_get_main_queue(), ^{ + UIAlertController *alert = + [UIAlertController alertControllerWithTitle:title + message:message + preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *okAction = - [UIAlertAction actionWithTitle:@"OK" - style:UIAlertActionStyleDefault - handler:nil]; + UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:nil]; - [alert addAction:okAction]; + [alert addAction:okAction]; - UIWindow *window = nil; - NSArray *windows = [[UIApplication sharedApplication] windows]; - for (UIWindow *w in windows) { - if (w.isKeyWindow) { - window = w; - break; - } - } + UIWindow *window = nil; + NSArray *windows = [[UIApplication sharedApplication] windows]; + for (UIWindow *w in windows) { + if (w.isKeyWindow) { + window = w; + break; + } + } - [window.rootViewController presentViewController:alert - animated:YES - completion:nil]; - }); + [window.rootViewController presentViewController:alert animated:YES completion:nil]; + }); } \ No newline at end of file