import UIKit import SwiftUI extension Font { public init(uiFont: UIFont) { self.init(uiFont as CTFont) } public func toUIFont() -> UIFont? { var font: UIFont? inspect(self) { label, value in guard label == "provider" else { return } inspect(value) { label, value in guard label == "base" else { return } guard let provider = SwiftUIFontProvider(from: value) else { return assertionFailure("Could not create font provider") } font = provider.uiFont() } } return font } } private enum SwiftUIFontProvider { case system(size: CGFloat, weight: Font.Weight?, design: Font.Design?) case textStyle(Font.TextStyle, weight: Font.Weight?, design: Font.Design?) case platform(CTFont) func uiFont() -> UIFont? { switch self { case let .system(size, weight, _): return weight?.toUIFontWeight() .map { .systemFont(ofSize: size, weight: $0) } ?? .systemFont(ofSize: size) case let .textStyle(textStyle, _, _): return textStyle.toUIFontTextStyle() .map(UIFont.preferredFont(forTextStyle:)) case let .platform(font): return font as UIFont } } init?(from reflection: Any) { switch String(describing: type(of: reflection)) { case "SystemProvider": var props: ( size: CGFloat?, weight: Font.Weight?, design: Font.Design? ) = (nil, nil, nil) inspect(reflection) { label, value in switch label { case "size": props.size = value as? CGFloat case "weight": props.weight = value as? Font.Weight case "design": props.design = value as? Font.Design default: return } } guard let size = props.size else { return nil } self = .system( size: size, weight: props.weight, design: props.design ) case "TextStyleProvider": var props: ( style: Font.TextStyle?, weight: Font.Weight?, design: Font.Design? ) = (nil, nil, nil) inspect(reflection) { label, value in switch label { case "style": props.style = value as? Font.TextStyle case "weight": props.weight = value as? Font.Weight case "design": props.design = value as? Font.Design default: return } } guard let style = props.style else { return nil } self = .textStyle( style, weight: props.weight, design: props.design ) case "PlatformFontProvider": var font: CTFont? inspect(reflection) { label, value in guard label == "font" else { return } font = (value as? CTFont?)?.flatMap { $0 } } guard let font else { return nil } self = .platform(font) default: return nil } } } extension Font.TextStyle { fileprivate func toUIFontTextStyle() -> UIFont.TextStyle? { switch self { case .largeTitle: return .largeTitle case .title: return .title1 case .headline: return .headline case .subheadline: return .subheadline case .body: return .body case .callout: return .callout case .footnote: return .footnote case .caption: return .caption1 default: switch self { case .title2: return .title2 case .title3: return .title3 case .caption2: return .caption2 default: assertionFailure() return .body } } } } extension SwiftUI.Font.Weight { fileprivate func toUIFontWeight() -> UIFont.Weight? { var rawValue: CGFloat? = nil inspect(self) { label, value in guard label == "value" else { return } rawValue = value as? CGFloat } guard let rawValue else { return nil } return .init(rawValue) } } private func inspect(_ object: Any, with action: (Mirror.Child) -> Void) { Mirror(reflecting: object).children.forEach(action) }