Exploring SwiftUI: Typography System
Book

Exploring MusicKit and Apple Music API

Unlock the full power of MusicKit & Apple Music APIs in your apps with the best guide! Use code musickit-blog for a limited-time 35% discount!

While working on the next update for Quoting, I realized that the fonts are all over the place, and inconsistencies are creeping in. Someplace it is title3 with light weight, someplace it is title2 with regular weight.

I created a folder for Design System to overcome this problem and have consistency across the app for a smoother future update cycle. This is the part of the series that talks about the fonts.

I took inspiration from Building a SwiftUI Design System – Part 2: Colors, Typography, Iconography, Animation written by Joel Pöllänen and modified the idea according to my needs.

To start, the app uses a custom font Nunito. I play around with fonts once in a while, like Montserrat or Poppins, and changing it at one place is so convenient.

Font Type

The enum FontType contains the cases for the font types and has a variable to return the capitalized name.

enum FontType: String {
  case montserrat
  case josefinSans
  case poppins
  case openSans
  case sourceSansPro
  case nunito
  
  var name: String {
    self.rawValue.capitalized
  }
}

Font Weight

Then, there’s another enum, FontWeight, to define the various weights that are commonly used. It follows the convention of naming it by appending a “-“ in front of the capitalized name:

enum FontWeight: String {
  case extraLight
  case light
  case thin
  case regular
  case medium
  case semiBold
  case bold
  case extraBold
  case black
  
  case extraLightItalic
  case lightItalic
  case thinItalic
  case regularItalic
  case mediumItalic
  case semiBoldItalic
  case boldItalic
  case extraBoldItalic
  case blackItalic
  
  var name: String {
    "-" + self.rawValue.capitalized
  }
}

Note that this enum has all the possible cases and doesn’t check if there’s any missing font.

Custom Font Methods

For the custom font method, I created an extension on Font and added two variations of static private methods:

  • font(type:weight:style:)
  • font(type:weight:size:)

They’re private so that I don’t accidentally use them anywhere. From iOS 14 and above, the custom font sizes automatically update with the system sizes, so this implementation works great to support Dynamic Type:

@available(iOS 14, *)
extension Font {
  /// Use this method for custom fonts with variable weight and style.
  /// Dynamically updates the font size with the system size.
  /// - Parameters:
  ///   - type: Cases that describe the preferred type of font.
  ///   - weight: Cases that describe the preferred weight for fonts.
  ///   - style: Constants that describe the preferred styles for fonts.
  /// - Returns: Custom font based on the parameters you specified.
  static private func font(type: FontType, weight: FontWeight, style: UIFont.TextStyle) -> Font {
    .custom(type.name + weight.name, size: UIFont.preferredFont(forTextStyle: style).pointSize)
  }
  
  /// Use this method for custom fonts with variable weight and size.
  /// Dynamically updates the font size with the system size.
  /// - Parameters:
  ///   - type: Cases that describe the preferred type of font.
  ///   - weight: Cases that describe the preferred weight for fonts.
  ///   - size: Constants that describe the preferred size for fonts.
  /// - Returns: Custom font based on the parameters you specified.
  static private func font(type: FontType, weight: FontWeight, size: CGFloat) -> Font {
    .custom(type.name + weight.name, size: size)
  }
}

Combining the type and weight name becomes similar to the font’s file name referenced in the Info.plist. For example, Nunito-SemiBoldItalic.ttf.

Now that the base is ready, it’s time to create the custom fonts!

Custom Fonts Naming

If I use the same name as SwiftUI’s font like headline or body, it works well, but SwiftUI previews can’t distinguish between the two and won’t run at all. To solve that problem, I prepended the constants with the word “app”:

@available(iOS 14, *)
extension Font {
  
  /// A font with the large title text style.
  static let appLargeTitle = font(type: .nunito, weight: .bold, style: .largeTitle)
  
  /// A font with the title text style.
  static let appTitle = font(type: .nunito, weight: .bold, style: .title1)
  
  /// A font for second level hierarchical headings.
  static let appSecondaryTitle = font(type: .nunito, weight: .bold, style: .title2)
  
  /// A font for third level hierarchical headings.
  static let appTertiaryTitle = font(type: .nunito, weight: .bold, style: .title3)
  
  /// A font with the headline text style.
  static let appHeadline = font(type: .nunito, weight: .bold, style: .headline)
  
  /// A font with the body text style.
  static let appBody = font(type: .nunito, weight: .regular, style: .body)

  /// A font with the callout text style.
  static let appCallout = font(type: .nunito, weight: .regular, style: .callout)
  
  /// A font with the caption text style.
  static let appCaption = font(type: .nunito, weight: .regular, style: .caption1)
  
  /// A font with the credits text style.
  static let appCredits = font(type: .nunito, weight: .light, style: .caption2)
}

The above list is all the fonts used in Quoting app. If I plan to change the title to be extra bold of type Poppins, it’ll just be a single line of change!

Usage

Using it is as simple as using SwiftUI’s default fonts. You use the font modifier:

struct TitleView: View {
  private var title: String
  
  init(_ title: String) {
    self.title = title
  }
  
  var body: some View {
    Text(title)
      .font(.appLargeTitle)
      .frame(maxWidth: .infinity)
      .padding(.leading)
  }
}

Conclusion

Having a typography system from the beginning or while refactoring helps you sort the fonts in one place, which becomes the source of truth. It creates consistency across the app and gives you the ease of updating any font in a single file.

The second part of this series will be about creating a Color System.

If you have a better approach, please tag @rudrankriyam on Twitter! I love constructive feedback and appreciate constructive criticism.

Thanks for reading, and I hope you’re enjoying it!

Book

Exploring MusicKit and Apple Music API

Unlock the full power of MusicKit & Apple Music APIs in your apps with the best guide! Use code musickit-blog for a limited-time 35% discount!

Written by

Rudrank Riyam

Hi, my name is Rudrank. I create apps for Apple Platforms while listening to music all day and night. Author of "Exploring MusicKit". Apple WWDC 2019 scholarship winner.