XCThemeService.m 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. //
  2. // XCThemeService.m
  3. // XCTheme
  4. //
  5. // Created by 邢铖 on 2023/11/19.
  6. //
  7. #import "XCThemeProtocol.h"
  8. #import "XCTheme-Swift.h"
  9. #import <XCZip/XCZip.h>
  10. @xcmlservice(XCThemeProtocol, XCThemeService)
  11. @synthesize downloadBlock = _downloadBlock;
  12. - (UIUserInterfaceStyle)currentUserInterfaceStyle {
  13. return XCThemeManager.shared.currentUserInterfaceStyle;
  14. }
  15. - (void)prepareThemeTarget:(nullable NSObject *)target block:(nullable void (^)(XCThemeSpecModel * _Nullable))block {
  16. [XCThemeManager.shared prepareThemeBlockWithTarget:target :^(XCThemeSpec * _Nullable theme) {
  17. XCThemeSpecModel * _Nullable wrappedModel = [[XCThemeSpecModel alloc] initWithWrappedObject:theme];
  18. if (block) {
  19. block(wrappedModel);
  20. }
  21. }];
  22. }
  23. #pragma mark - Theme Download
  24. - (NSArray<XCThemeSpecModel *> *)fetchBundleSpecs {
  25. NSString *path = [NSBundle.mainBundle.resourcePath stringByAppendingString:@"/ThemeResources"];
  26. NSArray *items = [[NSFileManager.defaultManager contentsOfDirectoryAtPath:path error:nil] sortedArrayUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2) {
  27. return [obj1 compare:obj2];
  28. }];
  29. NSMutableArray<XCThemeSpecModel *> *specs = [NSMutableArray new];
  30. for (NSString *file in items) {
  31. NSString *filePath = [NSString stringWithFormat:@"%@/%@", path, file];
  32. BOOL isDir = NO;
  33. [NSFileManager.defaultManager fileExistsAtPath:filePath isDirectory:&isDir];
  34. if (isDir) {
  35. XCThemeSpec *theme = [[XCThemeSpec alloc] initWithThemeId:file];
  36. if (theme) {
  37. XCThemeSpecModel *specModel = [[XCThemeSpecModel alloc] initWithWrappedObject:theme];
  38. [specs addObject:specModel];
  39. }
  40. }
  41. }
  42. return [specs copy];
  43. }
  44. - (NSArray<XCThemeSpecModel *> *)fetchDownloadedSpecs {
  45. NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true).firstObject;
  46. NSString *path = [documentsPath stringByAppendingString:@"/ThemeResources"];
  47. NSArray *items = [NSFileManager.defaultManager contentsOfDirectoryAtPath:path error:nil];
  48. NSMutableArray<XCThemeSpecModel *> *specs = [NSMutableArray new];
  49. for (NSString *file in items) {
  50. NSString *filePath = [NSString stringWithFormat:@"%@/%@", path, file];
  51. BOOL isDir = NO;
  52. [NSFileManager.defaultManager fileExistsAtPath:filePath isDirectory:&isDir];
  53. if (isDir) {
  54. XCThemeSpec *theme = [[XCThemeSpec alloc] initWithThemeId:filePath];
  55. if (theme) {
  56. XCThemeSpecModel *specModel = [[XCThemeSpecModel alloc] initWithWrappedObject:theme];
  57. [specs addObject:specModel];
  58. }
  59. }
  60. }
  61. return [specs copy];
  62. }
  63. - (NSArray<XCThemeSpecModel *> *)fetchLocalSpecs {
  64. NSMutableArray *specs = [NSMutableArray new];
  65. [specs addObjectsFromArray:[self fetchBundleSpecs]];
  66. [specs addObjectsFromArray:[self fetchDownloadedSpecs]];
  67. return [specs copy];
  68. }
  69. - (void)fetchThemeData:(NSString *)themeId completion:(void (^)(BOOL, NSString * _Nonnull, XCThemeSpecModel * _Nullable))completion {
  70. NSArray<XCThemeSpecModel *> *allLocalSpecs = [self fetchLocalSpecs];
  71. for (XCThemeSpecModel *spec in allLocalSpecs) {
  72. if ([spec.themeId isEqualToString:themeId]) {
  73. if (completion) {
  74. completion(YES, @"", spec);
  75. return;
  76. }
  77. }
  78. }
  79. if (!self.downloadBlock) {
  80. if (completion) {
  81. completion(NO, @"downloadBlock not specified", nil);
  82. }
  83. @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"downloadBlock not specified" userInfo:nil];
  84. }
  85. NSString *rootPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
  86. NSString *downloadTHMPath = [rootPath stringByAppendingFormat:@"/ThemeResources/%@.thm", themeId];
  87. NSString *extractPath = [rootPath stringByAppendingFormat:@"/ThemeResources/%@", themeId];
  88. self.downloadBlock(themeId, downloadTHMPath, ^(BOOL success, NSString * _Nonnull reason) {
  89. if (!success) {
  90. if (completion) {
  91. completion(NO, reason, nil);
  92. }
  93. return;
  94. }
  95. BOOL extractSuccess = [SSZipArchive unzipFileAtPath:downloadTHMPath toDestination:extractPath uniqueId:nil];
  96. [NSFileManager.defaultManager removeItemAtPath:downloadTHMPath error:nil];
  97. if (!extractSuccess) {
  98. [NSFileManager.defaultManager removeItemAtPath:extractPath error:nil];
  99. if (completion) {
  100. completion(NO, @"Extract download failed", nil);
  101. }
  102. return;
  103. }
  104. XCThemeSpec *spec = [[XCThemeSpec alloc] initWithThemeId:themeId];
  105. if (spec) {
  106. XCThemeSpecModel *specModel = [[XCThemeSpecModel alloc] initWithWrappedObject:spec];
  107. if (completion) {
  108. completion(YES, @"", specModel);
  109. }
  110. } else {
  111. [NSFileManager.defaultManager removeItemAtPath:extractPath error:nil];
  112. if (completion) {
  113. completion(NO, @"Theme not completed", nil);
  114. }
  115. }
  116. });
  117. }
  118. - (XCThemeSpecModel *)currentTheme {
  119. if (XCThemeManager.shared.selectedTheme) {
  120. return [[XCThemeSpecModel alloc] initWithWrappedObject:XCThemeManager.shared.selectedTheme];
  121. }
  122. return nil;
  123. }
  124. - (void)applyTheme:(XCThemeSpecModel *)localSpec {
  125. XCThemeManager.shared.selectedTheme = localSpec.wrappedObject;
  126. }
  127. @end