Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.
71
89%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Advisory
Suggest reviewing before use
Low-level synchronization tools for protecting shared mutable state when actors
are not the right fit. All primitives discussed here are Sendable and safe
to use from multiple threads.
Module: Synchronization · Availability: iOS 18.0+
Mutex<Value> is a synchronization primitive that protects shared mutable
state via mutual exclusion. It blocks threads attempting to acquire the lock,
ensuring only one execution context accesses the protected value at a time.
Documentation: sosumi.ai/documentation/synchronization/mutex
import Synchronization
class ImageCache: Sendable {
let storage = Mutex<[String: UIImage]>([:])
func image(forKey key: String) -> UIImage? {
storage.withLock { $0[key] }
}
func store(_ image: UIImage, forKey key: String) {
storage.withLock { $0[key] = image }
}
func removeAll() {
storage.withLock { $0.removeAll() }
}
}Use withLockIfAvailable to attempt acquisition without blocking. Returns
nil if the lock is already held.
let counter = Mutex<Int>(0)
// Non-blocking attempt — returns nil if lock is contended
if let value = counter.withLockIfAvailable({ $0 }) {
print("Current count: \(value)")
} else {
print("Lock was busy, skipping")
}Value: The protected state is stored inside the mutex,
making it clear what the lock protects.Sendable: Mutex conforms to Sendable, so it can be stored in
Sendable types (classes, actors, global state).Mutex that you already hold on the
same thread is undefined behavior.await inside withLock. The lock is held for
the duration of the closure — blocking across a suspension point will
deadlock or starve other threads.Module: os · Availability: iOS 16.0+
OSAllocatedUnfairLock<State> wraps os_unfair_lock in a safe Swift API.
It heap-allocates the underlying lock, avoiding the unsound address-of
problem that makes raw os_unfair_lock unusable from Swift.
Documentation: sosumi.ai/documentation/os/osallocatedunfairlock
import os
enum LoadState: Sendable {
case idle
case loading
case complete(Data)
case failed(Error)
}
final class ResourceLoader: Sendable {
let state = OSAllocatedUnfairLock(initialState: LoadState.idle)
func beginLoading() {
state.withLock { $0 = .loading }
}
func completeLoading(with data: Data) {
state.withLock { $0 = .complete(data) }
}
var currentState: LoadState {
state.withLock { $0 }
}
}When protecting external state or a code section rather than a specific value:
let lock = OSAllocatedUnfairLock()
lock.withLock {
// Critical section — no associated state
writeToSharedFile(data)
}Available but discouraged. Must unlock from the same thread that locked.
Never use across await suspension points.
lock.lock()
defer { lock.unlock() }
// Critical sectionMutex<Value> | OSAllocatedUnfairLock<State> | |
|---|---|---|
| Availability | iOS 18+ | iOS 16+ |
| Module | Synchronization | os |
| State model | Value stored inside lock (generic Value) | Optional state via initialState: |
withLockIfAvailable | Returns nil on contention | Returns nil on contention |
| Ownership assertions | Not available | precondition(.owner) / precondition(.notOwner) |
| Manual lock/unlock | Not available | Available (lock() / unlock()) |
| Recommendation | Preferred for iOS 18+ code | Use when targeting iOS 16–17 |
Guideline: Use Mutex for new code targeting iOS 18+. Use
OSAllocatedUnfairLock when you need to support iOS 16–17, or when you need
ownership assertions for debugging.
Module: Synchronization · Availability: iOS 18.0+
Atomic<Value> provides lock-free atomic operations on values conforming to
AtomicRepresentable. Use atomics for simple counters, flags, and
compare-and-swap patterns where a full lock would be overkill.
Documentation: sosumi.ai/documentation/synchronization/atomic
import Synchronization
final class RequestTracker: Sendable {
let activeRequests = Atomic<Int>(0)
func beginRequest() {
activeRequests.wrappingAdd(1, ordering: .relaxed)
}
func endRequest() {
activeRequests.wrappingSubtract(1, ordering: .relaxed)
}
var count: Int {
activeRequests.load(ordering: .relaxed)
}
}let isShutdown = Atomic<Bool>(false)
func shutdown() {
let (exchanged, _) = isShutdown.compareExchange(
expected: false,
desired: true,
ordering: .acquiringAndReleasing
)
guard exchanged else { return } // Already shut down
performCleanup()
}Atomic operations require an explicit memory ordering:
| Ordering | Use case |
|---|---|
.relaxed | Counters, statistics — no ordering guarantees needed |
.acquiring | Read that must see all writes before a corresponding release |
.releasing | Write that must be visible to a corresponding acquire |
.acquiringAndReleasing | Compare-and-swap, read-modify-write |
.sequentiallyConsistent | Strongest guarantee — rarely needed |
Guideline: Use .relaxed for simple counters. Use
.acquiringAndReleasing for compare-and-swap patterns. Avoid
.sequentiallyConsistent unless you have a proven need — it is the most
expensive ordering.
// GOOD: Actor for a cache accessed from async contexts
actor ImageDownloader {
private var cache: [URL: UIImage] = [:]
func image(for url: URL) async throws -> UIImage {
if let cached = cache[url] { return cached }
let (data, _) = try await URLSession.shared.data(from: url)
let image = UIImage(data: data)!
cache[url] = image
return image
}
}Task and introduces
unwanted asynchrony.Atomic<Int> or Atomic<Bool> is cheaper and
simpler than creating an actor for a single value.// GOOD: Mutex for synchronous, high-frequency access
final class MetricsCollector: Sendable {
let metrics = Mutex<[String: Int]>([:])
// Called from tight loops, C callbacks, or synchronous code
func increment(_ key: String) {
metrics.withLock { $0[key, default: 0] += 1 }
}
func snapshot() -> [String: Int] {
metrics.withLock { $0 }
}
}Need shared mutable state protection?
├── Can all access be async?
│ ├── Yes → Use an actor
│ └── No → Use Mutex or OSAllocatedUnfairLock
├── Single scalar value (counter, flag)?
│ └── Use Atomic<Value>
├── Performance-critical (nanosecond-level)?
│ └── Use Mutex or Atomic
└── Bridging C/ObjC callbacks?
└── Use Mutex or OSAllocatedUnfairLockNever put locks inside actors. An actor already serializes access; adding a lock creates double synchronization and risks deadlocks.
// WRONG: Lock inside an actor — double synchronization
actor BadCache {
let lock = Mutex<[String: Data]>([:]) // Unnecessary!
// The actor already protects its state
}
// CORRECT: Just use the actor's built-in isolation
actor GoodCache {
var cache: [String: Data] = [:]
func store(_ data: Data, key: String) {
cache[key] = data
}
}Never use DispatchSemaphore or NSLock in modern Swift. Use Mutex
(iOS 18+) or OSAllocatedUnfairLock (iOS 16+) instead. Legacy lock types
have no Sendable conformance and are not designed for structured
concurrency.
Never hold a lock across await. This blocks the thread and can deadlock
the cooperative thread pool.
// WRONG: Holding lock across suspension point
mutex.withLock { value in
value = await fetchData() // DEADLOCK RISK
}
// CORRECT: Fetch first, then lock to update
let data = await fetchData()
mutex.withLock { value in
value = data
}skills
accessorysetupkit
references
activitykit
references
adattributionkit
references
alarmkit
references
app-clips
app-intents
references
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-localization
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
spritekit
references
storekit
swift-api-design-guidelines
swift-architecture
swift-charts
references
swift-codable
swift-concurrency
swift-formatstyle
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