Pattern · Auth and Onboarding

New

Interests Picker SwiftUI Block

A first-run step for picking topics: wrapping chips by section, a search that filters, a count that rolls, and Continue at the minimum.

Topics as chips with a symbol and a label, grouped under section titles in a flow layout that fits any width and text size. Picked chips take a light accent tint with a check, the count rolls, a search filters as it's typed, and the button counts down Pick 2 more until the minimum turns it into Continue, which runs your closure through working, done and failed. An optional maximum steps the other chips back.

  • SwiftUI
  • iOS 17.0+
  • interests-picker
Get SwiftPieces Pro

$199 once, every piece included. See how installing works · Sign in

01

What's included

SwiftUI source you own, built on Apple frameworks only.

  • Chips with a symbol and label in a wrapping flow layout, grouped by section
  • Picked chips take an accent tint and a check as the count rolls
  • Search filters by topic or section, with a no-match state
  • Pick 2 more until the minimum, then Continue with working, done and failed
  • An optional maximum, and grey chips while topics load

02

Implementation notes

How it behaves, what it handles, and the decisions behind it.

The step opens with "What are you into?", "Pick at least 3." and a count of what's picked that rolls with each tap. A search field filters the topics as it's typed, by topic or by section, and says "No topics match" when nothing does. One card holds the topics as chips grouped under section titles; the chips wrap in a flow layout that fits any width and text size. Each chip is a symbol and a label on a quiet fill; picked, it takes a light accent tint with a check in place of the symbol. Until the minimum is met the button reads "Pick 2 more" on a quiet fill and can't be tapped; at the minimum it turns into Continue in the accent, which runs your closure and shows working, done and failed in place. With a maximum, the other chips step back once it's reached and a line says to tap a topic to swap it out. Pass nil while the topics load and the card draws grey chips.

Notes

  • InterestsPicker(categories:selection:minimum:maximum:title:onContinue:onSkip:). It is a section: no navigation stack, background, padding or scroll view of its own, so place it in your onboarding's column, such as a ScrollView with .padding(.horizontal, 20).
  • categories is a list of InterestsPicker.Category (an id, a title and its topics), each topic an InterestsPicker.Topic with an id, a title and an SF Symbol name. Category.samples has fourteen neutral topics under Make, Move and Learn. Pass nil while you fetch them.
  • onContinue receives the picked topic ids as a Set<String>. Throw to fail: the block shows a thrown LocalizedError's errorDescription, or "Couldn't save your interests. Try again." A CancellationError goes back to rest quietly. The check holds for about a second; move to the next step from your closure or after it.
  • selection is a Binding<Set<String>> for a screen that keeps the picks, to restore them or show them elsewhere. Leave it out and the block keeps its own.
  • minimum is 3 by default; 0 makes Continue available at once. maximum caps the picks; nil means no limit.
  • onSkip shows "Skip for now" under Continue. Nil hides it.
  • Search matches a topic's title, or a section's title to show the whole section. Clearing it brings everything back; picks are kept either way.
  • Haptics: a selection tick as each chip is picked or unpicked, success when the picks save and an error when they fail.
  • VoiceOver reads each chip as a button with Selected when picked, the count as "3 of at least 3 picked", and the locked button as "Pick 2 more to continue". It announces "Interests saved" and failures. Chips are 40 points tall with a 44 point target. At accessibility sizes the count moves under the rule, chips grow and wrap onto more lines, and a label too long for the line wraps inside its chip.
  • Reduce Motion turns every change into a short fade.
  • InterestsPickerExample at the end of InterestsPicker.swift is Fieldnote's third first-run step from the preview. It shows the grey chips for a moment, loads Category.samples, needs three picks and saves after a second.

Theming

  • Reads SPTheme from @Environment(\.spTheme) and requires the design-system item.
  • The title is the display face at 28 points, semibold at most, in textPrimary; the rule is type.body in textSecondary; the count is type.subhead medium with tabular figures, textPrimary once the minimum is met.
  • The search field is 52 points on fill with radius.md and a separator hairline that turns to textPrimary while searching; its symbol is textSecondary.
  • The card is color.surface with radius.lg, 16 point padding and 24 points between sections; section titles are type.headline.
  • Chips are capsules on fill with type.subhead medium labels in textPrimary and symbols in textSecondary. A picked chip is accent at 14% with a 60% accent hairline and an accent check. Locked chips drop to 40% opacity.
  • The button is fill with textSecondary until the minimum, then accent with accentOn, in type.headline, radius.md, 52 points. Skip for now is textPrimary semibold. Errors are type.footnote in danger.
  • Picks move with motion.snappy, the loaded topics with motion.smooth, chips press with motion.bouncy and the button with motion.standard.