Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fueled typography #1

Open
wants to merge 3 commits into
base: fueled
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,14 @@ ios:
typography:
# [optional] Absolute or relative path to swift file where to export UIKit fonts (UIFont extension).
fontSwift: "./Source/UIComponents/UIFont+extension.swift"
# [optional] Absolute or relative path to swift file where to generate LabelStyle extensions for each style (LabelStyle extension).
labelStyleSwift: "./Source/UIComponents/LabelStyle+extension.swift"
# [optional] Absolute or relative path to swift file where to generate TextStyle extensions for each style (TextStyle extension).
textStyleSwift: "./Source/UIComponents/TextStyle+extension.swift"
# [optional] Absolute or relative path to swift file where to export SwiftUI fonts (Font extension).
swiftUIFontSwift: "./Source/View/Common/Font+extension.swift"
# Should FigmaExport generate UILabel for each text style (font)? E.g. HeaderLabel, BodyLabel, CaptionLabel
generateLabels: true
# Relative or absolute path to directory where to place UILabel for each text style (font) (Requred if generateLabels = true)
labelsDirectory: "./Source/UIComponents/"
# Should FigmaExport generate TextStyles for each text style (font)? E.g. Header, Body, Caption
generateTextStyles: true
# Relative or absolute path to directory where to place TextStyles for each text style (font) (Required if generateTextStyles = true)
textStylesDirectory: "./Source/UIComponents/"
Comment on lines +152 to +159
Copy link

@ada10086 ada10086 Nov 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be in addition to the original labelStyleSwift, generateLabels, and labelsDirectory properties instead of replacing them? Since we are still supporting generating the Label, LabelStyle, LabelStyle+extension files for UIKit

# Typography name style: camelCase or snake_case
nameStyle: camelCase

Expand Down
8 changes: 4 additions & 4 deletions Examples/Example/Pods/FigmaExport/Release/figma-export.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// swiftlint:disable all
//
// The code generated using FigmaExport — Command line utility to export
// colors, typography, icons and images from Figma to Xcode project.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// swiftlint:disable all
//
// The code generated using FigmaExport — Command line utility to export
// colors, typography, icons and images from Figma to Xcode project.
Expand All @@ -10,43 +11,31 @@
import UIKit

