Skip to content
프로젝트

SvenTiigi/WhatsNewKit has 4,440 GitHub stars and 217 forks. GitHot measured +1 stars in 24 hours and +1 in 7 days. Last checked 2026-11-13 04:06 UTC. Last push 2024-10-10.

이 수치를 측정하는 방법

What you can do

  • Easily present your new app features 🤩
  • Automatic & Manual presentation mode ✅
  • Support for SwiftUI, UIKit and AppKit 🧑‍🎨
  • Runs on iOS, macOS and visionOS 📱 🖥 👓

측정된 스타 증가

in 24h
+1
in 7 days
+1
in 15 days
업데이트 지연
in 1 month
업데이트 지연

서로 겹치는 기간의 순증가량이며 일별 합계가 아닙니다. 측정값이 없으면 비워 둡니다. 스타는 관심도를 나타내며 품질을 보장하지 않습니다.

소개

Showcase your awesome new app features 📱

학습

README.md

Original

logo

WhatsNewKit

A Swift Package to easily showcase your new app features.
It's designed from the ground up to be fully customized to your needs.

Swift Version Platforms
Build and Test Status Documentation Twitter Mastodon

Example

import SwiftUI import WhatsNewKit

struct ContentView: View {

<span class="pl-k">var</span> <span class="pl-s1">body</span><span class="pl-kos">:</span> <span class="pl-k">some</span> <span class="pl-smi">View</span> <span class="pl-kos">{</span>
    <span class="pl-en">NavigationView</span> <span class="pl-kos">{</span>
        // ...
    <span class="pl-kos">}</span>
    <span class="pl-kos">.</span><span class="pl-en">whatsNewSheet</span><span class="pl-kos">(</span><span class="pl-kos">)</span>
<span class="pl-kos">}</span>

}

Features

  • Easily present your new app features 🤩
  • Automatic & Manual presentation mode ✅
  • Support for SwiftUI, UIKit and AppKit 🧑‍🎨
  • Runs on iOS, macOS and visionOS 📱 🖥 👓
  • Adjustable layout 🔧

Installation

Swift Package Manager

To integrate using Apple's Swift Package Manager, add the following as a dependency to your Package.swift:

dependencies: [
    .package(url: "https://github.com/SvenTiigi/WhatsNewKit.git", from: "2.0.0")
]

Or navigate to your Xcode project then select Swift Packages, click the “+” icon and search for WhatsNewKit.

Example

Check out the example application to see WhatsNewKit in action. Simply open the Example/Example.xcodeproj and run the "Example" scheme.

Example Applications

Usage

Table of contents

Manual Presentation

If you wish to manually present a WhatsNewView you can make use of the sheet(whatsNew:) modifier.

