Block · Account
NewInvite Friends SwiftUI Block
A referral section: what both people get, a track that fills as friends join, the invite code to copy or share, and who you invited.
The referral section of an account or rewards screen. One card states the reward, fills a seat on a quiet track for each friend who joins, copies the invite code with one tap and opens the system share sheet. Invited friends list below with a status chip, and pending invites can be reminded.
- SwiftUI
- iOS 17.0+
- invite-friends
$199 once, every piece included. See how installing works · Sign in
01
What's included
SwiftUI source you own, built on Apple frameworks only.
- What both people get, with a track that fills a seat as each friend joins
- The invite code copies with one tap and answers Copied
- Share invite opens the system share sheet with your link, message and preview
- Invited friends with Invited, Joined and Rewarded chips, and Remind on pending invites
- Loading, empty and all-rewards-earned states, driven by one
Referralvalue
02
Implementation notes
How it behaves, what it handles, and the decisions behind it.
One surface card states what both people get ("Give a month, get a month") with a line on how it works, then a quiet track with a seat for each friend the reward needs under "2 of 5 joined" and "3 to go". When a friend joins, the count rolls and the next seat fills from its leading edge; when every seat is full, the count line drops "to go" and a calm line with a success checkmark says the rewards are earned. Under it, the invite code sits in a field-styled row in a monospaced face: tap anywhere on it and the code goes to the pasteboard while Copy answers "Copied" with a checkmark and a success haptic, then settles back. Share invite, the one accent action, opens the system share sheet with your link, your message and a preview. Below the card, everyone you invited lists newest first: initials on a quiet fill (an envelope or phone for an invite that went to an address), the name over a tinted status chip (Invited, Joined, Rewarded) and when, and Remind at the trailing edge of a pending invite. Remind shows a spinner while your closure runs, then reads "Reminded"; a thrown error puts its message under the row in the danger colour and Remind comes back to try again. With no invites yet the list says "No invites yet. Share your link to get started." Past five friends, a Show all row opens the rest. Pass nil while the referral loads and the block shows grey shapes in its own layout.
Notes
InviteFriendsBlock(referral:sharePreviewIcon:onRemind:onCopy:onShare:)takes oneInviteFriendsBlock.Referral. It is a section: no navigation stack, background, padding or scroll view of its own, so place it in your own column: aScrollViewwith.padding(.horizontal, 20), or a sheet.Referralholds theheadline, adetailline, thegoal(one seat per friend the reward needs), an optionalcompleteNotefor the full track (defaults to "You've earned every reward. Thanks for spreading the word."), the invitecode, theshareURL, theshareMessageandfriends.joinedCountandisCompleteare there for your own logic.Referral.sampleis Maya's Stillwater referral from the preview.- A
Referral.Friendhas anid, aname(their name, or the email or masked phone number the invite went to), astatus, thedatethey reached it andreminded.Statusis.invited(pending, offers Remind),.joined(counts toward the goal, reward not yet paid) or.rewarded(counts, reward paid).joinedandrewardedfill seats; friends past the goal read as "7 joined" on a full track. - Pass a new
Referralwhenever your backend reports a change and the block animates the difference: the chip moving on, the count rolling, a seat filling. VoiceOver announces a friend joining while the block is on screen, never on the first load. onRemindisasync throwsand runs for oneFriend. Nil hides Remind. Throwing shows the error'serrorDescription(or "Couldn't send a reminder. Try again.") under that row. Setremindedon the friend from your backend so the row keeps reading "Reminded" after a relaunch.- Copying uses
UIPasteboardand sharing usesShareLinkwithSharePreview(the headline, andsharePreviewIconsuch as your app icon when you pass one), so neither needs anything from you.onCopyandonShareare optional and exist for analytics. - Haptics mark a copied code and a sent reminder with success, and a failed reminder with an error.
- VoiceOver reads the headline as a header, the progress as one element ("Friends joined, 2 of 5. 3 to go."), the code row as a button that spells the code out with a Copies the code hint, each friend as their name, status and when, and Remind as its own button. Copying and sent reminders are announced.
- Dynamic Type: the count and "to go", and the code and Copy, stack when they no longer fit side by side, and at accessibility sizes each friend's chip, date and Remind drop under the name. Every row keeps one shape at any width, down to 320 points; long names wrap rather than truncate. Initials stay inside their circle.
- Reduce Motion fades seats in instead of sweeping them, drops the press scale and turns every change into a short fade. The ghost layout holds still instead of breathing.
InviteFriendsBlockExampleat the end ofInviteFriendsBlock.swiftis the Rewards screen in the preview: five invites, two joined, one already reminded, and Priya joining a moment after the screen opens. It reads the theme from the environment; passwrapsInNavigationStack: falseto push it in your own stack.
Theming
- Reads
SPThemefrom@Environment(\.spTheme)and requires thedesign-systemitem. - The card and the list are
color.surfacewithradius.lgand 16 point padding; rows are at least 64 points tall withSPHairlinedividers inseparator, inset to the text; the two groups sit 24 points apart (space.xl). - The headline is the one display moment: the display face (
type.face) at 28 points intextPrimary, capped at semibold. When the face isn't bundled it falls back to SF Pro in the family's figure design, except that a monospaced figure design (Press, Neon) falls back to the text design, so a sentence never sets in mono. The detail, "to go" and Remind's quiet states usetype.subhead; the counttype.subheadsemibold with monospaced digits; namestype.body; dates and the complete linetype.footnote; chipstype.captionsemibold; the list titletype.headline. - The track's seats are
textPrimaryonfill. The code row is a field:fill,radius.md, a 1 pointseparatorstroke, the code intype.bodymonospaced. - Status chips are
success(Joined) andinfo(Rewarded) on a 14% tint of themselves, andtextSecondaryonfill(Invited). Avatars arefillwithtextSecondaryinitials. Failures aredanger; the complete checkmark issuccess. - Share invite is the
accentwithaccentOntext intype.headlineandradius.md, the only accent on the block. - Changes move with
motion.smooth, presses and the Copied swap withmotion.snappy. Every animation becomes a short fade under Reduce Motion.
03
Keep building
Pieces that work well next to this one.