iOS 0.3.4 provides one native picker implementation for SwiftUI and UIKit.
The ready-made flow, customization layers, public component catalogue, scoped
composition, lifecycle recovery, and typed checkout handoff are part of the
tagged Swift package.
Application and SDK ownership
| Native SwiftUI/UIKit owns | SeatLayer SDK owns |
|---|---|
| Event header, filters, floor/section navigation, confirmation, tiers and quantities, cart, hold state, checkout, loading/errors, Test Mode, attribution and Back | Seats, labels, venue geometry, hit testing, pan/pinch, camera, authored 3D, and panorama pixels |
| Dynamic Type, VoiceOver, safe areas, scene lifecycle, native presentation and haptics | Authoritative inventory state and supported map capabilities |
The picker exposes a replaceable native buyer component tree while SeatLayer keeps venue geometry and inventory behavior behind the public SDK API.
Integration ladder
| Path | API | Host ownership |
|---|---|---|
| Ready SwiftUI | SeatLayerPicker |
Configuration and callbacks |
| Ready UIKit | SeatLayerPickerViewController |
Navigation and dismissal |
| Branded picker | Theme, strings, options, styles, builders | Brand or selected complete parts |
| Custom SwiftUI | SeatLayerPickerScope + public views |
Entire SwiftUI hierarchy |
| Custom UIKit | SeatLayerPickerMapView or SeatLayerPickerMapViewController + controller |
Entire UIKit hierarchy |
| Raw map | SeatLayerView |
Every buyer surface and transition |
Ready-picker contract
SeatLayerPicker(
configuration: SeatLayerConfiguration(
event: "ev_your_event_key",
publicKey: "pk_test_your_public_key",
locale: "en",
currency: "USD"
),
onCheckout: { handoff in
try await checkoutBackend.begin(holdId: handoff.holdId)
}
)For private inventory, replace publicKey with a renewable
buyerAccessTokenProvider backed by your server. Return only the short-lived
native buyer-access token and treat the SDK access context as opaque.
UIKit’s SeatLayerPickerViewController hosts the same public component tree.
Call handleBack() from host navigation and use
updateAppearance(theme:themeMode:strings:styles:) for in-place appearance
changes.
Four customization layers
Theme native and map roles together
let theme = SeatLayerPickerTheme(
background: "#0F1522",
surface: "#1A2234",
text: "#EEF1F8",
accent: "#FF6584",
onAccent: "#111827",
map: SeatLayerPickerMapTheme(
background: "#0F1522",
rowLabelColor: "#D7DEEA",
textColor: "#EEF1F8",
selectionColor: "#FF6584"
)
).auto follows the current color scheme without rebuilding the picker.
Style one surface
var styles = SeatLayerPickerStyles()
styles[.checkoutBar] = SeatLayerPickerPartStyle(
background: "#FF6584",
cornerRadius: 18,
horizontalPadding: 12,
verticalPadding: 8
)A style changes appearance, not placement, inventory truth, or hit targets.
Change optional chrome and wording
let options = SeatLayerPickerOptions(
chrome: SeatLayerPickerChromeOptions(floorStrip: false)
)
let strings = SeatLayerPickerStrings(
overrides: [
SeatLayerPickerStringKey.continueWord.rawValue: "Review order"
],
localeIdentifier: "fr-FR"
)The 37 bundled locale dictionaries resolve exact BCP-47 tag, then language, then English. Event and inventory data supplied by SeatLayer stays authoritative.
Wrap or replace one complete part
var builders = SeatLayerPickerBuilders()
builders.header = { context in
AnyView(
context.defaultContent
.background(.ultraThinMaterial)
)
}Every builder receives the current read-only state, controller, presentation state,
resolved style, and canonical defaultContent. Missing or throwing builders
fall back to the stock component. Test Mode and attribution deliberately have
no builder authority.
Build your own SwiftUI hierarchy
SeatLayerPickerScope gives the tree one controller, one presentation model,
and one picker state.
let options = SeatLayerPickerOptions(confirmSelection: true)
SeatLayerPickerScope(options: options) { controller in
ZStack {
SeatLayerPickerMap(
configuration: SeatLayerConfiguration(
event: "ev_your_event_key",
publicKey: "pk_test_your_public_key"
),
options: options,
controller: controller
)
VStack {
SeatLayerPickerHeader()
SeatLayerPickerPriceLegend()
SeatLayerPickerFloorStrip()
Spacer()
SeatLayerPickerDockBar()
SeatLayerPickerCartList()
}
}
}Callbacks observe rather than own the flow. Custom UI invokes typed semantic
controller actions such as focusSection, overview, setFloor,
setCategoryFilter, refreshAvailability, and checkout.
UIKit applications that own all chrome use SeatLayerPickerMapView or
SeatLayerPickerMapViewController, observe pickerController.snapshot, and
call the same public controller actions.
Public component catalogue
The ready picker is composed from these same public surfaces:
| Buyer job | Default components |
|---|---|
| Identity and venue navigation | SeatLayerPickerHeader, price legend, floor selector/strip, section navigator, dock bar |
| Map and filters | SeatLayerPickerMap, map controls, accessibility filters |
| Decisions | Best-seats form, seat confirmation/confirm card, tier choices, GA and table prompts |
| Cart and hold | Cart list/sheet, checkout bar, hold countdown, hold-lapse notice, action error |
| Immersive | SeatLayerVenue3D, SeatLayerSeatViewChrome |
| System states | Loading, retryable/fatal error, empty/sold-out/sales-closed |
| Truth components | SeatLayerPickerTestModeIndicator, SeatLayerPickerAttribution |
There are 25 replaceable builder parts. Test Mode and attribution are public truth components outside that replacement matrix.
State and checkout guarantees
- Picker state always belongs to the active Event and supersedes older state.
- Inventory-changing actions are serialized.
- Repeated Continue taps join one checkout flight.
- Pending tier choice is applied before confirmation and remains authoritative through cart and handoff.
- Ordinary picker state omits
holdId; onlySeatLayerPickerCheckoutHandoffexposes it. - A successful callback transfers hold ownership to the host.
- A rejected callback rejects only that handoff and returns ownership safely.
initialHoldIdrestores a host-owned hold;readOnly: trueblocks mutation through both ready-made and custom controller paths.
Lifecycle and recovery
The ready SwiftUI picker observes scenePhase; UIKit observes application
notifications. Foreground reconciliation consumes hold lapse, unavailable
inventory, and authoritative server hold state before new actions. Custom hosts
use lifecycle, refreshAvailability(), and synchronize().
Back closes the most local state first: prompt → cart → confirmation → focused section → venue → host. Immersive 3D and panorama actions are capability-gated, so unsupported actions are never presented or sent.
Test Mode and required attribution
Custom hosts must render truthful Test Mode state and the bottom-right attribution when required by the SeatLayer account configuration. Chrome options and builders cannot force or suppress it.
Production checklist
- Pin exact Swift package
0.3.4. - Validate SwiftUI and UIKit paths used by your product.
- Exercise Dynamic Type, VoiceOver, RTL, rotation, foreground/background, interactive dismissal, hold lapse, unavailable inventory, and rejected checkout.
- Test real tiers, 3D targets, panorama navigation, Test Mode, and attribution.
- Verify supported physical devices; simulator success is not device proof.
- Confirm the trusted backend inspects and books the handoff hold.
Product scope
The picker selects one Event. It does not accept native Performance Group or Season configuration.