struct ContentView: View {

<span class="pl-c1">@</span><span class="pl-c1">State</span>
<span class="pl-k">var</span> <span class="pl-s1">whatsNew</span><span class="pl-kos">:</span> <span class="pl-smi">WhatsNew</span><span class="pl-c1"><span class="pl-c1">?</span></span> <span class="pl-c1">=</span> <span class="pl-en">WhatsNew</span><span class="pl-kos">(</span>
    <span class="pl-s1">title</span><span class="pl-kos">:</span> <span class="pl-s">"</span><span class="pl-s">WhatsNewKit</span><span class="pl-s">"</span><span class="pl-kos">,</span>
    <span class="pl-s1">features</span><span class="pl-kos">:</span> <span class="pl-kos">[</span>
        <span class="pl-kos">.</span><span class="pl-en">init</span><span class="pl-kos">(</span>
            <span class="pl-s1">image</span><span class="pl-kos">:</span> <span class="pl-kos">.</span><span class="pl-en">init</span><span class="pl-kos">(</span>
                <span class="pl-s1">systemName</span><span class="pl-kos">:</span> <span class="pl-s">"</span><span class="pl-s">star.fill</span><span class="pl-s">"</span><span class="pl-kos">,</span>
                <span class="pl-s1">foregroundColor</span><span class="pl-kos">:</span> <span class="pl-kos">.</span><span class="pl-s1">orange</span>
            <span class="pl-kos">)</span><span class="pl-kos">,</span>
            <span class="pl-s1">title</span><span class="pl-kos">:</span> <span class="pl-s">"</span><span class="pl-s">Showcase your new App Features</span><span class="pl-s">"</span><span class="pl-kos">,</span>
            <span class="pl-s1">subtitle</span><span class="pl-kos">:</span> <span class="pl-s">"</span><span class="pl-s">Present your new app features...</span><span class="pl-s">"</span>
        <span class="pl-kos">)</span><span class="pl-kos">,</span>
        // ...
    <span class="pl-kos">]</span>
<span class="pl-kos">)</span>

<span class="pl-k">var</span> <span class="pl-s1">body</span><span class="pl-kos">:</span> <span class="pl-k">some</span> <span class="pl-smi">View</span> <span class="pl-kos">{</span>
    <span class="pl-en">NavigationView</span> <span class="pl-kos">{</span>
        // ...
    <span class="pl-kos">}</span>
    <span class="pl-kos">.</span><span class="pl-en">sheet</span><span class="pl-kos">(</span>
        <span class="pl-s1">whatsNew</span><span class="pl-kos">:</span> <span class="pl-smi">self</span><span class="pl-kos">.</span><span class="pl-s1">$whatsNew</span>
    <span class="pl-kos">)</span>
<span class="pl-kos">}</span>

}

Automatic Presentation

The automatic presentation mode allows you to simply declare your new features via the SwiftUI Environment and WhatsNewKit will take care to present the corresponding WhatsNewView.

First add a .whatsNewSheet() modifier to the view where the WhatsNewView should be presented on.

struct ContentView: View {

<span class="pl-k">var</span> <span class="pl-s1">body</span><span class="pl-kos">:</span> <span class="pl-k">some</span> <span class="pl-smi">View</span> <span class="pl-kos">{</span>
    <span class="pl-en">NavigationView</span> <span class="pl-kos">{</span>
        // ...
    <span class="pl-kos">}</span>
    // Automatically present a WhatsNewView, if needed.
    // The WhatsNew that should be presented to the user
    // is automatically retrieved from the `WhatsNewEnvironment`
    <span class="pl-kos">.</span><span class="pl-en">whatsNewSheet</span><span class="pl-kos">(</span><span class="pl-kos">)</span>
<span class="pl-kos">}</span>

}

The .whatsNewSheet() modifier is making use of the WhatsNewEnvironment to retrieve an optional WhatsNew object that should be presented to the user for the current version. Therefore you can easily configure the WhatsNewEnvironment via the environment modifier.

extension App: SwiftUI.App {

<span class="pl-k">var</span> <span class="pl-s1">body</span><span class="pl-kos">:</span> <span class="pl-k">some</span> <span class="pl-smi">Scene</span> <span class="pl-kos">{</span>
    <span class="pl-en">WindowGroup</span> <span class="pl-kos">{</span>
        <span class="pl-en">ContentView</span><span class="pl-kos">(</span><span class="pl-kos">)</span>
            <span class="pl-kos">.</span><span class="pl-en">environment</span><span class="pl-kos">(</span>
                \<span class="pl-kos">.</span><span class="pl-s1">whatsNew</span><span class="pl-kos">,</span>
                <span class="pl-en">WhatsNewEnvironment</span><span class="pl-kos">(</span>
                    // Specify in which way the presented WhatsNew Versions are stored.
                    // In default the `UserDefaultsWhatsNewVersionStore` is used.
                    <span class="pl-s1">versionStore</span><span class="pl-kos">:</span> <span class="pl-en">UserDefaultsWhatsNewVersionStore</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">,</span>
                    // Pass a `WhatsNewCollectionProvider` or an array of WhatsNew instances
                    <span class="pl-s1">whatsNewCollection</span><span class="pl-kos">:</span> <span class="pl-smi">self</span>
                <span class="pl-kos">)</span>
            <span class="pl-kos">)</span>
    <span class="pl-kos">}</span>
<span class="pl-kos">}</span>

}

