How to use custom fonts in SwiftUI

In this article, I will show you how to build a custom font modifier CustomFontModifier in SwiftUI, which can be used for typography in your iOS app.

<span><span style="color: var(--shiki-token-function)">Text</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-string-expression)">&quot;Juhu!&quot;</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)">    .</span><span style="color: var(--shiki-token-function)">customFont</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">.bold</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> .h1</span><span style="color: var(--shiki-token-punctuation)">))</span></span>
<span></span>

Creating a custom Font

When building an app using a custom font, I’ve found that creating a custom font modifier CustomFontModifier is the best solution.

To implement this, start by creating a new file Font.swift, where we define our custom font, alongside it’s typographic sizes:

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">SwiftUI</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">enum</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">Font</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> light(</span><span style="color: var(--shiki-token-parameter)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> Size)</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> regular(</span><span style="color: var(--shiki-token-parameter)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> Size)</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> medium(</span><span style="color: var(--shiki-token-parameter)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> Size)</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> bold(</span><span style="color: var(--shiki-token-parameter)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> Size)</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">enum</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">Size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">CGFloat </span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> h1 </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">21</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> h2 </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">16</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> h3 </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">14</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> h4 </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">12</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> font: UIFont {</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">switch</span><span style="color: var(--shiki-color-text)"> self {</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> .</span><span style="color: var(--shiki-token-function)">light</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">:</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">UIFontMetrics</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">forTextStyle</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> .body</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-color-text)">.</span><span style="color: var(--shiki-token-function)">scaledFont</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">for</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> R.font.osloSansLight</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> size.</span><span style="color: var(--shiki-token-constant)">rawValue</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> .</span><span style="color: var(--shiki-token-function)">regular</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">:</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">UIFontMetrics</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">forTextStyle</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> .body</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-color-text)">.</span><span style="color: var(--shiki-token-function)">scaledFont</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">for</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> R.font.osloSansRegular</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> size.</span><span style="color: var(--shiki-token-constant)">rawValue</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> .</span><span style="color: var(--shiki-token-function)">medium</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">:</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">UIFontMetrics</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">forTextStyle</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> .body</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-color-text)">.</span><span style="color: var(--shiki-token-function)">scaledFont</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">for</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> R.font.osloSansMedium</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> size.</span><span style="color: var(--shiki-token-constant)">rawValue</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> .</span><span style="color: var(--shiki-token-function)">bold</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">:</span></span>
<span><span style="color: var(--shiki-color-text)">            </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">UIFontMetrics</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">forTextStyle</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> .body</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-color-text)">.</span><span style="color: var(--shiki-token-function)">scaledFont</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">for</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> R.font.osloSansBold</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> size.</span><span style="color: var(--shiki-token-constant)">rawValue</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">!</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)">        }</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

I’ve placed my inside /Resources , and access it with the

<span><span style="color: var(--shiki-token-keyword)">static</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">func</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">osloSansRegular</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-color-text)">: CGFloat) </span><span style="color: var(--shiki-token-keyword)">-&gt;</span><span style="color: var(--shiki-color-text)"> UIFont</span><span style="color: var(--shiki-token-keyword)">?</span></span>
<span></span>

function. In my implementation, I am using UIFontMetrics to support Dynamic Type. If this behavior is not wanted, you could simply return the font directly:

<span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> font: UIFont {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">switch</span><span style="color: var(--shiki-color-text)"> self {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> .</span><span style="color: var(--shiki-token-function)">light</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">:</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> R.font.</span><span style="color: var(--shiki-token-function)">osloSansLight</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> size.</span><span style="color: var(--shiki-token-constant)">rawValue</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">!</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> .</span><span style="color: var(--shiki-token-function)">regular</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">:</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> R.font.</span><span style="color: var(--shiki-token-function)">osloSansRegular</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> size.</span><span style="color: var(--shiki-token-constant)">rawValue</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">!</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> .</span><span style="color: var(--shiki-token-function)">medium</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">:</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> R.font.</span><span style="color: var(--shiki-token-function)">osloSansMedium</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> size.</span><span style="color: var(--shiki-token-constant)">rawValue</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">!</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">case</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> .</span><span style="color: var(--shiki-token-function)">bold</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">:</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> R.font.</span><span style="color: var(--shiki-token-function)">osloSansBold</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> size.</span><span style="color: var(--shiki-token-constant)">rawValue</span><span style="color: var(--shiki-token-punctuation)">)</span><span style="color: var(--shiki-token-keyword)">!</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

Custom Font Modifier

In a new file CustomFontModifier.swift, we implement the custom modifier, accepting a Fontand applying it the the content:

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">SwiftUI</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">struct</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">CustomFontModifier</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">ViewModifier </span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">var</span><span style="color: var(--shiki-color-text)"> font: Font</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">func</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">body</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-function)">content</span><span style="color: var(--shiki-color-text)">: Content) </span><span style="color: var(--shiki-token-keyword)">-&gt;</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">some</span><span style="color: var(--shiki-color-text)"> View {</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">let</span><span style="color: var(--shiki-color-text)"> uiFont </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> font.font</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> content.</span><span style="color: var(--shiki-token-function)">font</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">.custom</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">uiFont.fontName, size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> uiFont.pointSize</span><span style="color: var(--shiki-token-punctuation)">))</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">extension</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">View</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">func</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">customFont</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-function)">_</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-parameter)">font</span><span style="color: var(--shiki-color-text)">: Font) </span><span style="color: var(--shiki-token-keyword)">-&gt;</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">some</span><span style="color: var(--shiki-color-text)"> View {</span></span>
<span><span style="color: var(--shiki-color-text)">        self.</span><span style="color: var(--shiki-token-function)">modifier</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">CustomFontModifier</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">font</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> font</span><span style="color: var(--shiki-token-punctuation)">))</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

Usage

With our custom font modifier, we can apply it to all components that require our custom font:

<span><span style="color: var(--shiki-token-function)">Text</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-string-expression)">&quot;Custom Font Engaged&quot;</span><span style="color: var(--shiki-token-punctuation)">)</span></span>
<span><span style="color: var(--shiki-color-text)">    .</span><span style="color: var(--shiki-token-function)">customFont</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">.regular</span><span style="color: var(--shiki-token-punctuation)">(</span><span style="color: var(--shiki-token-function)">size</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-token-function)"> .h2</span><span style="color: var(--shiki-token-punctuation)">))</span></span>
<span></span>