SwiftUI Design System
If your dreams don’t scare you, then your dreams aren’t big enough. And I also remember, Sutan Sjahrir once said, ‘a life that is not risked will never be won’. (Najwa Shihab)
What is Design System ?
A design system is a comprehensive set of guidelines, components, and tools that are used to create consistent and cohesive user interfaces across different platforms and products. It serves as a single source of truth for design and development, helping teams maintain a unified look and feel throughout their products.
So Why We need Design System ?
In short we need Design System to Ensures a unified look and feel across different products and platforms, providing a seamless user experience.
With Consistency we can get Efficiency that speeds up the design and development process by providing reusable components and patterns.
Enhances collaboration between designers and developers by providing a shared language and set of tools, reducing miscommunication and ensuring alignment.
Oke, we need to implement Design System to our Project Sample. Design template I grab from Figma Free Mobile App Template. So I choose Podcast App for this project.
For building Design System at least we need to create Foundation like Color, Typography and Spacing.
Color
For color I use Assets to declare all color that I need, like bellow image
Then we create Color+Extension to handle color token
import SwiftUI
public extension Color {
static let accent1 = Color("Accent 1", bundle: .module)
static let accent2 = Color("Accent 2", bundle: .module)
static let accent3 = Color("Accent 3", bundle: .module)
static let grayScale1 = Color("Grayscale 1", bundle: .module)
static let grayScale2 = Color("Grayscale 2", bundle: .module)
static let grayScale3 = Color("Grayscale 3", bundle: .module)
static let grayScale4 = Color("Grayscale 4", bundle: .module)
static let grayScale5 = Color("Grayscale 5", bundle: .module)
}
I know color naming still confusing but for now we just create like Podcast App Guidelines. We can use it just like this
Color.accent1
Typography
For Typography we need to import our custom font to our Package Manager or Project.
After we import Font to our project we need to create Class and method to register custom font in to our App, it is super easy
import SwiftUI
public enum NotoSans: String, CaseIterable {
case thin = "NotoSans-Thin"
case bold = "NotoSans-Bold"
case light = "NotoSans-Light"
case black = "NotoSans-Black"
case medium = "NotoSans-Medium"
case regular = "NotoSans-Regular"
case semiBold = "NotoSans-SemiBold"
case extraBold = "NotoSans-ExtraBold"
case extraLight = "NotoSans-ExtraLight"
}
public struct NotoSansFont {
public static func registerFonts() {
NotoSans.allCases.forEach {
registerFont(bundle: .module, fontName: $0.rawValue, fontExtension: "ttf")
}
}
fileprivate static func registerFont(bundle: Bundle, fontName: String, fontExtension: String) {
guard let fontURL = bundle.url(forResource: fontName, withExtension: fontExtension),
let fontDataProvider = CGDataProvider(url: fontURL as CFURL),
let font = CGFont(fontDataProvider) else {
fatalError("Couldn't ceate font from filename: \(fontName) with extension \(fontExtension)")
}
var error: Unmanaged<CFError>?
CTFontManagerRegisterGraphicsFont(font, &error)
}
}
Declare all type of your font there, after that we create extension for Font like this
import SwiftUI
extension Font {
public static var h1: Font = {
return notoSans(.bold, size: 36)
}()
public static var h2: Font = {
return notoSans(.semiBold, size: 24)
}()
public static var h3: Font = {
return notoSans(.medium, size: 18)
}()
public static var h4: Font = {
return notoSans(.medium, size: 14)
}()
public static var h5: Font = {
return notoSans(.medium, size: 12)
}()
public static var h6: Font = {
return notoSans(.regular, size: 12)
}()
public static func notoSans(_ font: NotoSans, size: CGFloat) -> Font {
return .custom(font.rawValue, size: size)
}
}
To use it we can just
Text("Listen to the best Podcast")
.font(.h1)
.foregroundColor(.grayScale1)
But we can simplify again with ViewModifier
struct TitleStyle: ViewModifier {
func body(content: Content) -> some View {
content.font(.h1)
}
}
extension View {
public func titleStyle() -> some View {
modifier(TitleStyle())
}
}
Then we can call it like this
Text("Listen to the best Podcast")
.foregroundColor(.grayScale1)
.titleStyle()
For Spacing we just add this
public enum Spacing {
public static let small: CGFloat = 8
public static let medium: CGFloat = 16
public static let large: CGFloat = 32
public static let extrLarge: CGFloat = 64
}
Foundation DONE !
Next we go in to Component, for now we just create Button component
Button
On the SwiftUI we can create ButtonStyle than can be use to all button, not just that for TextField SwiftUI have TextFieldStyle.
So how we use it ?
It’s very simple actually, you can just copied from my code
import SwiftUI
public struct PrimaryButtonStyle: ButtonStyle {
public init() {}
public func makeBody(configuration: Configuration) -> some View {
configuration.label
.font(.h4)
.padding(Spacing.medium)
.background(Capsule().fill(Color.accent1))
.foregroundColor(.white)
.scaleEffect(configuration.isPressed ? 0.95 : 1.0)
}
}
And how you can use it ? You can use it like this
Button {
} label: {
Text("Create new account")
}
.buttonStyle(PrimaryButtonStyle())
But we can make it more pretty just add this code
extension ButtonStyle where Self == PrimaryButtonStyle {
public static var primary: Self { Self() }
}
So whenever you register ButtonStyle you just call it like this
Button {
} label: {
Text("Create new account")
}
.buttonStyle(.primary)
Give me 6 hours to chop down a tree and I will spend the first 4 hours sharpening the axe.
So with Design System like we spend 4 hours to sharpening the axe, this will take more than half your time but that’s worth to do.