// // Typography+StyleProvider.swift // VDS // // Created by Matt Bruce on 9/25/24. // import Foundation import UIKit import VDSCoreTokens extension TextStyle { // Class responsible for providing the correct TextStyles based on language public class Provider { // Base styles for English (default) private static let baseStyles: [Style: TextStyle] = [ .boldFeatureXLarge: TextStyle(rawValue: Style.boldFeatureXLarge.rawValue, fontFace: .edsBold, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature144 : VDSTypography.fontSizeFeature96, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature136 : VDSTypography.lineHeightFeature88, edgeInsets: .bottom(UIDevice.isIPad ? -6: -4)), .featureXLarge: TextStyle(rawValue: Style.featureXLarge.rawValue, fontFace: .dsLight, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature144 : VDSTypography.fontSizeFeature96, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature136 : VDSTypography.lineHeightFeature88, letterSpacing: VDSTypography.letterSpacingSemiwide, edgeInsets: .bottom(UIDevice.isIPad ? -6: -4)), .boldFeatureLarge: TextStyle(rawValue: Style.boldFeatureLarge.rawValue, fontFace: .edsBold, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature128 : VDSTypography.fontSizeFeature80, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature120 : VDSTypography.lineHeightFeature76, edgeInsets: .bottom(UIDevice.isIPad ? -6: -2)), .featureLarge: TextStyle(rawValue: Style.featureLarge.rawValue, fontFace: .dsLight, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature128 : VDSTypography.fontSizeFeature80, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature120 : VDSTypography.lineHeightFeature76, letterSpacing: VDSTypography.letterSpacingSemiwide, edgeInsets: .bottom(UIDevice.isIPad ? -6: -2)), .boldFeatureMedium: TextStyle(rawValue: Style.boldFeatureMedium.rawValue, fontFace: .edsBold, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature96 : VDSTypography.fontSizeFeature64, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature88 : VDSTypography.lineHeightFeature64, edgeInsets: .bottom(UIDevice.isIPad ? -4: -2)), .featureMedium: TextStyle(rawValue: Style.featureMedium.rawValue, fontFace: .dsLight, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature96 : VDSTypography.fontSizeFeature64, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature88 : VDSTypography.lineHeightFeature64, letterSpacing: VDSTypography.letterSpacingSemiwide, edgeInsets: .bottom(UIDevice.isIPad ? -4: -2)), .boldFeatureSmall: TextStyle(rawValue: Style.boldFeatureSmall.rawValue, fontFace: .edsBold, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature80 : VDSTypography.fontSizeFeature48, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature76 : VDSTypography.lineHeightFeature48, edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)), .featureSmall: TextStyle(rawValue: Style.featureSmall.rawValue, fontFace: .dsLight, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature80 : VDSTypography.fontSizeFeature48, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature76 : VDSTypography.lineHeightFeature48, letterSpacing: VDSTypography.letterSpacingSemiwide, edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)), .boldFeatureXSmall: TextStyle(rawValue: Style.boldFeatureXSmall.rawValue, fontFace: .edsBold, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature64 : VDSTypography.fontSizeFeature40, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature64 : VDSTypography.lineHeightFeature40, edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)), .featureXSmall: TextStyle(rawValue: Style.featureXSmall.rawValue, fontFace: .dsLight, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeFeature64 : VDSTypography.fontSizeFeature40, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightFeature64 : VDSTypography.lineHeightFeature40, letterSpacing: VDSTypography.letterSpacingSemiwide, edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)), .boldTitle2XLarge: TextStyle(rawValue: Style.boldTitle2XLarge.rawValue, fontFace: .edsBold, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle64 : VDSTypography.fontSizeTitle40, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle64 : VDSTypography.lineHeightTitle40, edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)), .title2XLarge: TextStyle(rawValue: Style.title2XLarge.rawValue, fontFace: .dsLight, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle64 : VDSTypography.fontSizeTitle40, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle64 : VDSTypography.lineHeightTitle40, letterSpacing: VDSTypography.letterSpacingSemiwide, edgeInsets: .bottom(UIDevice.isIPad ? -2: 0)), .boldTitleXLarge: TextStyle(rawValue: Style.boldTitleXLarge.rawValue, fontFace: .edsBold, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle48 : VDSTypography.fontSizeTitle32, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle48 : VDSTypography.lineHeightTitle36), .titleXLarge: TextStyle(rawValue: Style.titleXLarge.rawValue, fontFace: .dsLight, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle48 : VDSTypography.fontSizeTitle32, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle48 : VDSTypography.lineHeightTitle36, letterSpacing: VDSTypography.letterSpacingSemiwide), .boldTitleLarge: TextStyle(rawValue: Style.boldTitleLarge.rawValue, fontFace: .edsBold, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle32 : VDSTypography.fontSizeTitle24, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle36 : VDSTypography.lineHeightTitle28), .titleLarge: TextStyle(rawValue: Style.titleLarge.rawValue, fontFace: UIDevice.isIPad ? .dsLight : .edsRegular, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle32 : VDSTypography.fontSizeTitle24, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle36 : VDSTypography.lineHeightTitle28, letterSpacing: UIDevice.isIPad ? VDSTypography.letterSpacingSemiwide : 0), .boldTitleMedium: TextStyle(rawValue: Style.boldTitleMedium.rawValue, fontFace: .edsBold, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle24 : VDSTypography.fontSizeTitle20, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle28 : VDSTypography.lineHeightTitle24), .titleMedium: TextStyle(rawValue: Style.titleMedium.rawValue, fontFace: .edsRegular, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle24 : VDSTypography.fontSizeTitle20, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle28 : VDSTypography.lineHeightTitle24), .boldTitleSmall: TextStyle(rawValue: Style.boldTitleSmall.rawValue, fontFace: .edsBold, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle20 : VDSTypography.fontSizeTitle16, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle24 : VDSTypography.lineHeightTitle20), .titleSmall: TextStyle(rawValue: Style.titleSmall.rawValue, fontFace: .edsRegular, pointSize: UIDevice.isIPad ? VDSTypography.fontSizeTitle20 : VDSTypography.fontSizeTitle16, lineHeight: UIDevice.isIPad ? VDSTypography.lineHeightTitle24 : VDSTypography.lineHeightTitle20), .boldBodyLarge: TextStyle(rawValue: Style.boldBodyLarge.rawValue, fontFace: .edsBold, pointSize: VDSTypography.fontSizeBody16, lineHeight: VDSTypography.lineHeightBody20, letterSpacing: VDSTypography.letterSpacingWide), .bodyLarge: TextStyle(rawValue: Style.bodyLarge.rawValue, fontFace: .edsRegular, pointSize: VDSTypography.fontSizeBody16, lineHeight: VDSTypography.lineHeightBody20, letterSpacing:VDSTypography.letterSpacingWide), .boldBodyMedium: TextStyle(rawValue: Style.boldBodyMedium.rawValue, fontFace: .edsBold, pointSize: VDSTypography.fontSizeBody14, lineHeight: VDSTypography.lineHeightBody18, letterSpacing: VDSTypography.letterSpacingWide), .bodyMedium: TextStyle(rawValue: Style.bodyMedium.rawValue, fontFace: .edsRegular, pointSize: VDSTypography.fontSizeBody14, lineHeight: VDSTypography.lineHeightBody18, letterSpacing: VDSTypography.letterSpacingWide), .boldBodySmall: TextStyle(rawValue: Style.boldBodySmall.rawValue, fontFace: .etxBold, pointSize: VDSTypography.fontSizeBody12, lineHeight: VDSTypography.lineHeightBody16), .bodySmall: TextStyle(rawValue: Style.bodySmall.rawValue, fontFace: .etxRegular, pointSize: VDSTypography.fontSizeBody12, lineHeight: VDSTypography.lineHeightBody16), .boldMicro: TextStyle(rawValue: Style.boldMicro.rawValue, fontFace: .etxBold, pointSize: VDSTypography.fontSizeMicro11, lineHeight: VDSTypography.lineHeightMicro16), .micro: TextStyle(rawValue: Style.micro.rawValue, fontFace: .etxRegular, pointSize: VDSTypography.fontSizeMicro11, lineHeight: VDSTypography.lineHeightMicro16) ] // Spanish lineHeight overrides private static let spanishLineHeightOverrides: [Style: CGFloat] = [ .boldFeatureXLarge: UIDevice.isIPad ? 156 : 104, .featureXLarge: UIDevice.isIPad ? 156 : 104, .boldFeatureLarge: UIDevice.isIPad ? 140 : 88, .featureLarge: UIDevice.isIPad ? 140 : 88, .boldFeatureMedium: UIDevice.isIPad ? 104 : 72, .featureMedium: UIDevice.isIPad ? 104 : 72, .boldFeatureSmall: UIDevice.isIPad ? 88 : 56, .featureSmall: UIDevice.isIPad ? 88 : 56, .boldFeatureXSmall: UIDevice.isIPad ? 72 : 48, .featureXSmall: UIDevice.isIPad ? 72 : 48, .boldTitle2XLarge: UIDevice.isIPad ? 72 : 48, .title2XLarge: UIDevice.isIPad ? 72 : 48, .boldTitleXLarge: UIDevice.isIPad ? 56 : 36, .titleXLarge: UIDevice.isIPad ? 56 : 36 ] // Cache for the current styles based on the current language private static var currentStyles: [Style: TextStyle] = [:] // Function to get the style with conditional lineHeight adjustment static func style(for key: Style) -> TextStyle { DispatchQueue.once(block: { TextStyle.Provider.initialize() }) guard let style = currentStyles[key] else { fatalError("TextStyle for \(key.rawValue) is not defined.") } return style } // Update current styles only once when language changes static func updateCurrentStyles() { // Start with the base styles currentStyles = baseStyles // If the language is Spanish, apply the lineHeight overrides if LanguageManager.currentLanguage == .spanish { for (styleKey, spanishLineHeight) in spanishLineHeightOverrides { if var style = currentStyles[styleKey] { style = TextStyle( rawValue: style.rawValue, fontFace: style.fontFace, pointSize: style.pointSize, lineHeight: spanishLineHeight, // Apply the Spanish lineHeight letterSpacing: style.letterSpacing, edgeInsets: style.edgeInsets ) currentStyles[styleKey] = style } } } } // Initial setup to populate the current styles based on the initial language static func initialize() { updateCurrentStyles() Style.allCases.forEach { style in let found = Provider.currentStyles[style] assert(found != nil, "\(style.rawValue) has not been set in the TextStyleProvider.baseStyles") } } } }