Community-maintained Agent Skills for complete Swift and Apple-platform app delivery.
73
92%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Medium
Suggest reviewing before use
Extended App Intents patterns beyond the basics covered in the main skill.
Covers @Parameter variants, EntityPropertyQuery, assistant schemas, focus
filters, SiriKit migration, error handling, confirmation flows, authentication,
URL-representable types, and Spotlight indexing.
@Parameter Initializer Variants@Parameter(title: "Name")
var name: String
@Parameter(title: "Name", description: "The user's full name")
var name: String
@Parameter(title: "Enabled", default: true)
var enabled: Bool
@Parameter(title: "Website")
var url: URL?@Parameter(title: "Volume", controlStyle: .slider, inclusiveRange: (0.0, 100.0))
var volume: Double
@Parameter(title: "Rating", controlStyle: .stepper, inclusiveRange: (1, 5))
var rating: Int
@Parameter(title: "Temperature", default: 72.0, inclusiveRange: (60.0, 90.0))
var temperature: DoubleNumeric controls default to .stepper. Int supports .stepper and .field;
Double also supports .slider.
Provide a dynamic set of options at runtime:
struct CategoryOptionsProvider: DynamicOptionsProvider {
func results() async throws -> [String] {
await CategoryStore.shared.allNames()
}
}
@Parameter(title: "Category", optionsProvider: CategoryOptionsProvider())
var category: StringRequest clarification when the system cannot resolve a value:
@Parameter(
title: "Size",
requestValueDialog: "What size would you like?",
requestDisambiguationDialog: "Which size did you mean?"
)
var size: CupSizeTransform raw input into the target type:
@Parameter(title: "Contact", resolvers: [ContactResolver()])
var contact: ContactEntitySpecify one custom query for entity resolution when the entity's defaultQuery
is not the right search behavior:
@Parameter(title: "Trail", query: TrailStringQuery())
var trail: TrailEntityUse the current iOS 18+ size: overload when the system UI should enforce a
fixed or bounded number of entity values:
@Parameter(title: "Items", size: .init(exactly: 3))
var items: [ItemEntity]Apple deprecated the original iOS 17 entity-array size: overloads, then added
replacement overloads in iOS 18. Do not treat the argument itself as deprecated.
Use IntentCollectionSize(min:max:) for a range, or omit size: only when the
system UI should allow an unconstrained array.
Validate the count again in perform() at the execution boundary:
guard items.count == 3 else {
throw $items.needsValueError("Choose exactly three items.")
}Apple references:
@Parameter(title: "Document", supportedContentTypes: [.pdf, .plainText])
var document: IntentFile
@Parameter(title: "Image", supportedContentTypes: [.png, .jpeg])
var image: IntentFile?@Parameter(
title: "Distance",
defaultUnit: .miles,
defaultUnitAdjustForLocale: true,
supportsNegativeNumbers: false
)
var distance: Measurement<UnitLength>
@Parameter(title: "Weight", defaultUnit: .kilograms)
var weight: Measurement<UnitMass>
@Parameter(title: "Temperature", defaultUnit: .fahrenheit)
var temp: Measurement<UnitTemperature>Control how parameters connect to Shortcuts input:
@Parameter(title: "Text", inputConnectionBehavior: .connectToPreviousIntentResult)
var text: String
@Parameter(title: "File", inputConnectionBehavior: .optionalIfProvided)
var file: IntentFile?Request values, disambiguation, and confirmation at runtime inside perform():
func perform() async throws -> some IntentResult {
// Request a missing value
if quantity == nil {
throw $quantity.needsValueError("How many would you like?")
}
// Disambiguate among options
let resolved = try await $size.requestDisambiguation(
among: [.small, .medium, .large],
dialog: "Which size?"
)
// Confirm a value
try await $amount.requestConfirmation(for: amount, dialog: "Charge \(amount)?")
return .result()
}Use @IntentParameterDependency when an upstream intent parameter changes the
valid options for another parameter. It is available in iOS 17+ for
DynamicOptionsProvider implementations, including the EntityQuery family.
struct ConfigureProjectWidget: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Project"
@Parameter(title: "Workspace")
var workspace: WorkspaceEntity?
@Parameter(title: "Project")
var project: ProjectEntity?
}
struct ProjectQuery: EntityQuery {
@IntentParameterDependency<ConfigureProjectWidget>(\.$workspace)
private var configuration
func entities(for identifiers: [ProjectEntity.ID]) async throws -> [ProjectEntity] {
try await ProjectStore.shared.projects(identifiedBy: identifiers)
.map(ProjectEntity.init)
}
func suggestedEntities() async throws -> [ProjectEntity] {
guard let workspaceID = configuration?.workspace?.id else {
return []
}
return try await ProjectStore.shared.projects(in: workspaceID)
.map(ProjectEntity.init)
}
func defaultResult() async -> ProjectEntity? {
guard let workspaceID = configuration?.workspace?.id else {
return nil
}
return try? await ProjectStore.shared.recentProject(in: workspaceID)
.map(ProjectEntity.init)
}
}The dependency can be nil while the system is asking for options before the
upstream value is configured. Handle that state deliberately: return an empty list,
or return a small unfiltered set only when those choices remain valid. Dependencies
may read multiple parameters when an option requires more than one parent choice.
EntityQuery inherits defaultResult() through DynamicOptionsProvider. Implement
it only when the app has a stable, helpful default such as a current workspace or a
recent project. Do not silently choose the first fetched entity merely to avoid an
unconfigured parameter.
Apple references:
The most powerful query variant. Declare filterable properties and sortable fields for structured Siri and Shortcuts queries.
enum TrailComparator: Sendable {
case nameContains(String)
case nameEquals(String)
case lengthGreaterThan(Measurement<UnitLength>)
case lengthLessThan(Measurement<UnitLength>)
case lengthEquals(Measurement<UnitLength>)
}
struct TrailPropertyQuery: EntityPropertyQuery {
typealias ComparatorMappingType = TrailComparator
static var properties = QueryProperties {
Property(\TrailEntity.$name) {
ContainsComparator { TrailComparator.nameContains($0) }
EqualToComparator { TrailComparator.nameEquals($0) }
}
Property(\TrailEntity.$trailLength) {
GreaterThanComparator { TrailComparator.lengthGreaterThan($0) }
LessThanComparator { TrailComparator.lengthLessThan($0) }
EqualToComparator { TrailComparator.lengthEquals($0) }
}
}
static var sortingOptions = SortingOptions {
SortableBy(\TrailEntity.$name)
SortableBy(\TrailEntity.$trailLength)
}
func entities(
matching comparators: [TrailComparator],
mode: ComparatorMode,
sortedBy: [EntityQuerySort<TrailEntity>],
limit: Int?
) async throws -> [TrailEntity] {
var results = TrailStore.shared.allTrails.map { TrailEntity(from: $0) }
results = results.filter { trail in
let matches = comparators.map { comparator in
switch comparator {
case .nameContains(let value):
trail.name.localizedCaseInsensitiveContains(value)
case .nameEquals(let value):
trail.name.localizedStandardCompare(value) == .orderedSame
case .lengthGreaterThan(let value):
trail.trailLength > value
case .lengthLessThan(let value):
trail.trailLength < value
case .lengthEquals(let value):
trail.trailLength == value
}
}
guard !matches.isEmpty else { return true }
return mode == .and ? matches.allSatisfy { $0 } : matches.contains(true)
}
if let limit {
results = Array(results.prefix(limit))
}
return results
}
func entities(for identifiers: [Trail.ID]) async throws -> [TrailEntity] {
TrailStore.shared.allTrails
.filter { identifiers.contains($0.id) }
.map { TrailEntity(from: $0) }
}
func suggestedEntities() async throws -> [TrailEntity] {
TrailStore.shared.featured.map { TrailEntity(from: $0) }
}
}The comparator closures map user-supplied values into a query-specific
ComparatorMappingType; entities(matching:) receives those mapped values, not
raw EntityQueryComparator objects.
| Comparator | Supported Types |
|---|---|
EqualToComparator | Equatable properties |
NotEqualToComparator | Equatable properties |
ContainsComparator | Sequence properties |
HasPrefixComparator | String |
HasSuffixComparator | String |
GreaterThanComparator | Comparable properties |
LessThanComparator | Comparable properties |
GreaterThanOrEqualToComparator | Comparable properties |
LessThanOrEqualToComparator | Comparable properties |
IsBetweenComparator | Comparable properties supported by Shortcuts |
.tessl-plugin
skills
accessorysetupkit
references
activitykit
adattributionkit
references
alarmkit
references
app-clips
app-intents
app-store-optimization
app-store-review
apple-on-device-ai
appmigrationkit
audioaccessorykit
references
authentication
references
avkit
background-processing
references
browserenginekit
callkit
references
carplay
cloudkit
contacts-framework
references
core-bluetooth
references
core-data
core-motion
references
core-nfc
references
coreml
references
cryptokit
cryptotokenkit
references
debugging-instruments
device-integrity
references
dockkit
energykit
references
eventkit
financekit
references
focus-engine
gamekit
healthkit
references
homekit
references
ios-accessibility
ios-app-workflow
references
ios-ettrace-performance
ios-localization
ios-memgraph-analysis
ios-networking
ios-simulator
references
metrickit
references
musickit
references
natural-language
references
paperkit
references
passkit
references
pdfkit
pencilkit
references
permissionkit
references
photokit
push-notifications
realitykit
references
relevancekit
references
scenekit
sensorkit
speech-recognition
references
spritekit
storekit
swift-api-design-guidelines
swift-architecture
references
swift-charts
swift-codable
references
swift-code-review
swift-concurrency
swift-formatstyle
references
swift-language
swift-security
references
swift-testing
swiftdata
swiftlint
swiftui-animation
swiftui-gestures
references
swiftui-layout-components
swiftui-liquid-glass
references
swiftui-patterns
swiftui-performance
swiftui-responsive-layout
swiftui-uikit-interop
swiftui-webkit
tabletopkit
tipkit
vision-framework
weatherkit
references