Organize your fonts in iOS Projects

Sinan Ege
hepsiburadatech
4 min readJul 19, 2018

--

Everyone who develops iOS Apps have to face with font problem. For example we want to create a new label programmatically and change its font to “AvenirNext-Medium”

let label = UILabel(frame: .zero)label.font = UIFont(name: "AvenirNext-Medium", size: 14)

Lets look at this AvenirNext-Medium part of code. We can divide it to two part due to dash (“-”) character:

  1. Font Family: Before dash character part which is AvenirNext in our example.
  2. Font Style: After dash part character which is Medium in our example.

Also you can create a label and change its font from xib/storyboard. These two methods are doing exactly same things.

Change Font from XIB or Storyboard in Xcode

Actually problems are already started! Think that you are working on huge project and usually projects use few fonts. If you cant find a way to handle all of theese line of codes, you will have to write same lines to change fonts and when you search AvenirNext-Medium in your project there will be 9235123 same line .

The underlying problem of always writing create font with hardcoded string is that UIFont(name: String, size: CGFloat) initializer is failable initializer (You can get more information about Failable Initializers from Swift Blog). That means if you make a type while writing font name for example AvenirNEXTMedium, UIFont initializer will return a nil and if you write a code like

self.label = UIFont(name: "AvenirNextMedium", size: 16)

your label’s font will be System font with 16 size automatically because there is no font with this name.

Another problem to create UIFont with hardcoded string is you cant control your application’s fonts after a while. For example you decided to change your application fonts. You have to search and replace all of your hardcoded font names in project.

So we have to find a smart solution for your project. Okey here we go!

  • First of all create a struct. We will call this struct when we want to create new font everytime.
public struct FontBook {}
  • After that create a new protocol. Classes which conforms this protocol will be available via Fontbook.
public protocol isFont {var family: String { get }var style: String { get }}
  • Lets create our first Font as enum which conformRawRepresantable so we will use String enums and it will also conform isFont protocol.
public enum AvenirNext: String, isFont {
public var family: String {
return "AvenirNext"
}

public var style: String {
return self.rawValue.capitalizingFirstLetter()
}

case bold
case demiBold
case heavy
case medium
case regular

}

family and style properties required for conforming isFont protocol. As we know font names are like AvenirNext-Medium . Write every style of this font family as case of enum.

If you dont write any string equivalent of cases in string enum , it will be string of your case(like “bold”). So style computed property will return capitalized first letter version of raw value. (capitalizingFirstLetter() method is String extension)

  • Put your font into Fontbook struct and write a method into font enum that returns a UIFont
public struct FontBook {

public enum AvenirNext: String, isFont {
public var family: String {
return "AvenirNext"
}

public var style: String {
return self.rawValue.capitalizingFirstLetter()
}

case bold
case demiBold
case heavy
case medium
case regular
public func size(_ size: CGFloat) -> UIFont {
let fontName = "\(family)-\(style)"
return UIFont(name: fontName, size: size) ?? UIFont.systemFont(ofSize: size)
}
}
}

This method takes a size as a input parameter, creates UIFont with isFont protocol properties and returns UIFont.

  • Now we can use our new struct like:
    FontBook.AvenirNext.medium.size(14)

What if we want to add another font? You should do these steps:

  1. Create new string enum which conform isFont protocol
  2. Set family and style properties
  3. Define every style of font as a enum case
  4. Write a method that returns a UIFont with desired size

If you notice that you will write same lines in step 4. You can write extension to isFont protocol to get rid of step 4.

extension isFont {
public func size(_ size: CGFloat) -> UIFont {
let fontName = "\(family)-\(style)"
return UIFont(name: fontName, size: size) ?? UIFont.systemFont(ofSize: size)
}
}

As we say before, if we want to change our font style of app what will we do?

Write a extension to your FontBook to define fonts aliases according to usage. For example:

public extension FontBook {
public static let subTitle = AvenirNext.medium
public static let title = AvenirNext.heavy.size(18)
}

Now you can use your FontBook all of your app and if you want to change titles size or just general fonts of subtitles, just change from FontBook.

Here is the final status of FontBook

public protocol isFont {
var family: String { get }

var style: String { get }
}
extension isFont {
public func size(_ size: CGFloat) -> UIFont {
let fontName = "\(family)-\(style)"
return UIFont(name: fontName, size: size) ?? UIFont.systemFont(ofSize: size)
}
}
public struct FontBook {

public enum AvenirNext: String, isFont {
public var family: String {
return "AvenirNext"
}

public var style: String {
return self.rawValue.capitalizingFirstLetter()
}

case bold
case demiBold
case heavy
case medium
case regular

}

public enum MyriadWebPro: String, isFont {
public var family: String {
return "MyriadWebPro"
}

public var style: String {
return self.rawValue.capitalizingFirstLetter()
}

case regular

}
}

--

--