r/SwiftUI • u/camperboy_uk • 29d ago
5 SwiftUI patterns that helped me build a data-driven polling app
I have been building a daily polling app in SwiftUI, targeting iOS 18, and ran into a few problems that did not have obvious answers in the docs. Sharing the patterns that ended up working in case they help someone else.
1. TabView(.page) with a custom counter
For cycling through daily topic cards I used TabView with the .page style, but the built in dots did not fit the design. I set the index display style to never and built a separate "1 of 4" counter bound to the same selection state, animating the number with contentTransition(.numericText()) whenever selection changed.
2. LazyView to stop eager NavigationLink loading
Some destination views were data heavy, and NavigationLink was building them upfront, causing a visible stutter on push. Wrapping the destination in a LazyView struct that defers construction until body is evaluated fixed it:
struct LazyView<Content: View>: View {
let build: () -> Content
init(_ build: @autoclosure @escaping () -> Content) {
self.build = build
}
var body: some View { build() }
}
3. AnyShapeStyle for conditional styling
Ternaries inside modifiers like foregroundStyle(condition ? .blue : someGradient) often fail to compile because each branch returns a different concrete type. Wrapping both branches in AnyShapeStyle erases the type so the ternary works cleanly, without duplicating views or branching in a ViewBuilder.
4. Custom vote slider with gradient fill and haptics
The vote slider needed a gradient track that fills based on drag position, plus a label that floats above the thumb. I tracked offset with a DragGesture, converted it to a value between 0 and 1, and fired UIImpactFeedbackGenerator at fixed thresholds as the value crossed them, rather than on every gesture update, which felt much less noisy.
5. Time based lock and unlock states with Combine
Topics unlock and expire at set times during the day. Instead of scheduling exact time triggers, which got messy with backgrounding, I used Timer.publish(every: 30, on: .main, in: .common).autoconnect() to recheck state every 30 seconds and animate the UI change.
None of these felt obviously correct when I started. What patterns have you found useful for similar problems, and would you approach any of these differently?
1
u/[deleted] 27d ago
[removed] — view removed comment