본문으로 건너뛰기
cccskills
무료GitHub에 공개

whats-new

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.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

설치 방법 보기

포함된 파일(2)

  • SKILL.md10.7 KB
  • templates.md19.2 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

What's New Generator

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.

When This Skill Activates

Use this skill when the user:

  • Asks to "add a what's new screen" or "show what's new"
  • Mentions "changelog" or "changelog UI"
  • Wants an "app update screen" or "update notification"
  • Asks about "new features screen" or "feature announcements"
  • Mentions "release notes UI" or "release notes screen"
  • Wants a "version update notification" or "post-update screen"

Pre-Generation Checks

1. Project Context Detection

  • Check deployment target (iOS 16+ / macOS 13+ minimum; iOS 17+ / macOS 14+ for @Observable)
  • Check Swift version (requires Swift 5.9+)
  • Identify source file locations and project structure
  • Determine how app version is accessed (Bundle.main.infoDictionary, custom build config, etc.)

2. Conflict Detection

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:

  • Replace existing implementation?
  • Keep existing, integrate alongside?

3. Version Access Pattern

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"].

Configuration Questions

Ask user via AskUserQuestion:

  1. Presentation style?

    • Sheet (recommended — .sheet(item:) auto-dismiss)
    • Full-screen cover (.fullScreenCover)
    • Inline (embedded in a view hierarchy)
  2. Content source?

    • Hardcoded in Swift (simplest, no network needed)
    • Local JSON file bundled in app
    • Remote JSON endpoint (fetched on launch)
  3. Dismiss behavior?

    • "Continue" button (explicit acknowledgment)
    • Swipe to dismiss (sheet default)
    • Both (button + swipe)
  4. Page indicators?

    • Dot indicators (TabView page style)
    • Page count label ("1 of 3")
    • None (single scrollable view)

Generation Process

Step 1: Read Templates

Read templates.md for production Swift code.

Step 2: Create Core Files

Generate these files:

  1. WhatsNewFeature.swift — Model for a single feature (title, description, SF Symbol, tint color)
  2. WhatsNewRelease.swift — Groups features by version string with date
  3. VersionTracker.swift — Tracks last-shown version in UserDefaults, compares against current bundle version
  4. WhatsNewProvider.swift — Protocol + local implementation (optional remote)

Step 3: Create UI Files

  1. WhatsNewView.swift — Paged view with TabView(.page) showing features
  2. WhatsNewSheet.swift — Wrapper that auto-presents via .sheet(item:) when new version detected

Step 4: Create Optional Files

Based on configuration:

  • RemoteWhatsNewProvider.swift — If remote content source selected
  • WhatsNewJSON.swift — If local JSON source selected

Step 5: Determine File Location

Check project structure:

  • If Sources/ exists -> Sources/WhatsNew/
  • If App/ exists -> App/WhatsNew/
  • Otherwise -> WhatsNew/

Output Format

After generation, provide:

Files Created

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)

Integration Steps

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...
        }
    }
}

Adding New Releases

// 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...
]

Testing

@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)
}

Common Patterns

Define Features Per Version

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)
}

Conditional Display

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
}

Remote Content Loading

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()

Gotchas

CFBundleShortVersionString vs CFBundleVersion

  • CFBundleShortVersionString = marketing version (e.g., "2.1.0") — use this for What's New
  • CFBundleVersion = build number (e.g., "47") — changes every build, NOT suitable for What's New tracking
  • Always compare the marketing version, never the build number

First Install vs Update Detection

  • On first install, lastShownVersion is nil
  • Decide whether first-time users should see What's New (usually NO — show onboarding instead)
  • If lastShownVersion 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
}

TestFlight vs App Store Versions

  • TestFlight builds may have the same marketing version but different build numbers
  • If using TestFlight for beta testing, consider including build number in tracking key for testers
  • For production, always track by marketing version only

Version String Comparison

  • Use .numeric option in String.compare(_:options:) so "2.0.0" > "1.10.0" (not lexicographic)
  • Never compare version strings with < or > operators directly — "9.0" would sort after "10.0" lexicographically

Accessibility

  • Ensure all feature images have accessibility labels
  • Support Dynamic Type in feature descriptions
  • VoiceOver should read features sequentially — use .accessibilityElement(children: .combine) on feature cards

References

  • templates.md — All production Swift templates
  • Related: generators/onboarding-generator — First-launch onboarding (complementary — onboarding for first install, What's New for updates)

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Run a structured accessibility audit on an iOS/macOS app — automated XCUITest audits, Accessibility Inspector, manual VoiceOver/Dynamic Type passes, and App Store Accessibility Nutrition Label evaluation. Use before release, when preparing Nutrition Label declarations, or for EU Accessibility Act compliance.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rshankras/claude-code-apple-skills8192026년 7월 24일 업데이트

Generate accessibility infrastructure for VoiceOver, Dynamic Type, and accessibility features. Use when improving app accessibility, adding accessibility labels and hints, or auditing compliance.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rshankras/claude-code-apple-skills8192026년 7월 24일 업데이트

Generates an Apple-compliant account deletion flow with multi-step confirmation UI, optional data export, configurable grace period, Keychain cleanup, and server-side deletion request. Use when user needs account deletion, right-to-delete, or Apple App Review compliance for account removal.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rshankras/claude-code-apple-skills8192026년 7월 24일 업데이트

Privacy-preserving ad measurement with AdAttributionKit (SKAdNetwork's successor) — install and re-engagement attribution, conversion-value strategy under crowd anonymity, and end-to-end postback testing. Use when running paid acquisition beyond Apple Ads, measuring re-engagement campaigns, designing conversion values, or migrating from SKAdNetwork.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rshankras/claude-code-apple-skills8192026년 7월 24일 업데이트

alarmkit

무료

AlarmKit integration for scheduling alarms and timers with custom UI, Live Activities, and snooze support. Use when implementing alarm or timer features in iOS 26+ apps.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rshankras/claude-code-apple-skills8192026년 7월 24일 업데이트

Interpret app metrics and make data-driven decisions. Covers DAU/MAU, retention, LTV, ARPU, App Store Connect analytics, AARRR funnel analysis, cohort analysis, and diagnostic decision trees. Use when user wants to understand their metrics, diagnose problems, or build a data-driven growth plan.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

rshankras/claude-code-apple-skills8192026년 7월 24일 업데이트

rshankras의 스킬 모두 보기

이 스킬의 문제 신고