From apple-dev
Generates a "What's New" / changelog screen shown after app updates with version tracking, feature highlights, and one-time display per version. Use when user wants release notes UI, update notifications, or feature announcements.
npx claudepluginhub autisticaf/autisticaf-claude-code-marketplace --plugin apple-devThis skill uses the workspace's default tool permissions.
> **First step:** Tell the user: "generators-whats-new skill loaded."
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
First step: Tell the user: "generators-whats-new skill loaded."
Generate a complete "What's New" screen that displays after app updates — highlights new features, changes, and improvements with version tracking to ensure it only shows once per update.
Use this skill when the user:
Bundle.main.infoDictionary, custom build config, etc.)Search for existing what's new or changelog implementations:
Glob: **/*WhatsNew*.swift, **/*Changelog*.swift, **/*ReleaseNotes*.swift, **/*VersionTracker*.swift
Grep: "WhatsNew" or "whatsNew" or "lastShownVersion" or "changelog"
If found, ask user:
Detect how the app reads its version:
Grep: "CFBundleShortVersionString" or "Bundle.main.infoDictionary" or "appVersion"
Use whichever pattern the project already employs. If none found, default to Bundle.main.infoDictionary?["CFBundleShortVersionString"].
Ask user via AskUserQuestion:
Presentation style?
.sheet(item:) auto-dismiss).fullScreenCover)Content source?
Dismiss behavior?
Page indicators?
Read templates.md for production Swift code.
Generate these files:
WhatsNewFeature.swift — Model for a single feature (title, description, SF Symbol, tint color)WhatsNewRelease.swift — Groups features by version string with dateVersionTracker.swift — Tracks last-shown version in UserDefaults, compares against current bundle versionWhatsNewProvider.swift — Protocol + local implementation (optional remote)WhatsNewView.swift — Paged view with TabView(.page) showing featuresWhatsNewSheet.swift — Wrapper that auto-presents via .sheet(item:) when new version detectedBased on configuration:
RemoteWhatsNewProvider.swift — If remote content source selectedWhatsNewJSON.swift — If local JSON source selectedCheck project structure:
Sources/ exists -> Sources/WhatsNew/App/ exists -> App/WhatsNew/WhatsNew/After generation, provide:
WhatsNew/
├── WhatsNewFeature.swift # Single feature model
├── WhatsNewRelease.swift # Version-grouped features
├── VersionTracker.swift # Version persistence & comparison
├── WhatsNewProvider.swift # Content provider protocol + local impl
├── WhatsNewView.swift # Paged feature display
├── WhatsNewSheet.swift # Auto-presenting sheet wrapper
└── RemoteWhatsNewProvider.swift # Remote content (optional)
Option 1: View Modifier Style (Recommended)
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.whatsNewSheet() // Automatically shows after updates
}
}
}
Option 2: Manual Control in Root View
@main
struct MyApp: App {
@State private var whatsNewRelease: WhatsNewRelease?
var body: some Scene {
WindowGroup {
ContentView()
.sheet(item: $whatsNewRelease) { release in
WhatsNewView(release: release)
}
.task {
let tracker = VersionTracker()
if tracker.shouldShowWhatsNew() {
whatsNewRelease = WhatsNewProvider.local.latestRelease()
}
}
}
}
}
Option 3: Inline Embedding
struct HomeView: View {
@State private var tracker = VersionTracker()
var body: some View {
VStack {
if tracker.shouldShowWhatsNew(),
let release = WhatsNewProvider.local.latestRelease() {
WhatsNewView(release: release) {
tracker.markVersionAsShown()
}
}
// Rest of home content...
}
}
}
// In LocalWhatsNewProvider.swift — add a new entry per release
static let releases: [WhatsNewRelease] = [
WhatsNewRelease(
version: "2.1.0",
date: Date(timeIntervalSince1970: 1_700_000_000),
features: [
WhatsNewFeature(
title: "Dark Mode Support",
description: "Full dark mode across all screens.",
systemImage: "moon.fill",
tintColor: .indigo
),
WhatsNewFeature(
title: "Faster Search",
description: "Search results now appear instantly.",
systemImage: "magnifyingglass",
tintColor: .orange
),
]
),
// Previous releases...
]
@Test
func showsWhatsNewForNewVersion() {
let defaults = UserDefaults(suiteName: "TestWhatsNew")!
defaults.removePersistentDomain(forName: "TestWhatsNew")
let tracker = VersionTracker(
defaults: defaults,
currentVersion: "2.0.0"
)
// First launch — no previous version stored
#expect(tracker.shouldShowWhatsNew() == true)
tracker.markVersionAsShown()
#expect(tracker.shouldShowWhatsNew() == false)
}
@Test
func skipsWhatsNewWhenVersionUnchanged() {
let defaults = UserDefaults(suiteName: "TestWhatsNew2")!
defaults.removePersistentDomain(forName: "TestWhatsNew2")
let tracker = VersionTracker(
defaults: defaults,
currentVersion: "1.5.0"
)
tracker.markVersionAsShown()
// Same version — should not show
let tracker2 = VersionTracker(
defaults: defaults,
currentVersion: "1.5.0"
)
#expect(tracker2.shouldShowWhatsNew() == false)
}
@Test
func showsWhatsNewAfterUpdate() {
let defaults = UserDefaults(suiteName: "TestWhatsNew3")!
defaults.removePersistentDomain(forName: "TestWhatsNew3")
let tracker = VersionTracker(
defaults: defaults,
currentVersion: "1.0.0"
)
tracker.markVersionAsShown()
// Simulate update
let trackerAfterUpdate = VersionTracker(
defaults: defaults,
currentVersion: "2.0.0"
)
#expect(trackerAfterUpdate.shouldShowWhatsNew() == true)
}
Group features by release version so older users who skipped updates still see relevant changes:
// Show features for all versions newer than lastShownVersion
func featuresSinceLastShown() -> [WhatsNewFeature] {
let lastShown = tracker.lastShownVersion ?? "0.0.0"
return releases
.filter { $0.version.compare(lastShown, options: .numeric) == .orderedDescending }
.flatMap(\.features)
}
Only show if there are actually features to display for the current version:
if let release = provider.release(for: currentVersion),
tracker.shouldShowWhatsNew() {
// Present What's New
}
Fetch features from a server to update without app releases:
let provider = RemoteWhatsNewProvider(
endpoint: URL(string: "https://api.example.com/whats-new")!,
fallback: LocalWhatsNewProvider() // Offline fallback
)
let release = try await provider.latestRelease()
CFBundleShortVersionString = marketing version (e.g., "2.1.0") — use this for What's NewCFBundleVersion = build number (e.g., "47") — changes every build, NOT suitable for What's New trackinglastShownVersion is nillastShownVersion is nil, either skip What's New or set the current version without showing:func shouldShowWhatsNew() -> Bool {
guard let lastShown = lastShownVersion else {
// First install — mark current version, don't show
markVersionAsShown()
return false
}
return currentVersion.compare(lastShown, options: .numeric) == .orderedDescending
}
.numeric option in String.compare(_:options:) so "2.0.0" > "1.10.0" (not lexicographic)< or > operators directly — "9.0" would sort after "10.0" lexicographically.accessibilityElement(children: .combine) on feature cardsgenerators-onboarding-generator — First-launch onboarding (complementary — onboarding for first install, What's New for updates)