uikit-expert — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited uikit-expert (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Favor native APIs and Apple's documented guidance. Operating principles: facts over architecture (no MVVM/VIPER/Coordinator mandates, but do separate business logic for testability); correctness first, then performance; reserve "always"/"never" for correctness, use "suggest"/"consider" for optional optimizations.
All three modes draw on the same Core Guidelines below; each topic links to its reference file there. Pick the entry point:
viewDidLoad, geometry in viewIsAppearing[weak self], Task cancellation), concurrency (@MainActor)#available and sensible fallbacksreferences/view-controller-lifecycle.mdviewDidLoad for one-time setup: subviews, constraints, delegates — NOT geometryviewIsAppearing (back-deployed iOS 13+) for geometry-dependent work, trait-based layout, scroll-to-itemviewDidLayoutSubviews fires multiple times — use only for lightweight layer frame adjustmentsviewWillAppear is limited to transition coordinator animations and balanced notification registrationsuper in every lifecycle overrideaddChild → addSubview → didMove(toParent:) — in that exact orderdeinit logging during developmentreferences/auto-layout.mdtranslatesAutoresizingMaskIntoConstraints = false on programmatic viewsNSLayoutConstraint.activate([]) — never individual .isActive = trueisActive or modify .constant — never remove and recreate.required (1000) at runtime — use 999layoutIfNeeded() inside animation block on superview.flushUpdates option to simplify constraint animationconstraint.identifier on key constraints so Unable to simultaneously satisfy constraints logs are readablelet label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false // required on programmatic views
view.addSubview(label)
NSLayoutConstraint.activate([ // one batch = one solve pass
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])references/modern-collection-views.md, references/cell-configuration.md, references/list-performance.mdUICollectionViewDiffableDataSource with stable identifiers (UUID/database ID, not full model structs)reconfigureItems for content updates, reloadItems only when cell type changesapplySnapshotUsingReloadData for initial population (bypasses diffing)UICollectionViewCompositionalLayout for any non-trivial layoutUICollectionView.CellRegistration — no string identifiers, no manual castingUIContentConfiguration for cell content and UIBackgroundConfiguration for cell backgroundsconfigurationUpdateHandler for state-driven styling (selection, highlight)// Store the registration once — never create it inside the cell provider (crashes on iOS 15+).
private lazy var cellRegistration = UICollectionView.CellRegistration<PhotoCell, Photo> { cell, _, photo in
cell.configure(with: photo)
}
// Data source is keyed by the stable ID, so the provider receives an id — look up the model, then configure with it.
dataSource = .init(collectionView: collectionView) { [weak self] cv, indexPath, id in
guard let self, let photo = self.photo(for: id) else { return nil }
return cv.dequeueConfiguredReusableCell(using: self.cellRegistration, for: indexPath, item: photo)
}
var snapshot = NSDiffableDataSourceSnapshot<Section, Photo.ID>()
snapshot.appendSections([.main])
snapshot.appendItems(photos.map(\.id))
dataSource.apply(snapshot, animatingDifferences: true)references/navigation-patterns.mdUINavigationBarAppearance slots (standard, scrollEdge, compact, compactScrollEdge)navigationItem (per-VC) in viewDidLoad, not on navigationBar in viewWillAppearsetViewControllers(_:animated:) for deep links — not sequential push callstransitionCoordinator before push/popprefersLargeTitles once on the bar; use largeTitleDisplayMode per VCreferences/animation-patterns.mdUIView.animate — simple one-shot animations; check finished in completionUIViewPropertyAnimator — gesture-driven, interruptible; respect state machine (inactive → active → stopped)CABasicAnimation — layer-only properties (cornerRadius, shadow, 3D transforms); set model value firstUIView.animate(springDuration:bounce:) aligns with SwiftUIlayoutIfNeeded() on superviewreferences/memory-management.md[weak self] in all escaping closures[weak self], invalidate in viewWillDisappear[weak self] in closure, remove observer in deinit[weak self] in stored inner closuresweak var delegate: SomeDelegate? with AnyObject constraintdeinit — if never called, a retain cycle existsreferences/concurrency-main-thread.mdUIViewController is @MainActor — all subclass methods are implicitly main-actorTask references, cancel in viewDidDisappear — not deinitTask.isCancelled before UI updates after awaitTask.detached does NOT inherit actor isolation — explicit MainActor.run needed for UIDispatchQueue.main.sync from background — use await MainActor.runreferences/uikit-swiftui-interop.mdaddChild → addSubview → didMove), retain as stored propertysizingOptions = .intrinsicContentSize (iOS 16+) for Auto Layout containersupdateUIView, not makeUIView; guard against update loopsreferences/image-loading.mdbyPreparingThumbnail(of:) or prepareForDisplay() for async decodingprepareForReuse, clear image, verify identity on completionreferences/keyboard-scroll.mdUIKeyboardLayoutGuide (iOS 15+) — pin content bottom to view.keyboardLayoutGuide.topAnchorfollowsUndockedKeyboard = true for floating keyboardsreferences/adaptive-appearance.mdregisterForTraitChanges (iOS 17+) instead of deprecated traitCollectionDidChangeUIFont.preferredFont(forTextStyle:) + adjustsFontForContentSizeCategory = true.label, .systemBackground); re-resolve CGColor on trait changesaccessibilityLabel, accessibilityTraits, accessibilityHint on custom viewsUIAccessibilityCustomAction for complex list item actions| Deprecated / Legacy | Modern Replacement | Since |
|---|---|---|
traitCollectionDidChange | registerForTraitChanges(_:handler:) | iOS 17 |
| Keyboard notifications | UIKeyboardLayoutGuide | iOS 15 |
cell.textLabel / detailTextLabel | UIListContentConfiguration | iOS 14 |
register + string dequeue | UICollectionView.CellRegistration | iOS 14 |
reloadItems on snapshot | reconfigureItems | iOS 15 |
barTintColor / isTranslucent | UINavigationBarAppearance (4 slots) | iOS 13 |
UICollectionViewFlowLayout (complex) | UICollectionViewCompositionalLayout | iOS 13 |
Manual layoutIfNeeded() in animations | .flushUpdates option | iOS 26 |
| Legacy app lifecycle | UIScene + SceneDelegate | Mandatory iOS 26 |
ObservableObject + manual invalidation | @Observable + UIObservationTrackingEnabled | iOS 18 |
One gate per domain — the correctness items to verify (details in the matching Core Guideline above):
viewDidLoad (use viewIsAppearing); every override calls super; child VC = addChild → addSubview → didMove; deinit confirms dealloctranslatesAutoresizingMaskIntoConstraints = false; NSLayoutConstraint.activate([]); no churn (toggle isActive/.constant); no setNeedsLayout() inside layoutSubviews (infinite loop)reconfigureItems not reloadItems; no duplicate snapshot IDs (BUG_IN_CLIENT crash); CellRegistration not string dequeuenavigationItem not navigationBar; concurrent-transition guardCAAnimation sets model value first[weak self] in escaping closures; Tasks cancelled in viewDidDisappear; delegates weak; no strong-self recapture in nested closuresTask.isCancelled checked after await; no DispatchQueue.main.sync from background; no redundant @MainActor on VC subclassesprepareForReuse + verifies identity; NSCache sized by decoded bytesUIHostingController retained + full containment; updateUIView guards update loopsUIKeyboardLayoutGuide, not notificationsregisterForTraitChanges; Dynamic Type; CGColor re-resolved on trait change; accessibilityLabel/Traits on custom views#available guards with fallbacks; UIScene lifecycle; consider UIObservationTrackingEnabledreferences/view-controller-lifecycle.md — Lifecycle ordering, viewIsAppearing, child VC containmentreferences/auto-layout.md — Batch activation, constraint churn, priority, animation, debuggingreferences/modern-collection-views.md — Diffable data sources, compositional layout, CellRegistrationreferences/cell-configuration.md — UIContentConfiguration, UIBackgroundConfiguration, configurationUpdateHandlerreferences/list-performance.md — Prefetching, cell reuse, reconfigureItems, scroll performancereferences/navigation-patterns.md — Bar appearance, concurrent transitions, large titles, deep linksreferences/animation-patterns.md — UIView.animate, UIViewPropertyAnimator, CAAnimation, springsreferences/memory-management.md — Retain cycles, [weak self], Timer/CADisplayLink/nested closure trapsreferences/concurrency-main-thread.md — @MainActor, Task lifecycle, Swift 6, GCD migrationreferences/uikit-swiftui-interop.md — UIHostingController, UIViewRepresentable, sizing, state bridgingreferences/image-loading.md — Downsampling, decoded bitmap math, cell reuse race conditionreferences/keyboard-scroll.md — UIKeyboardLayoutGuide, scroll view insets, iPad floating keyboardreferences/adaptive-appearance.md — Trait changes, Dynamic Type, dark mode, VoiceOver, accessibilityreferences/modern-uikit-apis.md — Observation framework, updateProperties(), .flushUpdates, UIScene, Liquid Glass~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.