Product Components/HomeFeedScreen
Screen
HomeFeedScreen
A typical home/feed screen for social, productivity, or content apps. Combines a navigation bar, search bar, segmented tabs, a list of cards, and an activity feed — all wired with SwiftDS components and consistent token-based spacing.
Preview
Home
All
Following
Trending
A
Design System v2.0 released
NewAna Lima · 2m ago
J
Spring animations in SwiftUI
João M. · 1h ago
S
Token-based theming guide
Sara C. · 3h ago
◈Home
◎Explore
◉Inbox
▦Profile
Components used
DSNavigationBar
Title + trailing icon buttons
DSSearchBar
Inline search in the nav area
DSSegmentedControl
All / Following / Trending tabs
DSCard
Each feed item card
DSAvatar
Author avatar in each card
DSBadge
'New' badge on fresh content
DSTabs
Bottom tab navigation
DSSkeletonCard
Loading state for the feed
DSEmptyState
When feed has no content
Full implementation
HomeFeedScreen.swift
import SwiftUI
import SwiftDS
struct HomeFeedScreen: View {
@StateObject private var vm = HomeFeedViewModel()
@State private var selectedTab = "all"
var body: some View {
DSPageLayout {
// Navigation bar
DSNavigationBar(
title: "Home",
trailingItems: [
DSNavBarItem(icon: "plus", action: { vm.createPost() }),
DSNavBarItem(icon: "magnifyingglass", action: { vm.toggleSearch() }),
]
)
// Filter tabs
DSSegmentedControl(
options: [("All", "all"), ("Following", "following"), ("Trending", "trending")],
selected: $selectedTab
)
.padding(.horizontal, DSSpacing.md)
// Feed list
if vm.isLoading {
VStack(spacing: DSSpacing.md) {
ForEach(0..<4, id: \.self) { _ in DSSkeletonCard() }
}
.padding(DSSpacing.md)
} else if vm.posts.isEmpty {
DSEmptyState(
icon: "newspaper",
title: "Nothing here yet",
message: "Follow creators to see their posts.",
actionLabel: "Explore",
action: { selectedTab = "trending" }
)
} else {
LazyVStack(spacing: DSSpacing.sm) {
ForEach(vm.filteredPosts(tab: selectedTab)) { post in
FeedCard(post: post)
.onTapGesture { vm.open(post) }
}
}
.padding(DSSpacing.md)
}
}
}
}
struct FeedCard: View {
let post: Post
var body: some View {
DSCard(variant: .outlined, isInteractive: true) {
HStack(alignment: .top, spacing: DSSpacing.sm) {
DSAvatar(
initials: post.authorInitials,
size: .sm,
status: post.isOnline ? .online : nil
)
VStack(alignment: .leading, spacing: DSSpacing.xs) {
HStack {
DSText(post.title, style: .callout)
.lineLimit(2)
Spacer()
if post.isNew {
DSBadge("New", variant: .filled)
}
}
HStack(spacing: DSSpacing.xs) {
DSText(post.author, style: .caption)
.foregroundColor(DSColor.textSecondary)
DSText("·", style: .caption)
.foregroundColor(DSColor.textSecondary)
DSText(post.timeAgo, style: .caption)
.foregroundColor(DSColor.textSecondary)
}
}
}
}
}
}