WhatsNewKit Documentation

repository·main·Indexed 26 days ago

https://github.com/sventiigi/whatsnewkit

A Swift package for showcasing new app features via customizable sheets. It supports manual and automatic presentation modes across SwiftUI, UIKit, and AppKit for iOS, macOS, and visionOS. Key features include the WhatsNewEnvironment for automatic version-based presentation, multiple storage options via the WhatsNewVersionStore protocol (UserDefaults, iCloud, or In-Memory), and flexible layout customization for feature lists and actions.

Tokens
4.6K
Snippets
14
Records
14
Agent score
39%

What's inside WhatsNewKit

  1. Automatically present WhatsNewView using SwiftUI Environment

    main

    To enable automatic presentation, add the .whatsNewSheet() modifier to your view hierarchy. The view will automatically retrieve and present the appropriate WhatsNew object from the WhatsNewEnvironment provided in the environment.

    struct ContentView: View {
    
        var body: some View {
            NavigationView {
                // ...
            }
            // Automatically present a WhatsNewView, if needed.
            .whatsNewSheet()
        }
    
    }
  2. Install WhatsNewKit via Swift Package Manager

    main

    To integrate WhatsNewKit into your project, add it as a dependency in your Package.swift file or search for it in Xcode's Swift Packages menu.

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

    main

    You can customize the layout of the WhatsNewView by mutating WhatsNew.Layout.default, providing a default layout to the WhatsNewEnvironment, or passing a specific Layout object when presenting the view via .whatsNewSheet or .sheet.

    // Mutate default layout
    WhatsNew.Layout.default.featureListSpacing = 35
    
    // Provide default layout in environment
    .environment(
        \.whatsNew,
        .init(
            defaultLayout: WhatsNew.Layout(
                showsScrollViewIndicators: true,
                featureListSpacing: 35
            ),
            whatsNew: self
        )
    )
    
    // Pass layout to sheet
    .whatsNewSheet(
        layout: WhatsNew.Layout(
            contentPadding: .init(
                top: 80,
                leading: 0,
                bottom: 0,
                trailing: 0
            )
        )
    )
    
    .sheet(
        whatsNew: self.$whatsNew,
        layout: WhatsNew.Layout(
            footerActionSpacing: 20
        )
    )
  4. Configure WhatsNewEnvironment for Automatic Presentation

    main

    To use automatic presentation, you must configure the WhatsNewEnvironment via the .environment(\.whatsNew, ...) modifier at the app level. You can provide a versionStore and a whatsNewCollection. A common pattern is to make your App conform to WhatsNewCollectionProvider to declare features per version.

    extension App: SwiftUI.App {
    
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .environment(
                        \.whatsNew,
                        WhatsNewEnvironment(
                            // Specify in which way the presented WhatsNew Versions are stored.
                            // In default the `UserDefaultsWhatsNewVersionStore` is used.
                            versionStore: UserDefaultsWhatsNewVersionStore(),
                            // Pass a `WhatsNewCollectionProvider` or an array of WhatsNew instances
                            whatsNewCollection: self
                        )
                    )
            }
        }
    
    }
    
    // MARK: - App+WhatsNewCollectionProvider
    
    extension App: WhatsNewCollectionProvider {
    
        /// Declare your WhatsNew instances per version
        var whatsNewCollection: WhatsNewCollection {
            WhatsNew(
                version: "1.0.0",
                // ...
            )
            WhatsNew(
                version: "1.1.0",
                // ...
            )
            WhatsNew(
                version: "1.2.0",
                // ...
            )
        }
    
    }
  5. Manually present a WhatsNewView

    main

    If you want full control over when the new features view is shown, use the sheet(whatsNew:) modifier. You must provide a WhatsNew object containing a title and an array of features.

    struct ContentView: View {
    
        @State
        var whatsNew: WhatsNew? = WhatsNew(
            title: "WhatsNewKit",
            features: [
                .init(
                    image: .init(
                        systemName: "star.fill",
                        foregroundColor: .orange
                    ),
                    title: "Showcase your new App Features",
                    subtitle: "Present your new app features..."
                ),
                // ...
            ]
        )
    
        var body: some View {
            NavigationView {
                // ...
            }
            .sheet(
                whatsNew: self.$whatsNew
            )
        }
    
    }
  6. Initialize a WhatsNew instance

    main

    Use the WhatsNew struct to define the content of your

    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")
            )
        )
    )
  7. Use WhatsNewViewController in UIKit or AppKit

    main

    For non-SwiftUI applications, use WhatsNewViewController. To ensure the view is only presented once per version, use the failable initializer that accepts a versionStore (e.g., UserDefaultsWhatsNewVersionStore). If the initializer returns nil, the version has already been shown.

    // Standard initialization
    let whatsNewViewController = WhatsNewViewController(
        whatsNew: WhatsNew(
            version: "1.0.0",
            // ...
        ),
        layout: WhatsNew.Layout(
            contentSpacing: 80
        )
    )
    
    // Conditional presentation based on version history
    guard let whatsNewViewController = WhatsNewViewController(
        whatsNew: WhatsNew(
            version: "1.0.0",
            // ...
        ),
        versionStore: UserDefaultsWhatsNewVersionStore()
    ) else {
        // Version of WhatsNew has already been presented
        return
    }
    
    // Present the controller
    self.present(whatsNewViewController, animated: true)
  8. Initialize WhatsNewEnvironment with custom collections

    main

    You can initialize WhatsNewEnvironment by passing an array of WhatsNew instances or by using a WhatsNewBuilder closure. It also supports fallback logic: if a user is on version 1.0.1 but you only have a declaration for 1.0.0, it will automatically fall back to presenting the 1.0.0 features.

    // Initialize WhatsNewEnvironment by passing an array of WhatsNew Instances.
    // UserDefaultsWhatsNewVersionStore is used as default WhatsNewVersionStore
    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",
                // ...
            )
        }
    )
  9. Implement or use WhatsNewVersionStore

    main

    The WhatsNewVersionStore protocol manages which versions have already been shown to the user. WhatsNewKit provides three built-in implementations:

    • UserDefaultsWhatsNewVersionStore(): Persists versions in UserDefaults.
    • NSUbiquitousKeyValueWhatsNewVersionStore(): Persists versions in iCloud (requires iCloud Key-value storage capability).
    • InMemoryWhatsNewVersionStore(): Stores versions in memory (ideal for testing).

    You can also implement this protocol to use your own storage like Realm or Core Data.

    // Persists presented versions in the UserDefaults
    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()
  10. Configure WhatsNew.Feature

    main

    A WhatsNew.Feature describes an individual feature, typically consisting of an image (e.g., SF Symbol), a title, and a subtitle (which can be an AttributedString for Markdown).

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

    main

    The WhatsNew.Version type identifies the app version associated with the features. You can initialize it using major/minor/patch integers, a string literal, or by automatically detecting the current bundle version.

    // Initialize with major, minor, and patch
    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()
  12. Configure WhatsNew.PrimaryAction

    main

    The WhatsNew.PrimaryAction defines the behavior of the main button used to dismiss the view. You can set the title, colors, and optional haptic feedback. Note: hapticFeedback only executes on iOS.

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