// MARK: - App+WhatsNewCollectionProvider

extension App: WhatsNewCollectionProvider {

<span class="pl-c">/// Declare your WhatsNew instances per version</span>
<span class="pl-k">var</span> <span class="pl-s1">whatsNewCollection</span><span class="pl-kos">:</span> <span class="pl-smi">WhatsNewCollection</span> <span class="pl-kos">{</span>
    <span class="pl-en">WhatsNew</span><span class="pl-kos">(</span>
        <span class="pl-s1">version</span><span class="pl-kos">:</span> <span class="pl-s">"</span><span class="pl-s">1.0.0</span><span class="pl-s">"</span><span class="pl-kos">,</span>
        // ...
    <span class="pl-kos">)</span>
    <span class="pl-en">WhatsNew</span><span class="pl-kos">(</span>
        <span class="pl-s1">version</span><span class="pl-kos">:</span> <span class="pl-s">"</span><span class="pl-s">1.1.0</span><span class="pl-s">"</span><span class="pl-kos">,</span>
        // ...
    <span class="pl-kos">)</span>
    <span class="pl-en">WhatsNew</span><span class="pl-kos">(</span>
        <span class="pl-s1">version</span><span class="pl-kos">:</span> <span class="pl-s">"</span><span class="pl-s">1.2.0</span><span class="pl-s">"</span><span class="pl-kos">,</span>
        // ...
    <span class="pl-kos">)</span>
<span class="pl-kos">}</span>

}

WhatsNewEnvironment

The WhatsNewEnvironment will take care to determine the matching WhatsNew object that should be presented to the user for the current version.

As seen in the previous example you can initialize a WhatsNewEnvironment by specifying the WhatsNewVersionStore and providing a WhatsNewCollection.

