How To Improve Text Font Style Consistency in SwiftUI

Farhan Adji
7 min readDec 14, 2021

--

source: https://www.simpleswiftguide.com/swiftui-font-tutorial/
Source: https://www.simpleswiftguide.com/swiftui-font-tutorial/

In this tutorial, we will learn how to create a ViewModifier in SwiftUI to help improve font style consistency in SwiftUI.

Background

As we already know, creating views in SwiftUI is very easy and fast, a lot of components already provided in SwiftUI SDK, such as Text, Image, Button, Value selectors, etc. We’re not going to talk about all the components in the SDK, but we’re just talking about Text component and ways to improve style consistency.

When you work as a mobile developer in a company, one of your tasks should be create a page/component in the application according to the design/wireframe that has been created by the UI designer team. Usually, UI designers make designs/wireframes following design standards or what we call a design language system (DLS), what does design language system (DLS) mean?

Design Language System

It is a set of rules or guidelines that heightens the level of harmony in a digital ecosystem.

Source: https://www.figmacrush.com/core-design-system-template/

Simply, DLS is a framework that combines the set of reusable components, standards and documentation for consistency in products.

“Why are you talking about DLS? what exactly do you want to talk about in this article?”

Relax…… it relates to DLS, but okay… without further let’s jump to the code.

Let me ask you first, how do you change the font style and size for the Text component in SwiftUI?

Yes, in SwiftUI we can simply change the properties (e.g font style) of a control by calling methods that are known as modifier/view modifier. Let’s say you want to bold and bigger text, you can use the modified named fontWeight and font.

Text("Hello, world!")
.fontWeight(.bold)
.font(.title)

By calling those modifier, it actually return a new view that has the bolded and bigger text. In SwiftUI, you can further chain this new view with other modifiers. that’s how view modifier works in SwiftUI. wow magic

So far, so easy right?

But… some companies have their own typographic standards and most don’t use the standard fonts used by Apple or the standard fonts provided in SwiftUI.

“Okay, so what’s the solution? Just import fonts into project and use custom font style right?”

YES. Correct 💯

Source: Apple documentation

We just simply import fonts into project! But I won’t explain here how to import fonts, you can get them on the Apple documentation page here.

“I’ve imported the fonts into the project, so how do I use it?”

You could simply use the custom(_:size:) method to retrieve an instance of your font and apply it to a text view with the font(_:) modifier, match the name of the font with the font’s PostScript name. If SwiftUI can’t retrieve and apply your font, it renders the text view with the default system font instead.

The following example applies the font MyFont to a text view:

Text("Hello, world!")    
.font(Font.custom("MyFont", size: 18))

Okay so far it makes sense and pretty easy right?

“So where’s the problem?”

Hold on..

Let me ask you again, how do you make a text view according to the design?

Let’s say, your company uses tools to carry out the design process and to communicate between developers and designers, in that tools you can easily see the font type, size, color and other properties by selecting the text, then we just simply create a text view with properties as stated in the tools side-bar. EZ.

So the implementation in our code is something like this:

Text("dy 1")
.font(Font.custom("SourceSansPro", size: 16)
.foregroundColor(Color.black.opacity(0.87))

Okay.. so far it looks normal, and my team implement it like this for all views in our app. Everything looks good because we have already given what the UI designer wanted, Until…….

UI and UX designer make changes to the typography in our DLS.

Boom!

Wow, what a ̶s̶u̶r̶p̶r̶i̶s̶e̶! Our codebase is already big and has many views that implement text view with custom properties like the code above, it’s really confusing when we have to refactor our entire views.

So that this does not happen again, we are looking for a solution so that if there is a typography change in our DLS, then we don’t need to refactor entire views.

Solution

Usually, in the DLS documentation the properties of the view component are very clear and detailed, typography included. Normally, it comes with the font name, name of style, weight, size and other properties. As developers, we must be aware of this.

Source: https://www.courts.ca.gov/

if you don’t have an idea, then we can assume typography documentation in our DLS as in the picture above.

From here, we can create a custom view modifier to change the style of text view according to the style in our DLS.

what do we do first?

assuming we’ve imported fonts and usually different font weights are different files.

If we only have on font file then it is quite easy to start applying fonts to text view in our apps. We can just implement like this:

Text("Hello world")
.font(Font.custom("Lora", size: 16)
  1. Create custom font type modifier

But as I said above, usually different font weight different files. luckily in the example above we only use 2 types and usually there are only 2 files whose names are like this: “Lora-Regular”, “Lora-Bold”, but still, if you write the name of the font every time you want to use it, it is very annoying and the possibility of typos is very high. The solution to those problems, we can create a custom type font in SwiftUI by create an extension to the Font protocol. This will allow us to create a custom font type modifier.

extension Font {
public static func dlsFont(size: CGFloat, weight: Font.Weight = .regular) -> Font {
var fontName = "Lora-Regular"
if weight == .bold {
fontName = "Lora-Bold"
}
return Font.custom(fontName, size: size)
}
}

Let’s say, we wan’t to create text view with the style name H6 heading in our DLS, we can simply implement like this:

Text("Hello world")
.font(.dlsFont(size: 22, weight: .bold))

But this does not solve our problem…

Okay, next!

2. Create enum of typography style

enum TypographyStyle {
case small(color: Color = .black)
case body(color: Color = .black)
case heading6(color: Color = .black)
public var size: CGFloat {
switch self {
case .small: return 15
case .body: return 17
case .heading6: return 22
}
}
public var weight: Font.Weight {
switch self {
case .small, .body: return .regular
case .heading6: return .bold
}
}
}

make enum cases as many types of typography style in our DLS and also create variable properties in it. We also use the enum associated value to set what color in the text view, the reason is because the color of the text is quite dynamic, and we also provide a default color that is often used in our app.

I won’t make all of them, ̶b̶e̶c̶a̶u̶s̶e̶ ̶I̶’̶m̶ ̶t̶i̶r̶e̶d̶.̶

3. Create a base typography view modifier

struct BaseTypography: ViewModifier {
let type: TypographyStyle //our enum
let color: Color = .black
func body(content: Content) -> some View {
content
.font(.dlsFont(size: type.size, weight: type.weight))
.foregroundColor(color)
}
}

So from the view modifier that we made above, we want the text view to only need to send the style type from the typography without set what size and font style to use manually.

4. Create a final view modifier

extension View {
func typography(type: TypographyStyle) -> some View {
switch type {
case .small(let color), .body(let color), .heading6(let color):
return self.modifier(BaseTypography(type: type, color: color)
}
}
}

The final step is to create a final view modifier that will be used “directly” to modify font style in our text view component. In the code above, we use the switch case to get the colors stored as associated values, you can use another way to get the associated values from the enum case.

Example

VStack {
Text("Hello world")
.typography(type: .heading6())
Text("I ❤️ SwiftUI")
.typography(type: .body(Color.red))
}

and the result is like this:

tadaa! very easy, right?🤩

Conclusion

With this approach, we don’t need to make manual changes to each text view when there is a change in typography in our design language system, because we can manage font types, styles, and other properties centrally and use new view modifiers to apply them😉.

--

--

Farhan Adji
Farhan Adji

Responses (1)