Pattern · Prompt Input
NewModel Composer SwiftUI Block
A message composer with a model menu, Search, Think and Image chips, and a usage line that warns near the limit.
A composer pinned above the keyboard with a model pill whose menu describes each model and marks locked ones as Pro, mode chips that toggle, and a line of messages left that turns to the warning colour near the limit. At the limit the composer turns off and offers Upgrade; the app owns the model and modes through bindings.
- SwiftUI
- iOS 17.0+
- composer-modes
$199 once, every piece included. See how installing works · Sign in
01
What's included
SwiftUI source you own, built on Apple frameworks only.
- A model menu with a line about each model and Pro on locked ones
- Search, Think and Image chips that show their name when on
- "12 messages left today", warning near the limit
- Turns off at the limit with Upgrade
- Bindings for model and modes, and Send that turns into Stop
02
Implementation notes
How it behaves, what it handles, and the decisions behind it.
The composer is one card pinned above the keyboard: a field that grows from one line to six, then a row with the model pill, the mode chips and Send. The pill names the current model; its menu lists every model with a line about it, a check on the current one and a lock with "Pro" on the ones the plan doesn't include, which call your closure instead of switching. Mode chips are hairline rings around a symbol while off and fill with their name when on, and any number can be on. Under the card a line reads "12 messages left today"; from five left it turns to the warning colour with a symbol, and at the limit the field and controls turn off, the prompt reads "You've reached today's limit" and the line says when more arrive, with Upgrade at its end. While your onSend runs Stop takes Send's place, and a failed send hands the text back with the error under the card.
Notes
ModelComposer(model:modes:models:availableModes:usage:placeholder:onLocked:onUpgrade:onSend:). It is a section: no navigation stack, background, padding or scroll view of its own. Pin it with.safeAreaInset(edge: .bottom)and.padding(.horizontal, 20)on atheme.color.bgbackground.modelis aBinding<ModelComposer.Model.ID>andmodesaBinding<Set<ModelComposer.Mode.ID>>, so your app owns both and can keep them per conversation. An id that isn't inmodelsfalls back to the first unlocked model.ModelComposer.Modelis anid,name, one-linedetailandisLocked.Model.samplesis Atlas 3 Mini, Atlas 3 and Atlas 3 Max, the last locked. Picking a locked model leaves the binding alone and callsonLockedwith it; show your upgrade there.ModelComposer.Modeis anid,titleand SF Symbol..search,.thinkand.imageare ready andMode.defaultslists them. Pass an emptyavailableModesto hide the chips.ModelComposer.Usageisremaining,limit, an optionalresetsdate andwarnAt(5 by default), withisLowandisExhausted. The line reads "12 messages left today", or "12 messages left until 9:00 AM" with a reset time, and "You've used all 40 messages. More at 9:00 AM." at the limit. The block never counts: hand it a newUsageafter each message. Nil hides the line and never limits.onUpgradeshows Upgrade at the end of the line once the limit is reached. Nil hides it.onSendreceivesModelComposer.Message: the text, trimmed, theModeland theModes that were on, in chip order. Stop cancels the task and frees the composer at once. Any other thrown error shows itserrorDescription, or "Couldn't send. Try again.", and puts the text back.- At accessibility text sizes the chips keep to their symbols so the row stays on one line; the pill and chips keep 44 point targets.
- Haptics: a selection tick when the model or a mode changes, a light impact on Send, an error when a send fails.
- VoiceOver reads the pill as "Model" with the current name, each chip by name with On or Off and the selected trait, and the usage line as a sentence. Model changes, "Stopped" and failures are announced.
- Reduce Motion turns the chips' change and the count's roll into short fades.
ModelComposerExampleat the end ofModelComposer.swiftis the Research thread from the preview: Atlas 3 with 12 of 40 messages left, counting down with each message, and an upgrade prompt from the locked model or from Upgrade. PasswrapsInNavigationStack: falseto push it in your own stack.
Theming
- Reads
SPThemefrom@Environment(\.spTheme)and requires thedesign-systemitem. - The card is
color.surfacewithradius.lgand a 1 pointseparatorhairline that turns totextPrimarywhile the field is focused. The field istype.bodyintextPrimary, its prompttextTertiary. - The model pill is a
fillcapsule with its name intype.subheadsemiboldtextPrimaryand the chevrons intextSecondary. Chips are 32 point capsules: off, aseparatorring around atextSecondarysymbol; on,fillwith the filled symbol and the name intype.subheadmediumtextPrimary. - Send is the accent: an
accentcircle with anaccentOnarrow,fillwithtextTertiarywhile off. At the limit Send is off and Upgrade, intype.subheadsemiboldaccent, is the one accent left. - The usage line is
type.footnotewith monospaced digits intextSecondary, turning towarningnear and at the limit. Errors aretype.footnoteindanger. - State changes use
motion.smoothandmotion.snappy, and become short fades under Reduce Motion.
03
Keep building
Pieces that work well next to this one.