Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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.
.claude/skills/rshankras-whats-new/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | 36% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-12 | ✓→✓ | = Same ✓ | 66% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 79% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 77% | 0% |
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:
.sheet(item:) auto-dismiss).fullScreenCover)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)
swift@main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() .whatsNewSheet() // Automatically shows after updates } } }
Option 2: Manual Control in Root View
swift@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
swiftstruct 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... } } }
swift// 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... ]
swift@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:
swift// 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:
swiftif let release = provider.release(for: currentVersion), tracker.shouldShowWhatsNew() { // Present What's New }
Fetch features from a server to update without app releases:
swiftlet 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:swiftfunc 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)Other measured skills in the registry, with their headline benchmark lift.