let whatsNewEnvironment = WhatsNewEnvironment( whatsNewCollection: [ WhatsNew( version: "1.0.0", // ... ) ] )

// Initialize WhatsNewEnvironment with NSUbiquitousKeyValueWhatsNewVersionStore // which stores the presented versions in iCloud. // WhatsNewCollection is provided by a WhatsNewBuilder closure let whatsNewEnvironment = WhatsNewEnvironment( versionStore: NSUbiquitousKeyValueWhatsNewVersionStore(), whatsNewCollection: { WhatsNew( version: "1.0.0", // ... ) } )

Additionally, the WhatsNewEnvironment includes a fallback for patch versions. For example when a user installs version 1.0.1 and you only have declared a WhatsNew for version 1.0.0 the environment will automatically fallback to version 1.0.0 and present the WhatsNewView to the user if needed.

If you wish to further customize the behaviour of the WhatsNewEnvironment you can easily subclass it and override the whatsNew() function.

class MyCustomWhatsNewEnvironment: WhatsNewEnvironment {

<span class="pl-c">/// Retrieve a WhatsNew that should be presented to the user, if available.</span>
<span class="pl-k"><span class="pl-k">override</span></span> <span class="pl-en">func</span> <span class="pl-en">whatsNew</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">-&gt;</span> <span class="pl-smi">WhatsNew</span><span class="pl-c1"><span class="pl-c1">?</span></span> <span class="pl-kos">{</span>
    // The current version
    <span class="pl-k">let</span> <span class="pl-s1">currentVersion</span> <span class="pl-c1">=</span> <span class="pl-smi">self</span><span class="pl-kos">.</span><span class="pl-s1">currentVersion</span>
    // Your declared WhatsNew objects
    <span class="pl-k">let</span> <span class="pl-s1">whatsNewCollection</span> <span class="pl-c1">=</span> <span class="pl-smi">self</span><span class="pl-kos">.</span><span class="pl-s1">whatsNewCollection</span>
    // The WhatsNewVersionStore used to determine the already presented versions
    <span class="pl-k">let</span> <span class="pl-s1">versionStore</span> <span class="pl-c1">=</span> <span class="pl-smi">self</span><span class="pl-kos">.</span><span class="pl-s1">whatsNewVersionStore</span>
    // TODO: Determine WhatsNew that should be presented to the user...
<span class="pl-kos">}</span>

}

WhatsNewVersionStore

A WhatsNewVersionStore is a protocol type which is responsible for saving and retrieving versions that have been presented to the user.

let whatsNewVersionStore: WhatsNewVersionStore

// Save presented versions whatsNewVersionStore.save(presentedVersion: "1.0.0")

// Retrieve presented versions let presentedVersions = whatsNewVersionStore.presentedVersions

// Retrieve bool value if a given version has already been presented let hasPresented = whatsNewVersionStore.hasPresented("1.0.0")

WhatsNewKit comes along with three predefined implementations:

let userDefaultsWhatsNewVersionStore = UserDefaultsWhatsNewVersionStore()

// Persists presented versions in iCloud using the NSUbiquitousKeyValueStore let ubiquitousKeyValueWhatsNewVersionStore = NSUbiquitousKeyValueWhatsNewVersionStore()

// Stores presented versions in memory. Perfect for testing purposes let inMemoryWhatsNewVersionStore = InMemoryWhatsNewVersionStore()

If you already have a specific implementation to store user related settings like Realm or Core Data you can easily adopt your existing implementation to the WhatsNewVersionStore protocol.

NSUbiquitousKeyValueWhatsNewVersionStore

If you are making use of the NSUbiquitousKeyValueWhatsNewVersionStore please ensure to enable the iCloud Key-value storage capability in the "Signing & Capabilities" section of your Xcode project.

iCloud Key-value storage

WhatsNew

The following sections explains how a WhatsNew struct can be initialized in order to describe the new features for a given version of your app.

let whatsnew = WhatsNew(
    // The Version that relates to the features you want to showcase
    version: "1.0.0",
    // The title that is shown at the top
    title: "What's New",
    // The features you want to showcase
    features: [
        WhatsNew.Feature(
            image: .init(systemName: "star.fill"),
            title: "Title",
            subtitle: "Subtitle"
        )
    ],
    // The primary action that is used to dismiss the WhatsNewView
    primaryAction: WhatsNew.PrimaryAction(
        title: "Continue",
        backgroundColor: .accentColor,
        foregroundColor: .white,
        hapticFeedback: .notification(.success),
        onDismiss: {
            print("WhatsNewView has been dismissed")
        }
    ),
    // The optional secondary action that is displayed above the primary action
    secondaryAction: WhatsNew.SecondaryAction(
        title: "Learn more",
        foregroundColor: .accentColor,
        hapticFeedback: .selection,
        action: .openURL(
            .init(string: "https://github.com/SvenTiigi/WhatsNewKit")
        )
    )
)

WhatsNew.Version

The WhatsNew.Version specifies the version that has introduced certain features to your app.

let version = WhatsNew.Version( major: 1, minor: 0, patch: 0 )

// Initialize by string literal let version: WhatsNew.Version = "1.0.0"

// Initialize WhatsNew Version by using the current version of your bundle let version: WhatsNew.Version = .current()

WhatsNew.Title

A WhatsNew.Title represents the title text that is rendered above the features.

let title: WhatsNew.Title = "Continue"

// Initialize with text and foreground color let title = WhatsNew.Title( text: "Continue", foregroundColor: .primary )

// On >= iOS 15 initialize with AttributedString using Markdown let title = WhatsNew.Title( text: try AttributedString( markdown: "What's New" ) )

WhatsNew.Feature

A WhatsNew.Feature describe a specific feature of your app and generally consist of an image, title, and subtitle.

let feature = WhatsNew.Feature(
    image: .init(
        systemName: "wand.and.stars"
    ),
    title: "New Design",
    subtitle: .init(
        try AttributedString(
            markdown: "An awesome new _Design_"
        )
    )
)

WhatsNew.PrimaryAction

The WhatsNew.PrimaryAction allows you to configure the behaviour of the primary button which is used to dismiss the presented WhatsNewView

let primaryAction = WhatsNew.PrimaryAction(
    title: "Continue",
    backgroundColor: .blue,
    foregroundColor: .white,
    hapticFeedback: .notification(.success),
    onDismiss: {
        print("WhatsNewView has been dismissed")
    }
)

Note: HapticFeedback will only be executed on iOS

WhatsNew.SecondaryAction

A WhatsNew.SecondaryAction which is displayed above the WhatsNew.PrimaryAction can be optionally supplied when initializing a WhatsNew instance and allows you to present an additional View, perform a custom action or open an URL.

https://github.com/SvenTiigi/WhatsNewKit") ) )

// SecondaryAction with custom execution let secondaryActionCustom = WhatsNew.SecondaryAction( title: "Custom", action: .custom { presentationMode in // ... } )">

// SecondaryAction that presents a View
let secondaryActionPresentAboutView = WhatsNew.SecondaryAction(
    title: "Learn more",
    foregroundColor: .blue,
    hapticFeedback: .selection,
    action: .present {
        AboutView()
    }
)

// SecondaryAction that opens a URL let secondaryActionOpenURL = WhatsNew.SecondaryAction( title: "Read more", foregroundColor: .blue, hapticFeedback: .selection, action: .open( url: .init(string: "https://github.com/SvenTiigi/WhatsNewKit") ) )

// SecondaryAction with custom execution let secondaryActionCustom = WhatsNew.SecondaryAction( title: "Custom", action: .custom { presentationMode in // ... } )

Note: HapticFeedback will only be executed on iOS

Layout

WhatsNewKit allows you to adjust the layout of a presented WhatsNewView in various ways.

The most simple way is by mutating the WhatsNew.Layout.default instance.

WhatsNew.Layout.default.featureListSpacing = 35

When using the automatic presentation style you can supply a default layout when initializing the WhatsNewEnvironment.

.environment(
    \.whatsNew,
    .init(
        defaultLayout: WhatsNew.Layout(
            showsScrollViewIndicators: true,
            featureListSpacing: 35
        ),
        whatsNew: self
    )
)

Alternatively you can pass a WhatsNew.Layout when automatically or manually presenting the WhatsNewView

.whatsNewSheet(
    layout: WhatsNew.Layout(
        contentPadding: .init(
            top: 80,
            leading: 0,
            bottom: 0,
            trailing: 0
        )
    )
)
.sheet(
    whatsNew: self.$whatsNew,
    layout: WhatsNew.Layout(
        footerActionSpacing: 20
    )
)

WhatsNewViewController

When using UIKit or AppKit you can make use of the WhatsNewViewController.

let whatsNewViewController = WhatsNewViewController(
    whatsNew: WhatsNew(
        version: "1.0.0",
        // ...
    ),
    layout: WhatsNew.Layout(
        contentSpacing: 80
    )
)

If you wish to present a WhatsNewViewController only if the version of the WhatsNew instance has not been presented you can make use of the convenience failable initializer.

guard let whatsNewViewController = WhatsNewViewController( whatsNew: WhatsNew( version: "1.0.0", // ... ), versionStore: UserDefaultsWhatsNewVersionStore() ) else { // Version of WhatsNew has already been presented return }

// Present WhatsNewViewController // Version will be automatically saved in the provided // WhatsNewVersionStore when the WhatsNewViewController gets dismissed self.present(whatsNewViewController, animated: true)

관련 프로젝트 살펴보기