TUICore.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. // Created by Tencent on 2023/06/09.
  2. // Copyright © 2023 Tencent. All rights reserved.
  3. #import "TUICore.h"
  4. #import <objc/runtime.h>
  5. #import "TUIDefine.h"
  6. #import "TUIThemeManager.h"
  7. #import "TUIWeakProxy.h"
  8. @implementation TUICore
  9. + (void)initialize {
  10. TUIRegisterThemeResourcePath(TUICoreThemePath, TUIThemeModuleCore);
  11. TUIRegisterThemeResourcePath(TUIBundlePath(@"TUICoreTheme_Minimalist", TUICoreBundle_Key_Class), TUIThemeModuleCore_Minimalist);
  12. }
  13. + (void)registerService:(NSString *)serviceName object:(id<TUIServiceProtocol>)object {
  14. [TUIServiceManager.shareInstance registerService:serviceName service:object];
  15. }
  16. + (void)unregisterService:(NSString *)serviceName {
  17. [TUIServiceManager.shareInstance unregisterService:serviceName];
  18. }
  19. + (id<TUIServiceProtocol>)getService:(NSString *)serviceName {
  20. return [TUIServiceManager.shareInstance getService:serviceName];
  21. }
  22. + (id)callService:(NSString *)serviceName method:(NSString *)method param:(nullable NSDictionary *)param {
  23. return [TUIServiceManager.shareInstance callService:serviceName method:method param:param resultCallback:nil];
  24. }
  25. + (id)callService:(NSString *)serviceName
  26. method:(NSString *)method
  27. param:(nullable NSDictionary *)param
  28. resultCallback:(nullable TUICallServiceResultCallback)resultCallback {
  29. return [TUIServiceManager.shareInstance callService:serviceName method:method param:param resultCallback:resultCallback];
  30. }
  31. + (void)registerEvent:(NSString *)key subKey:(NSString *)subKey object:(id<TUINotificationProtocol>)object {
  32. [TUIEventManager.shareInstance registerEvent:key subKey:subKey object:object];
  33. }
  34. + (void)unRegisterEventByObject:(id<TUINotificationProtocol>)object {
  35. [TUIEventManager.shareInstance unRegisterEvent:object];
  36. }
  37. + (void)unRegisterEvent:(nullable NSString *)key subKey:(nullable NSString *)subKey object:(nullable id<TUINotificationProtocol>)object {
  38. [TUIEventManager.shareInstance unRegisterEvent:key subKey:subKey object:object];
  39. }
  40. + (void)notifyEvent:(NSString *)key subKey:(NSString *)subKey object:(nullable id)anObject param:(nullable NSDictionary *)param {
  41. [TUIEventManager.shareInstance notifyEvent:key subKey:subKey object:anObject param:param];
  42. }
  43. + (void)registerExtension:(NSString *)extensionID object:(id<TUIExtensionProtocol>)object {
  44. [TUIExtensionManager.shareInstance registerExtension:extensionID extension:object];
  45. }
  46. + (void)unRegisterExtension:(NSString *)extensionID object:(id<TUIExtensionProtocol>)object {
  47. [TUIExtensionManager.shareInstance unRegisterExtension:extensionID extension:object];
  48. }
  49. + (NSDictionary *)getExtensionInfo:(NSString *)extensionID param:(nullable NSDictionary *)param {
  50. return [TUIExtensionManager.shareInstance getExtensionInfo:extensionID param:param];
  51. }
  52. + (NSArray<TUIExtensionInfo *> *)getExtensionList:(NSString *)extensionID param:(nullable NSDictionary *)param {
  53. return [TUIExtensionManager.shareInstance getExtensionList:extensionID param:param];
  54. }
  55. + (BOOL)raiseExtension:(NSString *)extensionID parentView:(UIView *)parentView param:(nullable NSDictionary *)param {
  56. return [TUIExtensionManager.shareInstance raiseExtension:extensionID parentView:parentView param:param];
  57. }
  58. + (void)registerObjectFactory:(NSString *)factoryName objectFactory:(id<TUIObjectProtocol>)objectFactory {
  59. [TUIObjectFactoryManager.shareInstance registerObjectFactory:factoryName objectFactory:objectFactory];
  60. }
  61. + (void)unRegisterObjectFactory:(NSString *)factoryName {
  62. [TUIObjectFactoryManager.shareInstance unRegisterObjectFactory:factoryName];
  63. }
  64. + (id)createObject:(NSString *)factoryName key:(NSString *)key param:(NSDictionary *)param {
  65. return [TUIObjectFactoryManager.shareInstance createObject:factoryName method:key param:param];
  66. }
  67. @end
  68. #pragma mark - TUIRoute
  69. static const void *navigateValueCallback = @"navigateValueCallback";
  70. @implementation NSObject (TUIRoute)
  71. - (void)setNavigateValueCallback:(TUIValueResultCallback)callback {
  72. objc_setAssociatedObject(self, navigateValueCallback, callback, OBJC_ASSOCIATION_COPY_NONATOMIC);
  73. }
  74. - (TUIValueResultCallback)navigateValueCallback {
  75. return objc_getAssociatedObject(self, navigateValueCallback);
  76. }
  77. @end
  78. @implementation UIViewController (TUIRoute)
  79. - (void)pushViewController:(NSString *)viewControllerKey param:(nullable NSDictionary *)param forResult:(nullable TUIValueResultCallback)callback {
  80. NSAssert([self isKindOfClass:UINavigationController.class], @"self must be a navigation controller");
  81. UIViewController *vc = [TUIObjectFactoryManager.shareInstance createObject:viewControllerKey param:param];
  82. if ([vc isKindOfClass:UIViewController.class]) {
  83. vc.navigateValueCallback = callback;
  84. [(UINavigationController *)self pushViewController:vc animated:YES];
  85. } else {
  86. NSAssert(false, @"viewControllerKey not exists or invalid");
  87. }
  88. }
  89. - (void)presentViewController:(NSString *)viewControllerKey param:(nullable NSDictionary *)param forResult:(nullable TUIValueResultCallback)callback {
  90. [self presentViewController:viewControllerKey param:param embbedIn:nil forResult:callback];
  91. }
  92. - (void)presentViewController:(NSString *)viewControllerKey
  93. param:(nullable NSDictionary *)param
  94. embbedIn:(nullable UINavigationController *)navigationVC
  95. forResult:(nullable TUIValueResultCallback)callback {
  96. UIViewController *vc = [TUIObjectFactoryManager.shareInstance createObject:viewControllerKey param:param];
  97. if ([vc isKindOfClass:UIViewController.class]) {
  98. vc.navigateValueCallback = callback;
  99. if (navigationVC) {
  100. if ([navigationVC isKindOfClass:UINavigationController.class]) {
  101. NSMutableArray *arrayM = [NSMutableArray array];
  102. if (navigationVC.viewControllers.count > 0) {
  103. [arrayM addObjectsFromArray:navigationVC.viewControllers];
  104. }
  105. [arrayM addObject:vc];
  106. navigationVC.viewControllers = [NSArray arrayWithArray:arrayM];
  107. [self presentViewController:navigationVC animated:YES completion:nil];
  108. } else {
  109. NSAssert(false, @"navigationVC must be a navigation controller");
  110. }
  111. } else {
  112. [self presentViewController:vc animated:YES completion:nil];
  113. }
  114. } else {
  115. NSAssert(false, @"viewControllerKey not exists or invalid");
  116. }
  117. }
  118. @end
  119. #pragma mark - TUIService
  120. @interface TUIServiceManager ()
  121. @property(nonatomic, strong) NSMapTable<NSString *, id<TUIServiceProtocol>> *serviceMap;
  122. @end
  123. @implementation TUIServiceManager
  124. + (instancetype)shareInstance {
  125. static id instance = nil;
  126. if (instance == nil) {
  127. static dispatch_once_t onceToken;
  128. dispatch_once(&onceToken, ^{
  129. instance = [[self alloc] init];
  130. });
  131. }
  132. return instance;
  133. }
  134. - (void)registerService:(NSString *)serviceName service:(id<TUIServiceProtocol>)service {
  135. NSAssert(serviceName.length > 0, @"invalid service name");
  136. NSAssert(service != nil, @"invalid service");
  137. if (serviceName && service) {
  138. @synchronized(self.serviceMap) {
  139. [self.serviceMap setObject:service forKey:serviceName];
  140. }
  141. }
  142. }
  143. - (void)unregisterService:(NSString *)serviceName {
  144. NSAssert(serviceName.length > 0, @"invalid service name");
  145. if (serviceName) {
  146. @synchronized(self.serviceMap) {
  147. [self.serviceMap removeObjectForKey:serviceName];
  148. }
  149. }
  150. }
  151. - (nullable id<TUIServiceProtocol>)getService:(NSString *)serviceName {
  152. id<TUIServiceProtocol> service = nil;
  153. @synchronized(self.serviceMap) {
  154. service = [self.serviceMap objectForKey:serviceName];
  155. }
  156. return service;
  157. }
  158. - (nullable id)callService:(NSString *)serviceName
  159. method:(NSString *)method
  160. param:(nullable NSDictionary *)param
  161. resultCallback:(nullable TUICallServiceResultCallback)resultCallback {
  162. NSAssert(serviceName.length > 0, @"invalid service name");
  163. NSAssert(method.length > 0, @"invalid method");
  164. id<TUIServiceProtocol> service = [self getService:serviceName];
  165. id result = nil;
  166. if (resultCallback) {
  167. if (service && [service respondsToSelector:@selector(onCall:param:resultCallback:)]) {
  168. result = [service onCall:method param:param resultCallback:resultCallback];
  169. }
  170. } else {
  171. if (service && [service respondsToSelector:@selector(onCall:param:)]) {
  172. result = [service onCall:method param:param];
  173. }
  174. }
  175. return result;
  176. }
  177. - (NSMapTable<NSString *, id<TUIServiceProtocol>> *)serviceMap {
  178. if (_serviceMap == nil) {
  179. _serviceMap = [NSMapTable strongToWeakObjectsMapTable];
  180. }
  181. return _serviceMap;
  182. }
  183. @end
  184. #pragma mark - TUIEvent
  185. @interface TUIEventManager ()
  186. @property(nonatomic, strong) NSMutableArray<NSDictionary *> *eventList;
  187. @end
  188. @implementation TUIEventManager
  189. + (instancetype)shareInstance {
  190. static id instance = nil;
  191. if (instance == nil) {
  192. static dispatch_once_t onceToken;
  193. dispatch_once(&onceToken, ^{
  194. instance = [[self alloc] init];
  195. });
  196. }
  197. return instance;
  198. }
  199. - (void)registerEvent:(NSString *)key subKey:(NSString *)subKey object:(id<TUINotificationProtocol>)object {
  200. NSAssert(key.length > 0, @"invalid key");
  201. NSAssert(object != nil, @"invalid object");
  202. if (subKey.length == 0) {
  203. subKey = @"";
  204. }
  205. if (key && subKey && object) {
  206. NSDictionary *event = @{@"key" : [key copy], @"subKey" : [subKey copy], @"object" : [TUIWeakProxy proxyWithTarget:object]};
  207. @synchronized(self.eventList) {
  208. [self.eventList addObject:event];
  209. }
  210. }
  211. }
  212. - (void)unRegisterEvent:(id<TUINotificationProtocol>)object {
  213. [self unRegisterEvent:nil subKey:nil object:object];
  214. }
  215. - (void)unRegisterEvent:(nullable NSString *)key subKey:(nullable NSString *)subKey object:(nullable id<TUINotificationProtocol>)object {
  216. @synchronized(self.eventList) {
  217. NSMutableArray *removeEventList = [NSMutableArray array];
  218. for (NSDictionary *event in self.eventList) {
  219. NSString *pkey = [event objectForKey:@"key"];
  220. NSString *pSubKey = [event objectForKey:@"subKey"];
  221. id pObject = [event objectForKey:@"object"];
  222. if (pObject == nil || [(TUIWeakProxy *)pObject target] == nil) {
  223. [removeEventList addObject:event];
  224. }
  225. if (key == nil && subKey == nil && pObject == object) {
  226. [removeEventList addObject:event];
  227. } else if ([pkey isEqualToString:key] && subKey == nil && object == nil) {
  228. [removeEventList addObject:event];
  229. } else if ([pkey isEqualToString:key] && [subKey isEqualToString:pSubKey] && object == nil) {
  230. [removeEventList addObject:event];
  231. } else if ([pkey isEqualToString:key] && [subKey isEqualToString:pSubKey] && pObject == object) {
  232. [removeEventList addObject:event];
  233. }
  234. }
  235. [self.eventList removeObjectsInArray:removeEventList];
  236. }
  237. }
  238. - (void)notifyEvent:(NSString *)key subKey:(NSString *)subKey object:(nullable id)object param:(nullable NSDictionary *)param {
  239. NSAssert(key.length > 0, @"invalid key");
  240. if (subKey.length == 0) {
  241. subKey = @"";
  242. }
  243. @synchronized(self.eventList) {
  244. for (NSDictionary *event in self.eventList) {
  245. NSString *pkey = [event objectForKey:@"key"];
  246. NSString *pSubKey = [event objectForKey:@"subKey"];
  247. if ([pkey isEqualToString:key] && [pSubKey isEqualToString:subKey]) {
  248. id<TUINotificationProtocol> pObject = [event objectForKey:@"object"];
  249. if (pObject) {
  250. [pObject onNotifyEvent:key subKey:subKey object:object param:param];
  251. }
  252. }
  253. }
  254. }
  255. }
  256. - (NSMutableArray<NSDictionary *> *)eventList {
  257. if (_eventList == nil) {
  258. _eventList = [NSMutableArray array];
  259. }
  260. return _eventList;
  261. }
  262. @end
  263. #pragma mark - TUIExtension
  264. @implementation TUIExtensionInfo
  265. @end
  266. @interface TUIExtensionManager ()
  267. @property(nonatomic, strong) NSMutableDictionary<NSString *, NSHashTable<id<TUIExtensionProtocol>> *> *extensionMap;
  268. @end
  269. @implementation TUIExtensionManager
  270. + (instancetype)shareInstance {
  271. static id instance = nil;
  272. if (instance == nil) {
  273. static dispatch_once_t onceToken;
  274. dispatch_once(&onceToken, ^{
  275. instance = [[self alloc] init];
  276. });
  277. }
  278. return instance;
  279. }
  280. - (void)registerExtension:(NSString *)extensionID extension:(id<TUIExtensionProtocol>)extension {
  281. NSAssert(extensionID.length > 0, @"invalid extension id");
  282. NSAssert(extension != nil, @"invalid extension");
  283. if (extensionID && extension) {
  284. @synchronized(self.extensionMap) {
  285. NSHashTable *list = [self.extensionMap objectForKey:extensionID];
  286. if (list == nil) {
  287. list = [NSHashTable weakObjectsHashTable];
  288. }
  289. [list addObject:extension];
  290. [self.extensionMap setObject:list forKey:extensionID];
  291. }
  292. }
  293. }
  294. - (void)unRegisterExtension:(NSString *)extensionID extension:(id<TUIExtensionProtocol>)extension {
  295. NSAssert(extensionID.length > 0, @"invalid extension id");
  296. NSAssert(extension != nil, @"invalid extension");
  297. if (extensionID && extension) {
  298. @synchronized(self.extensionMap) {
  299. NSHashTable *list = [self.extensionMap objectForKey:extensionID];
  300. if (list == nil) {
  301. list = [NSHashTable weakObjectsHashTable];
  302. }
  303. [list removeObject:extension];
  304. [self.extensionMap setObject:list forKey:extensionID];
  305. }
  306. }
  307. }
  308. - (NSArray<TUIExtensionInfo *> *)getExtensionList:(NSString *)extensionID param:(nullable NSDictionary *)param {
  309. NSAssert(extensionID.length > 0, @"invalid extension id");
  310. NSHashTable *list = nil;
  311. @synchronized(self.extensionMap) {
  312. list = [self.extensionMap objectForKey:extensionID];
  313. }
  314. if (list == nil || list.count == 0) {
  315. return @[];
  316. }
  317. // get
  318. NSMutableArray *resultExtensionInfoList = [NSMutableArray array];
  319. for (id<TUIExtensionProtocol> observer in list) {
  320. if (observer && [observer respondsToSelector:@selector(onGetExtension:param:)]) {
  321. NSArray<TUIExtensionInfo *> *infoList = [observer onGetExtension:extensionID param:param];
  322. if (infoList) {
  323. [resultExtensionInfoList addObjectsFromArray:infoList];
  324. }
  325. }
  326. }
  327. // sort
  328. NSArray *result = [resultExtensionInfoList sortedArrayUsingComparator:^NSComparisonResult(TUIExtensionInfo *obj1, TUIExtensionInfo *obj2) {
  329. if (obj1.weight > obj2.weight) {
  330. return NSOrderedAscending;
  331. } else {
  332. return NSOrderedDescending;
  333. }
  334. }];
  335. return result;
  336. }
  337. - (BOOL)raiseExtension:(NSString *)extensionID parentView:(UIView *)parentView param:(nullable NSDictionary *)param {
  338. NSAssert(extensionID.length > 0, @"invalid extension id");
  339. NSAssert(parentView != nil, @"invalid parent view");
  340. NSHashTable *list = nil;
  341. @synchronized(self.extensionMap) {
  342. list = [self.extensionMap objectForKey:extensionID];
  343. }
  344. if (list == nil || list.count == 0) {
  345. return NO;
  346. }
  347. BOOL isResponserExist = NO;
  348. for (id<TUIExtensionProtocol> observer in list) {
  349. if (observer && [observer respondsToSelector:@selector(onRaiseExtension:parentView:param:)]) {
  350. isResponserExist = [observer onRaiseExtension:extensionID parentView:parentView param:param];
  351. if (isResponserExist) {
  352. break;
  353. }
  354. }
  355. }
  356. return isResponserExist;
  357. }
  358. - (NSDictionary *)getExtensionInfo:(NSString *)extensionID param:(nullable NSDictionary *)param {
  359. NSAssert(extensionID.length > 0, @"invalid extension id");
  360. NSHashTable *list = nil;
  361. @synchronized(self.extensionMap) {
  362. list = [self.extensionMap objectForKey:extensionID];
  363. }
  364. if (list == nil || list.count == 0) {
  365. return nil;
  366. }
  367. for (id<TUIExtensionProtocol> observer in list) {
  368. if (observer && [observer respondsToSelector:@selector(onGetExtensionInfo:param:)]) {
  369. NSDictionary *info = [observer onGetExtensionInfo:extensionID param:param];
  370. if (info) {
  371. return info;
  372. }
  373. }
  374. }
  375. return nil;
  376. }
  377. - (NSMutableDictionary<NSString *, NSHashTable<id<TUIExtensionProtocol>> *> *)extensionMap {
  378. if (_extensionMap == nil) {
  379. _extensionMap = [NSMutableDictionary dictionary];
  380. }
  381. return _extensionMap;
  382. }
  383. @end
  384. #pragma mark - TUIObjectFactory
  385. @interface TUIObjectFactoryManager ()
  386. @property(nonatomic, strong) NSMapTable<NSString *, id<TUIObjectProtocol>> *objectFactoryMap;
  387. @end
  388. @implementation TUIObjectFactoryManager : NSObject
  389. + (instancetype)shareInstance {
  390. static id instance = nil;
  391. if (instance == nil) {
  392. static dispatch_once_t onceToken;
  393. dispatch_once(&onceToken, ^{
  394. instance = [[self alloc] init];
  395. });
  396. }
  397. return instance;
  398. }
  399. - (void)registerObjectFactory:(NSString *)factoryName objectFactory:(id<TUIObjectProtocol>)objectFactory {
  400. NSAssert(factoryName.length > 0, @"invalid factory name");
  401. NSAssert(objectFactory != nil, @"invalid object factory");
  402. if (factoryName && objectFactory) {
  403. @synchronized(self.objectFactoryMap) {
  404. [self.objectFactoryMap setObject:objectFactory forKey:factoryName];
  405. }
  406. }
  407. }
  408. - (void)unRegisterObjectFactory:(NSString *)factoryName {
  409. NSAssert(factoryName.length > 0, @"invalid factory name");
  410. if (factoryName) {
  411. @synchronized(self.objectFactoryMap) {
  412. [self.objectFactoryMap removeObjectForKey:factoryName];
  413. }
  414. }
  415. }
  416. - (nullable id)createObject:(NSString *)factoryName method:(NSString *)method param:(nullable NSDictionary *)param {
  417. NSAssert(factoryName.length > 0, @"invalid factory name");
  418. NSAssert(method.length > 0, @"invalid method");
  419. id<TUIObjectProtocol> factory = nil;
  420. @synchronized(self.objectFactoryMap) {
  421. factory = [self.objectFactoryMap objectForKey:factoryName];
  422. }
  423. if (factory && [factory respondsToSelector:@selector(onCreateObject:param:)]) {
  424. return [factory onCreateObject:method param:param];
  425. }
  426. return nil;
  427. }
  428. - (nullable id)createObject:(NSString *)method param:(NSDictionary *)param {
  429. NSAssert(method.length > 0, @"invalid method");
  430. NSArray<id<TUIObjectProtocol>> *list = nil;
  431. @synchronized(self.objectFactoryMap) {
  432. list = self.objectFactoryMap.objectEnumerator.allObjects;
  433. }
  434. if (list == nil || list.count == 0) {
  435. return nil;
  436. }
  437. for (id<TUIObjectProtocol> factory in list) {
  438. if (factory && [factory respondsToSelector:@selector(onCreateObject:param:)]) {
  439. id obj = [factory onCreateObject:method param:param];
  440. if (obj) {
  441. return obj;
  442. }
  443. }
  444. }
  445. return nil;
  446. }
  447. - (NSMapTable<NSString *, id<TUIObjectProtocol>> *)objectFactoryMap {
  448. if (_objectFactoryMap == nil) {
  449. _objectFactoryMap = [NSMapTable strongToWeakObjectsMapTable];
  450. }
  451. return _objectFactoryMap;
  452. }
  453. @end