A breeze of Storybook for the Apps
Intro
After talking in the first article, “Building a Design System is a Collaborative Adventure, “ we want to give you a more specific insight into our custom Design Storybook for the Apps in today's read. When we started with our design system development at rebuy the web and app teams were sitting together to find a way of visualizing, testing, and documenting the upcoming design components we planned to build. For our web team, it got pretty quickly clear that they will decide on the tool Storybook. It simply delivers all the needs our web team has. The app team was searching for a similar tool that could fulfill the same needs. But sadly, after investigating this topic for a few days, we concluded that no suitable tool on the market meets our requirements. We also talked to some other tech companies in the Berlin IT market that also develop apps, and the conclusion from these talks was that we would need to build our custom solution for this problem. We were a bit jealous of what the web team got basically “for free“ with the Storybook, and since we still had a high need to provide something similar for the apps, we decided to build some “Storybook light” on our own. So let me show you what we have built for the apps and give you some insights into the feature set. For simplicity reasons, we are only showing the iOS version, but the Android version is very similar in features.
The Storybook Light
In the picture below you can see the current state of our custom Storybook-like implementation for iOS. It provides us with our Design Tokens, Components, and useful hints about its implementation and guides us with code examples and configuration options on how to use them. Though our tool might not win a beauty contest, it serves us and helps us to get our job done on a daily base. It is distributed twice a week for iOS, iPad OS, Mac OS, and Android.
The left navigation bar contains a list of all design tokens, basic components, animations, and a component composer for testing multiple more complex components next to each other.
- Here, you see the buttons we are providing for our application via the Design System as an example.
- We are linking to Figma because we are documenting the design and behavior of our components there.
- For all basic components that we provide, we also show at least one code example of how the developers can use this component.
- We give each component the option to test all settings of its public interface.
The component Composer
The component composer is a feature where designers, product owners, and developers can test multiple components next to each other. It supports everybody to quickly understand the capabilities of each component and gives a fast insight into how multiple components next to each other would interplay.
How we define a component
A component is a reusable view that sits in our design system library called RebuyUI
. It emerges out of the fact that we have at least 2 very similar views in our app that serve the same purpose. The designers analyze and unify them and make a component out of them. A component describes the stylings and behaviors itself in which it can be used. Modifications from outside beyond the public interface are prohibited and undesirable. The motto is “Change the system, not the application”. Components are allowed to be composed out of multiple existing components. The public interface exposes a minimum of logic and is as restrictive as possible. For example, instead of providing a property to set any available backgroundColor to the component, we only provide an enum that allows us to set the style enum { success, warning, failure, neutral }
. The components are free from business logic. Every component beyond these “basic“ components like Texts
, Buttons,
or InputFields
has to define only one public constructor parameter called model: Model
that conforms to JSON Codable
that we have called Reflectable
. This helps us simplify a few points and we will come to this in the next section.
How does a component look under the hood?
In the code below, you can see a simplified version of how we would build a KeyValueComponent. The component shows two texts next to each other, with the key first and the value afterwords that are divided by a colon and a space. For simplicity reasons and to show the general process, the code is simplified a bit and does not contain the full feature set that our KeyValueComponent has to offer.
import SwiftUI
/*
protocol ModelSampleData {
static var sample: Reflectable { get }
//...
}
protocol Reflectable: Codable {}
*/
public struct SampleKeyValueComponent: View {
public enum KeyStyle: String, CaseIterable {
case neutral
case highlight
//...
var foregroundColor: Color {
switch self {
case .neutral: .aSpecificColor
case .highlight: .anotherSpecificColor
}
}
}
public struct Model: Reflectable {
public let key: String
public let value: String
public let keyStyle: KeyStyle = .neutral
}
private let model: Model
public init(model: Model) {
self.model = model
}
public var body: some View {
HStack {
Text(model.key)
Text(": ")
Text(model.value)
.foregroundColor(model.keyStyle.foregroundColor)
}
}
}
extension SampleKeyValueComponent: ModelSampleData {
static sample: Reflectable {
Model(key: "John", value: "Doe")
}
}
Now you might wonder what we are doing with ModelSampleData
and Reflectable
.
ModelSampleData
With the help of this protocol, we achieve multiple different purposes:
- We have standardized the way of defining sample data
- This sample data guides the developers on how the data could look.
- The Storybook Component Composer is using this data to automatically init the views with this placeholder content
- We use the sample data in our SwiftUI previews.
- We also use this data to prefill loading states to generate the
redacted
effect
Reflectable
The reflectable is only helping the Storybook Component Composer to generate the edit view per component right now. Since Swift has limited reflection capabilities, we needed to work around this. We found the solution in making the Model
JSON Codable. First, we encode the Model. Then we manipulate the JSON data via the keypath with the newly changed value, and then we decode the JSON back to the Model. Of course, this is not super fast, but since this is just for an internal tool and the UI is never really slow, the solution is good enough for now. We might consider implementing some reflection solutions with the recently introduced Macros, but right now, we have no urgent need to change them. The edit view gets generated by the types of the Model. For example, a String leads to an InputField, while an enum will show its options as a segmented picker, or the color will open the color palette we have defined.
Conclusion
Final words In the end, building a Design System with a Storybook tool offers multiple advantages to us. One key benefit is the ability to showcase individual components in isolation; it helps us better understand and test them. Additionally, The storybook allows us to strengthen the collaboration among team members by providing a centralized place for component documentation. For instance, implementing this Storybook streamlined our development process by allowing designers and developers to work in tandem, resulting in faster iterations and higher-quality outcomes.
Thank you for reading. We look forward to hearing about your experiences and feedback in the comments.