public extension UIFont {

static func body() -> UIFont {
customFont("PTSans-Regular", size: 16.0, textStyle: .body, scaled: true)
}

static func caption() -> UIFont {
customFont("PTSans-Regular", size: 14.0, textStyle: .footnote, scaled: true)
}

static func header() -> UIFont {
customFont("PTSans-Bold", size: 20.0)
}

static func largeTitle() -> UIFont {
customFont("PTSans-Bold", size: 34.0, textStyle: .largeTitle, scaled: true)
}

static func uppercased() -> UIFont {
customFont("PTSans-Regular", size: 14.0)
}
static let body: UIFont = customFont("PTSans-Regular", size: 16.0, textStyle: .body)
static let caption: UIFont = customFont("PTSans-Regular", size: 14.0, textStyle: .footnote)
static let header: UIFont = customFont("PTSans-Bold", size: 20.0)
static let largeTitle: UIFont = customFont("PTSans-Bold", size: 34.0, textStyle: .largeTitle)
static let uppercased: UIFont = customFont("PTSans-Regular", size: 14.0)

private static func customFont(
_ name: String,
size: CGFloat,
textStyle: UIFont.TextStyle? = nil,
scaled: Bool = false) -> UIFont {

textStyle: UIFont.TextStyle? = nil
) -> UIFont {
guard let font = UIFont(name: name, size: size) else {
print("Warning: Font \(name) not found.")
return UIFont.systemFont(ofSize: size, weight: .regular)
}
if scaled, let textStyle = textStyle {

if let textStyle {
let metrics = UIFontMetrics(forTextStyle: textStyle)
return metrics.scaledFont(for: font)
} else {
return font
}

return font
}

internal func lineSpacing(lineHeight customLineHeight: CGFloat) -> CGFloat {
max(0, customLineHeight - self.lineHeight)
}
}
12 changes: 7 additions & 5 deletions Examples/Example/figma-export.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,15 @@ ios:

# Parameters for exporting typography
typography:
# Path to directory where to place UIFont+extension.swift file
# Choose what font system you want to use SwiftUI or UIKit
fontSystem: UIKit
# Path to directory where to place UIFont+extension.swift file required for both SwiftUI and UIKit
Comment on lines +64 to +66

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we include this in CONFIG.md as well?

fontSwift: "./UIComponents/Source/Typography/UIFont+extension.swift"
# [optional] Absolute or relative path to swift file where to generate LabelStyle extensions for each style (LabelStyle extension).
labelStyleSwift: "./UIComponents/Source/Typography/LabelStyle+extension.swift"
# Will FigmaExport generate UILabel for each text style (font) e.g. HeaderLabel, BodyLabel, CaptionLabel.
generateLabels: true
# Path to directory where to place UILabel for each text style (font) (Required if generateLabels = true)
labelsDirectory: "./UIComponents/Source/Typography"
# Should FigmaExport generate TextStyles or Labels (based on fontSystem) for each text style (font)? E.g. Header, Body, Caption
generateStyles: true
# Relative or absolute path to directory where to place TextStyles or Labels (based on fontSystem) for each text style (font) (Required if generateStyles = true)
stylesDirectory: "./UIComponents/Source/Typography"
Comment on lines +70 to +73

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# Typography name style: camelCase or snake_case
nameStyle: camelCase

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Examples/ExampleSwiftUI/figma-export.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ ios:
fontSwift: "./ExampleSwiftUI/View/Common/UIFont+extension.swift"
# [optional] Absolute or relative path to swift file where to export SwiftUI fonts (Font extension).
swiftUIFontSwift: "./ExampleSwiftUI/View/Common/Font+extension.swift"
# Will FigmaExport generate UILabel for each text style (font) e.g. HeaderLabel, BodyLabel, CaptionLabel.
generateLabels: false
# Should FigmaExport generate TextStyles for each text style (font)? E.g. Header, Body, Caption
generateTextStyles: true
Comment on lines +68 to +69

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

# Typography name style: camelCase or snake_case
nameStyle: camelCase
21 changes: 11 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,15 @@ If name of an image contains idiom at the end (e.g. ~ipad), it will be exported
#### Typography

When your execute `figma-export typography` command `figma-export` generates 3 files:
1. `UIFont+extension.swift` extension for UIFont that declares your custom fonts. Use these fonts like this `UIFont.header()`, `UIFont.caption1()`.
2. `LabelStyle.swift` struct for generating attributes for NSAttributesString with custom lineHeight and tracking (letter spacing).
3. `Label.swift` file that contains base Label class and class for each text style. E.g. HeaderLabel, BodyLabel, Caption1Label. Specify these classes in xib files on in code.
1. `TextStyle.swift` struct for generating TextStyles for SwiftUI with custom line spacing, kerning (letter spacing) and text case.
2. `TextStyle+extension.swift` extension including all the custom TextStyles from the Figma file.
3. `Font+extension` extension for Font that declares your custom fonts.
4. `UIFont+extension.swift` extension for UIFont that declares your custom fonts. Mainly used to get the default font lineHeight.

Example of these files:
- [./Examples/Example/UIComponents/Source/Typography/Label.swift](./Examples/Example/UIComponents/Source/Typography/Label.swift)
- [./Examples/Example/UIComponents/Source/Typography/LabelStyle.swift](./Examples/Example/UIComponents/Source/Typography/LabelStyle.swift)
- [./Examples/Example/UIComponents/Source/Typography/TextStyle.swift](./Examples/Example/UIComponents/Source/Typography/TextStyle.swift)
- [./Examples/Example/UIComponents/Source/Typography/TextStyle+extension.swift](./Examples/Example/UIComponents/Source/Typography/TextStyle+extension.swift)
- [./Examples/Example/UIComponents/Source/Typography/Font+extension.swift](./Examples/Example/UIComponents/Source/Typography/Font+extension.swift)
- [./Examples/Example/UIComponents/Source/Typography/UIFont+extension.swift](./Examples/Example/UIComponents/Source/Typography/UIFont+extension.swift)

### Android
Expand Down Expand Up @@ -430,11 +432,10 @@ Custom Stencil templates for colors and images must have the following names:
- Image+extension.swift.stencil for SwiftUI images

Custom Stencil templates for typography must have the following names:
- Label.swift.stencil,
- LabelStyle.swift.stencil,
- LabelStyle+extension.swift.stencil,
- TextStyle.swift.stencil,
- TextStyle+extension.swift.stencil,
- Font+extension.swift.stencil
- UIFont+extension.swift.stencil
Comment on lines +435 to 438

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same question - are we removing support for Label and LabelStyle?

- Font+extension.swift.stencil.stencil

##### Android

Expand All @@ -453,7 +454,7 @@ Custom Stencil templates must have the following names:
#### iOS
1. Add a custom font to the Xcode project. Drag & drop font file to the Xcode project, set target membership, and add font file name in the Info.plist file. [See developer documentation for more info.](https://developer.apple.com/documentation/uikit/text_display_and_fonts/adding_a_custom_font_to_your_app)<br><img src="images/fonts.png" width="400" />
2. Run `figma-export typography` to export text styles
3. Use generated fonts and labels in your code. E.g. `button.titleLabel?.font = UIFont.body()`, `let label = HeaderLabel()`.
3. Use generated fonts and text styles in your code. E.g. `Text("Header").textStyle(.header)`.

#### Android
1. Place font file under the `res` directory of your module
Expand Down
9 changes: 6 additions & 3 deletions Sources/FigmaExport/Input/Params.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import FigmaExportCore
import XcodeExport

extension NameStyle: Decodable {}

Expand Down Expand Up @@ -92,11 +93,13 @@ struct Params: Decodable {
}

struct Typography: Decodable {
let fontSystem: FontSystem?
let fontSwift: URL?
let labelStyleSwift: URL?
let textStyleSwift: URL?
let labelStyleSwift: URL?
let swiftUIFontSwift: URL?
let generateLabels: Bool
let labelsDirectory: URL?
let generateStyles: Bool
let stylesDirectory: URL?
let nameStyle: NameStyle
}
Comment on lines 95 to 104

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation is not consistent here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh good one, its difficult on the project since the default is spaces, so I need to change this for every file I'm working on.


Expand Down
12 changes: 6 additions & 6 deletions Sources/FigmaExport/Resources/iOSConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,14 @@ ios:
typography:
# [optional] Absolute or relative path to swift file where to export UIKit fonts (UIFont extension).
fontSwift: "./Source/UIComponents/UIFont+extension.swift"
# [optional] Absolute or relative path to swift file where to generate LabelStyle extensions for each style (LabelStyle extension).
labelStyleSwift: "./Source/UIComponents/LabelStyle+extension.swift"
# [optional] Absolute or relative path to swift file where to generate TextStyle extensions for each style (TextStyle extension).
textStyleSwift: "./Source/UIComponents/TextStyle+extension.swift"
# [optional] Absolute or relative path to swift file where to export SwiftUI fonts (Font extension).
swiftUIFontSwift: "./Source/View/Common/Font+extension.swift"
# Should FigmaExport generate UILabel for each text style (font)? E.g. HeaderLabel, BodyLabel, CaptionLabel
generateLabels: true
# Relative or absolute path to directory where to place UILabel for each text style (font) (Requred if generateLabels = true)
labelsDirectory: "./Source/UIComponents/"
# Should FigmaExport generate TextStyles for each text style (font)? E.g. Header, Body, Caption
generateTextStyles: true
# Relative or absolute path to directory where to place TextStyles for each text style (font) (Required if generateTextStyles = true)
textStylesDirectory: "./Source/UIComponents/"
# Typography name style: camelCase or snake_case
nameStyle: camelCase
"""#
14 changes: 8 additions & 6 deletions Sources/FigmaExport/Subcommands/ExportTypography.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ extension FigmaExportCommand {
nameStyle: typographyParams.nameStyle
)
let processedTextStyles = try processor.process(assets: textStyles).get()
logger.info("Saving text styles...")
let fontSystemString = typographyParams.fontSystem.map { " for \($0.rawValue)"} ?? ""
logger.info("Saving text styles\(fontSystemString)...")
try exportXcodeTextStyles(textStyles: processedTextStyles, iosParams: ios)
logger.info("Done!")
}
Expand All @@ -62,17 +63,18 @@ extension FigmaExportCommand {
fontExtensionURL: iosParams.typography?.fontSwift,
swiftUIFontExtensionURL: iosParams.typography?.swiftUIFontSwift
)
let labelUrls = XcodeTypographyOutput.LabelURLs(
labelsDirectory: iosParams.typography?.labelsDirectory,
labelStyleExtensionsURL: iosParams.typography?.labelStyleSwift
let styleUrls = XcodeTypographyOutput.StyleURLs(
directory: iosParams.typography?.stylesDirectory,
extensionsURL: iosParams.typography?.textStyleSwift
)
let urls = XcodeTypographyOutput.URLs(
fonts: fontUrls,
labels: labelUrls
styles: styleUrls
)
return XcodeTypographyOutput(
fontSystem: iosParams.typography?.fontSystem,
urls: urls,
generateLabels: iosParams.typography?.generateLabels,
generateStyles: iosParams.typography?.generateStyles,
addObjcAttribute: iosParams.addObjcAttribute,
templatesPath: iosParams.templatesPath
)
Expand Down
65 changes: 50 additions & 15 deletions Sources/XcodeExport/Model/XcodeTypographyOutput.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,41 @@
import Foundation

public enum FontSystem: String, Decodable {
case uiKit = "UIKit"
case swiftUI = "SwiftUI"

var styleExtensionStencilFile: String {
switch self {
case .swiftUI:
return "TextStyle+extension.swift.stencil"
case .uiKit:
return "LabelStyle+extension.swift.stencil"
}
}

var styleStencilFile: String {
switch self {
case .swiftUI:
return "TextStyle.swift.stencil"
case .uiKit:
return "LabelStyle.swift.stencil"
}
}

var styleFile: String {
switch self {
case .swiftUI:
return "TextStyle.swift"
case .uiKit:
return "LabelStyle.swift"
}
}
}

public struct XcodeTypographyOutput {
let fontSystem: FontSystem
let urls: URLs
let generateLabels: Bool
let generateStyles: Bool
let addObjcAttribute: Bool
let templatesPath: URL?

Expand All @@ -18,40 +51,42 @@ public struct XcodeTypographyOutput {
}
}

public struct LabelURLs {
let labelsDirectory: URL?
let labelStyleExtensionsURL: URL?
public struct StyleURLs {
let directory: URL?
let extensionsURL: URL?

public init(
labelsDirectory: URL? = nil,
labelStyleExtensionsURL: URL? = nil
directory: URL? = nil,
extensionsURL: URL? = nil
) {
self.labelsDirectory = labelsDirectory
self.labelStyleExtensionsURL = labelStyleExtensionsURL
self.directory = directory
self.extensionsURL = extensionsURL
}
}

public struct URLs {
public let fonts: FontURLs
public let labels: LabelURLs
public let styles: StyleURLs

public init(
fonts: FontURLs,
labels: LabelURLs
styles: StyleURLs
) {
self.fonts = fonts
self.labels = labels
self.styles = styles
}
}

public init(
fontSystem: FontSystem? = .swiftUI,
urls: URLs,
generateLabels: Bool? = false,
generateStyles: Bool? = false,
addObjcAttribute: Bool? = false,
templatesPath: URL? = nil
) {
self.fontSystem = fontSystem ?? .swiftUI
self.urls = urls
self.generateLabels = generateLabels ?? false
self.generateStyles = generateStyles ?? false
self.addObjcAttribute = addObjcAttribute ?? false
self.templatesPath = templatesPath
}
Expand Down
15 changes: 3 additions & 12 deletions Sources/XcodeExport/Resources/Font+extension.swift.stencil
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
{% include "header.stencil" %}
import SwiftUI

public extension Font {
{% for textStyle in textStyles %}{% if textStyle.supportsDynamicType %}
static func {{ textStyle.name }}() -> Font {
if #available(iOS 14.0, *) {
return Font.custom("{{ textStyle.fontName }}", size: {{ textStyle.fontSize }}, relativeTo: .{{ textStyle.type }})
} else {
return Font.custom("{{ textStyle.fontName }}", size: {{ textStyle.fontSize }})
}
}{% else %}
static func {{ textStyle.name }}() -> Font {
Font.custom("{{ textStyle.fontName }}", size: {{ textStyle.fontSize }})
}{% endif %}{% endfor %}
public extension Font {{ '{' }}{% for textStyle in textStyles %}{% if textStyle.supportsDynamicType %}
static let {{ textStyle.name }} = Font.custom("{{ textStyle.fontName }}", size: {{ textStyle.fontSize }}, relativeTo: .{{ textStyle.type }}){% else %}
static let {{ textStyle.name }} = Font.custom("{{ textStyle.fontName }}", size: {{ textStyle.fontSize }}){% endif %}{% endfor %}
}
Loading