Showcase is a Swift package designed to simplify the process of documenting and showcasing SwiftUI-based components. It provides a structured approach to create, organize, and present documentation for your SwiftUI projects. Showcase offers a variety of customization options and styles for presenting code samples, previews, and more.
- Structured documentation for SwiftUI components.
- Showcase your SwiftUI views' previews.
- Showcase libraries and chapters for organized content.
- Link external resources or documentation.
- Display code samples with syntax highlighting.
- Various styles for code blocks, previews, and external links.
- iOS 16+
- macOS 13+
- Swift 6.0+
- Xcode 16+
You can easily integrate Showcase into your Xcode project using Swift Package Manager. Follow these steps:
- Open your project in Xcode.
- Go to "File" -> "Swift Packages" -> "Add Package Dependency..."
- Enter the following URL when prompted: https://github.com/ipedro/swiftui-showcase
- Choose the version or branch you want to use.
- Click "Finish" to add the package to your project.
Alternatively, you can add Showcase as a dependency in your Package.swift file:
dependencies: [
.package(url: "https://github.com/ipedro/swiftui-showcase", from: "0.3.0")
]Also: Don't forget to add "Showcase" as a dependency of your package's target.
-
Import the Showcase module in your SwiftUI view file:
import Showcase -
Create and structure your showcase topics using
Topicinstances.
The Showcase view is used to display your showcase topics. Here's an example:
import Showcase
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
// Create a Showcase with a Topic, e.g., .card
Showcase(.mockCard)
.navigationTitle("Component Showcase")
}
}
}The ShowcaseList view allows you to list chapters of showcase topics. Here's an example:
import Showcase
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
// Create a list with two chapters using the DSL builders
ShowcaseList(
Chapter("Section 1") {
Topic("Card") {
Description("Display content and actions in a single container.")
}
Topic("Accordion") {
Description("Reveal or hide related content on demand.")
}
},
Chapter("Section 2") {
Topic("Button") {
Example {
Button("Tap me") {}
}
}
// New: Examples can include inline code blocks
Topic("Advanced Button") {
Example("With Source Code") {
Button("Submit") { }
.buttonStyle(.borderedProminent)
// Show the source code inline
CodeBlock("Implementation") {
"""
Button("Submit") { }
.buttonStyle(.borderedProminent)
"""
}
}
}
}
)
.navigationTitle("Component Showcase")
}
}
}The ShowcaseNavigationView view provides navigation to chapters of showcase topics. Here's an example:
import Showcase
import SwiftUI
struct ContentView: View {
var body: some View {
// Create a navigation stack from a document composed with the DSL
ShowcaseNavigationStack(
Document("My Chapter") {
Chapter("Section 1") {
Topic("Card") {
Description("Display content and actions in a single container.")
}
Topic("Button") {
Example {
Button("Tap me") {}
}
}
}
}
)
}
}Showcase provides powerful macros to automatically generate documentation from your SwiftUI components with minimal effort.
The @Showcasable macro automatically generates a showcaseTopic property for your types, extracting documentation from doc comments and discovering members:
import Showcase
import ShowcaseMacros
/// A card component for displaying content
///
/// Cards are versatile containers that can hold any SwiftUI content.
///
/// > Note: Always provide meaningful content to your cards
@Showcasable(icon: "rectangle.fill")
struct Card<Content: View>: View {
/// The card's optional title
let title: String?
/// The card's content
let content: Content
/// Creates a card with optional title
init(title: String? = nil, @ViewBuilder content: () -> Content) {
self.title = title
self.content = content()
}
var body: some View {
VStack(alignment: .leading) {
if let title {
Text(title).font(.headline)
}
content
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(12)
}
}The macro automatically generates:
extension Card: Showcasable {
public static var showcaseTopic: Topic {
Topic("Card") {
// Type relationships
CodeBlock("Type Relationships") {
"""
struct Card<Content: View>: View
"""
}
// Main description
Description {
"""
A card component for displaying content
Cards are versatile containers that can hold any SwiftUI content.
"""
}
// Note extracted from doc comment
Note {
"""
Always provide meaningful content to your cards
"""
}
// Auto-discovered initializer
Topic("init(title:content:)") {
Description {
"""
Creates a card with optional title
"""
}
CodeBlock("Declaration") {
"""
init(title: String?, content: () -> Content)
"""
}
}
// Auto-discovered properties
Topic("title") {
Description {
"""
The card's optional title
"""
}
CodeBlock("Declaration") {
"""
var title: String?
"""
}
}
Topic("content") {
Description {
"""
The card's content
"""
}
CodeBlock("Declaration") {
"""
var content: Content
"""
}
}
}
}
}Use @ShowcaseExample to define reusable examples that will be automatically discovered:
struct CardExamples {
@ShowcaseExample(title: "Simple Card")
static var simple: some View {
Card(title: "Welcome") {
Text("This is a simple card")
}
}
@ShowcaseExample(
title: "Card with Image",
description: "Cards can contain any SwiftUI content"
)
static var withImage: some View {
Card(title: "Photo") {
VStack {
Image(systemName: "photo")
.font(.largeTitle)
Text("Add your photo here")
}
}
}
}
// Reference examples in @Showcasable
@Showcasable(icon: "rectangle.fill", examples: [CardExamples.self])
struct Card<Content: View>: View {
// ... implementation
}The macro automatically adds examples to the generated topic:
extension Card: Showcasable {
public static var showcaseTopic: Topic {
Topic("Card") {
// ... type info and members ...
// Examples are automatically added
ExampleGroup("Examples") {
Example("Simple Card") {
CardExamples.simple
CodeBlock("Source Code") {
"""
Card(title: "Welcome") {
Text("This is a simple card")
}
"""
}
}
Example("Card with Image") {
Description {
"""
Cards can contain any SwiftUI content
"""
}
CardExamples.withImage
CodeBlock("Source Code") {
"""
Card(title: "Photo") {
VStack {
Image(systemName: "photo")
.font(.largeTitle)
Text("Add your photo here")
}
}
"""
}
}
}
}
}
}@ShowcaseHidden
Hide specific members from auto-discovery:
@Showcasable
struct MyView: View {
@ShowcaseHidden
private var internalHelper: String = ""
public var displayName: String = "User"
var body: some View {
Text("Hello, \(displayName)")
}
}The macro generates documentation excluding hidden members:
extension MyView: Showcasable {
public static var showcaseTopic: Topic {
Topic("MyView") {
CodeBlock("Type Relationships") {
"""
struct MyView: View
"""
}
// Only public, non-hidden members are documented
Topic("displayName") {
CodeBlock("Declaration") {
"""
var displayName: String
"""
}
}
// internalHelper is NOT included (marked with @ShowcaseHidden)
}
}
}- Automatic Updates: Documentation stays in sync with your code
- Less Boilerplate: No manual Topic construction needed
- Doc Comment Integration: Leverages existing documentation
- Type Safety: Compile-time validation of examples
- Auto-Discovery: Finds members, examples, and relationships automatically
Showcase is released under the MIT License. See LICENSE for details.
Contributions are welcome! Please see our Contribution Guidelines for more information.
Showcase is created and maintained by Pedro Almeida.