Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.
75
93%
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
Patterns for bridging callback-based, delegate-based, and GCD code into Swift Concurrency.
Use withCheckedContinuation (non-throwing) or withCheckedThrowingContinuation (throwing) to bridge completion-handler APIs into async/await. Available iOS 13+.
Docs: withCheckedContinuation · withCheckedThrowingContinuation
func fetchData() async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
legacyFetch { result in
switch result {
case .success(let data):
continuation.resume(returning: data)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}withCheckedContinuation detects misuse at runtime with diagnostics. Use withUnsafeContinuation only in performance-critical paths after correctness is proven.class LocationBridge: NSObject, CLLocationManagerDelegate {
private var continuation: CheckedContinuation<CLLocation, any Error>?
private let manager = CLLocationManager()
func requestLocation() async throws -> CLLocation {
try await withCheckedThrowingContinuation { continuation in
self.continuation = continuation
manager.delegate = self
manager.requestLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
continuation?.resume(returning: locations[0])
continuation = nil
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
continuation?.resume(throwing: error)
continuation = nil
}
}func fetchWithCancellation() async throws -> Data {
try await withTaskCancellationHandler {
try await withCheckedThrowingContinuation { continuation in
let task = legacyFetch { result in
switch result {
case .success(let data): continuation.resume(returning: data)
case .failure(let error): continuation.resume(throwing: error)
}
}
// Store task for cancellation
}
} onCancel: {
// Cancel the underlying work
}
}For APIs that deliver multiple values over time (delegates, NotificationCenter), use AsyncStream:
func locationUpdates() -> AsyncStream<CLLocation> {
AsyncStream { continuation in
let delegate = StreamingLocationDelegate(continuation: continuation)
continuation.onTermination = { _ in
delegate.stop()
}
delegate.start()
}
}| GCD Pattern | Migration direction |
|---|---|
DispatchQueue.main.async { } | @MainActor isolation or MainActor.run { } |
DispatchQueue.global().async { } | Task { } or Task.detached { } (Swift 6.2: @concurrent) |
DispatchGroup | async let or TaskGroup |
DispatchSemaphore | Actor isolation or AsyncStream |
DispatchWorkItem with cancel | Task with task.cancel() |
DispatchQueue serial queue | actor |
DispatchQueue.concurrentPerform when the surrounding API can become async | withTaskGroup, usually with bounded/chunked child work |
DispatchQueue.concurrentPerform for a measured synchronous CPU-bound parallel-for | Keep concurrentPerform; follow the audit below |
DispatchSource.makeTimerSource | Task.sleep(for:) in a loop, or Clock |
concurrentPerform versus task groupsApple documents DispatchQueue.concurrentPerform as an efficient synchronous
parallel-for: it executes every iteration and waits for them all to finish before
returning. A task group
also waits for its child tasks, but its API is async. Use a task group when the
surrounding operation can be asynchronous. Keep
concurrentPerform
when a caller must remain synchronous and measurement shows that independent,
finite CPU work benefits from a parallel-for. Finite CPU computation does not by
itself violate the cooperative executor's
forward-progress requirement.
The API is declared @preconcurrency, but its closure parameter is @Sendable.
Under Swift 6 complete checking, direct captures of both
UnsafeBufferPointer
and UnsafeMutableBufferPointer
are rejected because neither buffer view is Sendable. When the compiler cannot
express a manually proven pointer invariant, confine nonisolated(unsafe) to the
local base-pointer bindings captured by the closure:
func doubled(_ input: UnsafeBufferPointer<Int>) -> [Int] {
guard !input.isEmpty else { return [] }
return Array(unsafeUninitializedCapacity: input.count) { output, initializedCount in
nonisolated(unsafe) let inputBase = input.baseAddress!
nonisolated(unsafe) let outputBase = output.baseAddress!
// SAFETY: concurrentPerform joins before return. Iteration i reads only
// inputBase[i] and initializes only outputBase[i]; the ranges do not
// alias, both contain input.count elements, and both remain valid for
// the entire loop.
DispatchQueue.concurrentPerform(iterations: input.count) { index in
outputBase.advanced(by: index).initialize(
to: inputBase[index] * 2
)
}
initializedCount = input.count
}
}Before accepting this opt-out, require one adjacent // SAFETY: proof that
covers:
Disjoint ranges are a nonconflicting-access invariant, not synchronization.
Input/output aliasing is allowed only when the access proof remains
nonconflicting. For a same-base in-place transform, prove that iteration i
reads element i before writing element i, touches no other element, and that
the read/write sets for iterations i and j do not overlap when i != j.
Same pointer identity alone proves neither safety nor unsafety; shifted,
neighboring, strided, or tiled access requires a fresh alias and range proof.
Never widen the opt-out to a buffer view, enclosing type, or unrelated shared
state.
concurrentPerform does not automatically participate in Swift task
cancellation. If cancellation is required, design an explicit thread-safe
signal and define partial-output semantics, or move the operation behind an
async API.
Before retaining this carve-out:
These are engineering checks, not Apple API guarantees. See the supplemental Swift Forums discussion for the original strict-concurrency use case.
// Before (GCD)
let group = DispatchGroup()
for url in urls {
group.enter()
fetch(url) { _ in group.leave() }
}
group.notify(queue: .main) { updateUI() }
// After (Swift Concurrency)
let results = await withTaskGroup(of: Data?.self) { group in
for url in urls {
group.addTask { try? await fetch(url) }
}
return await group.reduce(into: [Data]()) { if let d = $1 { $0.append(d) } }
}
updateUI(results)// Before
let serialQueue = DispatchQueue(label: "com.app.cache")
serialQueue.async { self.cache[key] = value }
// After
actor Cache {
private var storage: [String: Data] = [:]
func set(_ key: String, _ value: Data) { storage[key] = value }
func get(_ key: String) -> Data? { storage[key] }
}.tessl-plugin
skills
accessorysetupkit
references
activitykit
references
adattributionkit
references
alarmkit
references
app-clips
app-intents
app-store-optimization
app-store-review
apple-on-device-ai
appmigrationkit
references
audioaccessorykit
references
authentication
references
avkit
references
background-processing
references
browserenginekit
references
callkit
references
carplay
references
cloudkit
references
contacts-framework
references
core-bluetooth
references
core-data
core-motion
references
core-nfc
references
coreml
references
cryptokit
references
cryptotokenkit
references
debugging-instruments
device-integrity
references
dockkit
references
energykit
references
eventkit
references
financekit
references
focus-engine
gamekit
references
healthkit
references
homekit
references
ios-accessibility
ios-ettrace-performance
ios-localization
ios-memgraph-analysis
ios-networking
ios-simulator
references
mapkit
metrickit
references
musickit
references
natural-language
references
paperkit
references
passkit
references
pdfkit
references
pencilkit
references
permissionkit
references
photokit
push-notifications
realitykit
references
relevancekit
references
scenekit
references
sensorkit
references
speech-recognition
references
spritekit
references
storekit
swift-api-design-guidelines
swift-architecture
references
swift-charts
references
swift-codable
references
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-uikit-interop
swiftui-webkit
tabletopkit
references
tipkit
references
vision-framework
weatherkit
references
widgetkit
references