// Copyright 2018-present 650 Industries. All rights reserved. #import #import #import #import #import #import static dispatch_once_t initializeCurrentFontProcessorsOnce; static NSPointerArray *currentFontProcessors; @implementation RCTFont (UMReactFontManager) + (UIFont *)UMUpdateFont:(UIFont *)uiFont withFamily:(NSString *)family size:(NSNumber *)size weight:(NSString *)weight style:(NSString *)style variant:(NSArray *)variant scaleMultiplier:(CGFloat)scaleMultiplier { UIFont *font; for (id fontProcessor in currentFontProcessors) { font = [fontProcessor updateFont:uiFont withFamily:family size:size weight:weight style:style variant:variant scaleMultiplier:scaleMultiplier]; if (font) { return font; } } return [self UMUpdateFont:uiFont withFamily:family size:size weight:weight style:style variant:variant scaleMultiplier:scaleMultiplier]; } @end /** * This class is responsible for allowing other modules to register as font processors in React Native. * * A font processor is an object conforming to UMFontProcessorInterface and is capable of * providing an instance of UIFont for given (family, size, weight, style, variant, scaleMultiplier). * * To be able to hook into React Native's way of processing fonts we: * - add a new class method to RCTFont, `UMUpdateFont:withFamily:size:weight:style:variant:scaleMultiplier` * with UMReactFontManager category. * - add a new static variable `currentFontProcessors` holding an array of... font processors. This variable * is shared between the RCTFont's category and UMReactFontManager class. * - when UMReactFontManager is initialized, we exchange implementations of RCTFont.updateFont... * and RCTFont.UMUpdateFont... After the class initialized, which happens only once, calling `RCTFont updateFont` * calls in fact implementation we've defined up here and calling `RCTFont UMUpdateFont` falls back * to the default implementation. (This is why we call `[self UMUpdateFont]` at the end of that function, * though it seems like an endless loop, in fact we dispatch to another implementation.) * - When some module adds a font processor using UMFontManagerInterface, UMReactFontManager adds a weak pointer to it * to currentFontProcessors array. * - Implementation logic of `RCTFont.UMUpdateFont` uses current value of currentFontProcessors when processing arguments. */ @interface UMReactFontManager () @property (nonatomic, strong) NSMutableSet *fontProcessors; @end @implementation UMReactFontManager UM_REGISTER_MODULE(); - (instancetype)init { if (self = [super init]) { _fontProcessors = [NSMutableSet set]; } return self; } + (const NSArray *)exportedInterfaces { return @[@protocol(EXFontManagerInterface)]; } + (void)initialize { dispatch_once(&initializeCurrentFontProcessorsOnce, ^{ currentFontProcessors = [NSPointerArray weakObjectsPointerArray]; }); Class rtcClass = [RCTFont class]; SEL rtcUpdate = @selector(updateFont:withFamily:size:weight:style:variant:scaleMultiplier:); SEL exUpdate = @selector(UMUpdateFont:withFamily:size:weight:style:variant:scaleMultiplier:); method_exchangeImplementations(class_getClassMethod(rtcClass, rtcUpdate), class_getClassMethod(rtcClass, exUpdate)); } # pragma mark - UMFontManager - (void)addFontProcessor:(id)processor { [_fontProcessors addObject:processor]; [currentFontProcessors compact]; [currentFontProcessors addPointer:(__bridge void * _Nullable)(processor)]; } @end