import ExpoModulesCore /** A registry of font family aliases mapped to their real font family names. */ private var fontFamilyAliases = [String: String]() /** A flag that is set to `true` when the ``UIFont.fontNames(forFamilyName:)`` is already swizzled. */ private var hasSwizzled = false /** Queue to protect shared resources */ private let queue = DispatchQueue(label: "expo.fontfamilyaliasmanager", attributes: .concurrent) /** Manages the font family aliases and swizzles the `UIFont` class. */ internal struct FontFamilyAliasManager { /** Whether the given alias has already been set. */ internal static func hasAlias(_ familyNameAlias: String) -> Bool { return queue.sync { fontFamilyAliases[familyNameAlias] != nil } } /** Sets the alias for the given family name. If the alias has already been set, its family name will be overriden. */ internal static func setAlias(_ familyNameAlias: String, forFont font: String) { maybeSwizzleUIFont() queue.sync(flags: .barrier) { fontFamilyAliases[familyNameAlias] = font } } /** Returns the family name for the given alias or `nil` when it's not set yet. */ internal static func familyName(forAlias familyNameAlias: String) -> String? { return queue.sync { fontFamilyAliases[familyNameAlias] } } } /** Swizzles ``UIFont.fontNames(forFamilyName:)`` to support font family aliases. This is necessary because the user provides a custom family name that is then used in stylesheets, however the font usually has a different name encoded in the binary, thus the system may use a different name. */ private func maybeSwizzleUIFont() { if hasSwizzled { return } #if !os(macOS) let originalFontNamesMethod = class_getClassMethod(UIFont.self, #selector(UIFont.fontNames(forFamilyName:))) let newFontNamesMethod = class_getClassMethod(UIFont.self, #selector(UIFont._expo_fontNames(forFamilyName:))) if let originalFontNamesMethod, let newFontNamesMethod { method_exchangeImplementations(originalFontNamesMethod, newFontNamesMethod) } else { log.error("expo-font is unable to swizzle `UIFont.fontNames(forFamilyName:)`") } #endif hasSwizzled = true }