[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-6882":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":22,"hasPages":24,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":32,"readmeContent":33,"aiSummary":34,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":35,"discoverSource":36},6882,"WhatsNewKit","SvenTiigi\u002FWhatsNewKit","SvenTiigi","Showcase your awesome new app features 📱","https:\u002F\u002Fsventiigi.github.io\u002FWhatsNewKit\u002F",null,"Swift",4381,215,41,7,0,2,9,1,60.9,"MIT License",false,"main",true,[26,27,28,29,30,31],"ios","macos","swift","swift-package","swiftui","whatsnew","2026-06-12 04:00:30","\u003Cbr\u002F>\n\n\u003Cp align=\"center\">\n    \u003Cimg src=\"Assets\u002Flogo.png\" width=\"30%\" alt=\"logo\">\n\u003C\u002Fp>\n\n\u003Ch1 align=\"center\">\n    WhatsNewKit\n\u003C\u002Fh1>\n\n\u003Cp align=\"center\">\n    A Swift Package to easily showcase your new app features.\n    \u003Cbr\u002F>\n    It's designed from the ground up to be fully customized to your needs.\n\u003C\u002Fp>\n\n\u003Cp align=\"center\">\n   \u003Ca href=\"https:\u002F\u002Fswiftpackageindex.com\u002FSvenTiigi\u002FWhatsNewKit\">\n    \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fendpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FSvenTiigi%2FWhatsNewKit%2Fbadge%3Ftype%3Dswift-versions\" alt=\"Swift Version\">\n   \u003C\u002Fa>\n   \u003Ca href=\"https:\u002F\u002Fswiftpackageindex.com\u002FSvenTiigi\u002FWhatsNewKit\">\n    \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fendpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FSvenTiigi%2FWhatsNewKit%2Fbadge%3Ftype%3Dplatforms\" alt=\"Platforms\">\n   \u003C\u002Fa>\n   \u003Cbr\u002F>\n   \u003Ca href=\"https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit\u002Factions\u002Fworkflows\u002Fbuild_and_test.yml\">\n       \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit\u002Factions\u002Fworkflows\u002Fbuild_and_test.yml\u002Fbadge.svg\" alt=\"Build and Test Status\">\n   \u003C\u002Fa>\n   \u003Ca href=\"https:\u002F\u002Fsventiigi.github.io\u002FWhatsNewKit\u002Fdocumentation\u002Fwhatsnewkit\u002F\">\n       \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FDocumentation-DocC-blue\" alt=\"Documentation\">\n   \u003C\u002Fa>\n   \u003Ca href=\"https:\u002F\u002Ftwitter.com\u002FSvenTiigi\u002F\">\n      \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FTwitter-@SvenTiigi-blue.svg?style=flat\" alt=\"Twitter\">\n   \u003C\u002Fa>\n   \u003Ca href=\"https:\u002F\u002Fmastodon.world\u002F@SvenTiigi\">\n      \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FMastodon-@SvenTiigi-8c8dff.svg?style=flat\" alt=\"Mastodon\">\n   \u003C\u002Fa>\n\u003C\u002Fp>\n\n\u003Cimg align=\"right\" width=\"315\" src=\"Assets\u002Fexample.png\" alt=\"Example\">\n\n```swift\nimport SwiftUI\nimport WhatsNewKit\n\nstruct ContentView: View {\n\n    var body: some View {\n        NavigationView {\n            \u002F\u002F ...\n        }\n        .whatsNewSheet()\n    }\n\n}\n```\n\n## Features\n\n- [x] Easily present your new app features 🤩\n- [x] Automatic & Manual presentation mode ✅\n- [x] Support for SwiftUI, UIKit and AppKit 🧑‍🎨\n- [x] Runs on iOS, macOS and visionOS 📱 🖥 👓\n- [x] Adjustable layout 🔧\n\n## Installation\n\n### Swift Package Manager\n\nTo integrate using Apple's [Swift Package Manager](https:\u002F\u002Fswift.org\u002Fpackage-manager\u002F), add the following as a dependency to your `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit.git\", from: \"2.0.0\")\n]\n```\n\nOr navigate to your Xcode project then select `Swift Packages`, click the “+” icon and search for `WhatsNewKit`.\n\n## Example\n\nCheck out the example application to see WhatsNewKit in action. Simply open the `Example\u002FExample.xcodeproj` and run the \"Example\" scheme.\n\n\u003Cp align=\"center\">\n    \u003Cimg width=\"95%\" src=\"Assets\u002Fexample-app.png\" alt=\"Example Applications\">\n\u003C\u002Fp>\n\n## Usage\n\n### Table of contents\n\n- [Manual Presentation](https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit\u002Ftree\u002Fmain#manual-presentation)\n- [Automatic Presentation](https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit\u002Ftree\u002Fmain#automatic-presentation)\n- [WhatsNewEnvironment](https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit\u002Ftree\u002Fmain#whatsnewenvironment)\n- [WhatsNewVersionStore](https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit\u002Ftree\u002Fmain#whatsnewversionstore)\n- [WhatsNew](https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit\u002Ftree\u002Fmain#whatsnew)\n- [Layout](https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit\u002Ftree\u002Fmain#layout)\n- [WhatsNewViewController](https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit\u002Ftree\u002Fmain#whatsnewviewcontroller)\n\n### Manual Presentation\n\nIf you wish to manually present a `WhatsNewView` you can make use of the `sheet(whatsNew:)` modifier.\n\n```swift\nstruct ContentView: View {\n\n    @State\n    var whatsNew: WhatsNew? = WhatsNew(\n        title: \"WhatsNewKit\",\n        features: [\n            .init(\n                image: .init(\n                    systemName: \"star.fill\",\n                    foregroundColor: .orange\n                ),\n                title: \"Showcase your new App Features\",\n                subtitle: \"Present your new app features...\"\n            ),\n            \u002F\u002F ...\n        ]\n    )\n\n    var body: some View {\n        NavigationView {\n            \u002F\u002F ...\n        }\n        .sheet(\n            whatsNew: self.$whatsNew\n        )\n    }\n\n}\n```\n\n### Automatic Presentation\n\nThe 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`.\n\nFirst add a `.whatsNewSheet()` modifier to the view where the `WhatsNewView` should be presented on.\n\n```swift\nstruct ContentView: View {\n\n    var body: some View {\n        NavigationView {\n            \u002F\u002F ...\n        }\n        \u002F\u002F Automatically present a WhatsNewView, if needed.\n        \u002F\u002F The WhatsNew that should be presented to the user\n        \u002F\u002F is automatically retrieved from the `WhatsNewEnvironment`\n        .whatsNewSheet()\n    }\n\n}\n```\n\nThe `.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.\n\n```swift\nextension App: SwiftUI.App {\n\n    var body: some Scene {\n        WindowGroup {\n            ContentView()\n                .environment(\n                    \\.whatsNew,\n                    WhatsNewEnvironment(\n                        \u002F\u002F Specify in which way the presented WhatsNew Versions are stored.\n                        \u002F\u002F In default the `UserDefaultsWhatsNewVersionStore` is used.\n                        versionStore: UserDefaultsWhatsNewVersionStore(),\n                        \u002F\u002F Pass a `WhatsNewCollectionProvider` or an array of WhatsNew instances\n                        whatsNewCollection: self\n                    )\n                )\n        }\n    }\n\n}\n\n\u002F\u002F MARK: - App+WhatsNewCollectionProvider\n\nextension App: WhatsNewCollectionProvider {\n\n    \u002F\u002F\u002F Declare your WhatsNew instances per version\n    var whatsNewCollection: WhatsNewCollection {\n        WhatsNew(\n            version: \"1.0.0\",\n            \u002F\u002F ...\n        )\n        WhatsNew(\n            version: \"1.1.0\",\n            \u002F\u002F ...\n        )\n        WhatsNew(\n            version: \"1.2.0\",\n            \u002F\u002F ...\n        )\n    }\n\n}\n```\n\n## WhatsNewEnvironment\n\nThe `WhatsNewEnvironment` will take care to determine the matching WhatsNew object that should be presented to the user for the current version.\n\nAs seen in the previous example you can initialize a `WhatsNewEnvironment` by specifying the `WhatsNewVersionStore` and providing a `WhatsNewCollection`.\n\n```swift\n\u002F\u002F Initialize WhatsNewEnvironment by passing an array of WhatsNew Instances.\n\u002F\u002F UserDefaultsWhatsNewVersionStore is used as default WhatsNewVersionStore\nlet whatsNewEnvironment = WhatsNewEnvironment(\n    whatsNewCollection: [\n        WhatsNew(\n            version: \"1.0.0\",\n            \u002F\u002F ...\n        )\n    ]\n)\n\n\u002F\u002F Initialize WhatsNewEnvironment with NSUbiquitousKeyValueWhatsNewVersionStore\n\u002F\u002F which stores the presented versions in iCloud.\n\u002F\u002F WhatsNewCollection is provided by a `WhatsNewBuilder` closure\nlet whatsNewEnvironment = WhatsNewEnvironment(\n    versionStore: NSUbiquitousKeyValueWhatsNewVersionStore(),\n    whatsNewCollection: {\n        WhatsNew(\n            version: \"1.0.0\",\n            \u002F\u002F ...\n        )\n    }\n)\n```\n\nAdditionally, 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.\n\nIf you wish to further customize the behaviour of the `WhatsNewEnvironment` you can easily subclass it and override the `whatsNew()` function.\n\n```swift\nclass MyCustomWhatsNewEnvironment: WhatsNewEnvironment {\n\n    \u002F\u002F\u002F Retrieve a WhatsNew that should be presented to the user, if available.\n    override func whatsNew() -> WhatsNew? {\n        \u002F\u002F The current version\n        let currentVersion = self.currentVersion\n        \u002F\u002F Your declared WhatsNew objects\n        let whatsNewCollection = self.whatsNewCollection\n        \u002F\u002F The WhatsNewVersionStore used to determine the already presented versions\n        let versionStore = self.whatsNewVersionStore\n        \u002F\u002F TODO: Determine WhatsNew that should be presented to the user...\n    }\n\n}\n```\n\n## WhatsNewVersionStore\n\nA `WhatsNewVersionStore` is a protocol type which is responsible for saving and retrieving versions that have been presented to the user.\n\n```swift\nlet whatsNewVersionStore: WhatsNewVersionStore\n\n\u002F\u002F Save presented versions\nwhatsNewVersionStore.save(presentedVersion: \"1.0.0\")\n\n\u002F\u002F Retrieve presented versions\nlet presentedVersions = whatsNewVersionStore.presentedVersions\n\n\u002F\u002F Retrieve bool value if a given version has already been presented\nlet hasPresented = whatsNewVersionStore.hasPresented(\"1.0.0\")\n```\n\nWhatsNewKit comes along with three predefined implementations:\n\n```swift\n\u002F\u002F Persists presented versions in the UserDefaults\nlet userDefaultsWhatsNewVersionStore = UserDefaultsWhatsNewVersionStore()\n\n\u002F\u002F Persists presented versions in iCloud using the NSUbiquitousKeyValueStore\nlet ubiquitousKeyValueWhatsNewVersionStore = NSUbiquitousKeyValueWhatsNewVersionStore()\n\n\u002F\u002F Stores presented versions in memory. Perfect for testing purposes\nlet inMemoryWhatsNewVersionStore = InMemoryWhatsNewVersionStore()\n```\n\nIf 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.\n\n### NSUbiquitousKeyValueWhatsNewVersionStore\n\nIf you are making use of the `NSUbiquitousKeyValueWhatsNewVersionStore` please ensure to enable the [iCloud Key-value storage](https:\u002F\u002Fdeveloper.apple.com\u002Flibrary\u002Farchive\u002Fdocumentation\u002FGeneral\u002FConceptual\u002FiCloudDesignGuide\u002FChapters\u002FDesigningForKey-ValueDataIniCloud.html) capability in the \"Signing & Capabilities\" section of your Xcode project.\n\n\u003Cp align=\"center\">\n    \u003Cimg src=\"Assets\u002Ficloud-key-value-storage.png\" alt=\"iCloud Key-value storage\">\n\u003C\u002Fp>\n\n## WhatsNew\n\nThe following sections explains how a `WhatsNew` struct can be initialized in order to describe the new features for a given version of your app.\n\n```swift\nlet whatsnew = WhatsNew(\n    \u002F\u002F The Version that relates to the features you want to showcase\n    version: \"1.0.0\",\n    \u002F\u002F The title that is shown at the top\n    title: \"What's New\",\n    \u002F\u002F The features you want to showcase\n    features: [\n        WhatsNew.Feature(\n            image: .init(systemName: \"star.fill\"),\n            title: \"Title\",\n            subtitle: \"Subtitle\"\n        )\n    ],\n    \u002F\u002F The primary action that is used to dismiss the WhatsNewView\n    primaryAction: WhatsNew.PrimaryAction(\n        title: \"Continue\",\n        backgroundColor: .accentColor,\n        foregroundColor: .white,\n        hapticFeedback: .notification(.success),\n        onDismiss: {\n            print(\"WhatsNewView has been dismissed\")\n        }\n    ),\n    \u002F\u002F The optional secondary action that is displayed above the primary action\n    secondaryAction: WhatsNew.SecondaryAction(\n        title: \"Learn more\",\n        foregroundColor: .accentColor,\n        hapticFeedback: .selection,\n        action: .openURL(\n            .init(string: \"https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit\")\n        )\n    )\n)\n```\n\n### WhatsNew.Version\n\nThe `WhatsNew.Version` specifies the version that has introduced certain features to your app.\n\n```swift\n\u002F\u002F Initialize with major, minor, and patch\nlet version = WhatsNew.Version(\n    major: 1,\n    minor: 0,\n    patch: 0\n)\n\n\u002F\u002F Initialize by string literal\nlet version: WhatsNew.Version = \"1.0.0\"\n\n\u002F\u002F Initialize WhatsNew Version by using the current version of your bundle\nlet version: WhatsNew.Version = .current()\n```\n\n### WhatsNew.Title\n\nA `WhatsNew.Title` represents the title text that is rendered above the features.\n\n```swift\n\u002F\u002F Initialize by string literal\nlet title: WhatsNew.Title = \"Continue\"\n\n\u002F\u002F Initialize with text and foreground color\nlet title = WhatsNew.Title(\n    text: \"Continue\",\n    foregroundColor: .primary\n)\n\n\u002F\u002F On >= iOS 15 initialize with AttributedString using Markdown\nlet title = WhatsNew.Title(\n    text: try AttributedString(\n        markdown: \"What's **New**\"\n    )\n)\n```\n\n### WhatsNew.Feature\n\nA `WhatsNew.Feature` describe a specific feature of your app and generally consist of an image, title, and subtitle.\n\n```swift\nlet feature = WhatsNew.Feature(\n    image: .init(\n        systemName: \"wand.and.stars\"\n    ),\n    title: \"New Design\",\n    subtitle: .init(\n        try AttributedString(\n            markdown: \"An awesome new _Design_\"\n        )\n    )\n)\n```\n\n### WhatsNew.PrimaryAction\n\nThe `WhatsNew.PrimaryAction` allows you to configure the behaviour of the primary button which is used to dismiss the presented `WhatsNewView`\n\n```swift\nlet primaryAction = WhatsNew.PrimaryAction(\n    title: \"Continue\",\n    backgroundColor: .blue,\n    foregroundColor: .white,\n    hapticFeedback: .notification(.success),\n    onDismiss: {\n        print(\"WhatsNewView has been dismissed\")\n    }\n)\n```\n\n> Note: HapticFeedback will only be executed on iOS\n\n### WhatsNew.SecondaryAction\n\nA `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.\n\n```swift\n\u002F\u002F SecondaryAction that presents a View\nlet secondaryActionPresentAboutView = WhatsNew.SecondaryAction(\n    title: \"Learn more\",\n    foregroundColor: .blue,\n    hapticFeedback: .selection,\n    action: .present {\n        AboutView()\n    }\n)\n\n\u002F\u002F SecondaryAction that opens a URL\nlet secondaryActionOpenURL = WhatsNew.SecondaryAction(\n    title: \"Read more\",\n    foregroundColor: .blue,\n    hapticFeedback: .selection,\n    action: .open(\n        url: .init(string: \"https:\u002F\u002Fgithub.com\u002FSvenTiigi\u002FWhatsNewKit\")\n    )\n)\n\n\u002F\u002F SecondaryAction with custom execution\nlet secondaryActionCustom = WhatsNew.SecondaryAction(\n    title: \"Custom\",\n    action: .custom { presentationMode in\n        \u002F\u002F ...\n    }\n)\n```\n\n> Note: HapticFeedback will only be executed on iOS\n\n## Layout\n\nWhatsNewKit allows you to adjust the layout of a presented `WhatsNewView` in various ways.\n\nThe most simple way is by mutating the `WhatsNew.Layout.default` instance.\n\n```swift\nWhatsNew.Layout.default.featureListSpacing = 35\n```\n\nWhen using the automatic presentation style you can supply a default layout when initializing the WhatsNewEnvironment.\n\n```swift\n.environment(\n    \\.whatsNew,\n    .init(\n        defaultLayout: WhatsNew.Layout(\n            showsScrollViewIndicators: true,\n            featureListSpacing: 35\n        ),\n        whatsNew: self\n    )\n)\n```\n\nAlternatively you can pass a `WhatsNew.Layout` when automatically or manually presenting the WhatsNewView\n\n```swift\n.whatsNewSheet(\n    layout: WhatsNew.Layout(\n        contentPadding: .init(\n            top: 80,\n            leading: 0,\n            bottom: 0,\n            trailing: 0\n        )\n    )\n)\n```\n\n```swift\n.sheet(\n    whatsNew: self.$whatsNew,\n    layout: WhatsNew.Layout(\n        footerActionSpacing: 20\n    )\n)\n```\n\n## WhatsNewViewController\n\nWhen using `UIKit` or `AppKit` you can make use of the `WhatsNewViewController`.\n\n```swift\nlet whatsNewViewController = WhatsNewViewController(\n    whatsNew: WhatsNew(\n        version: \"1.0.0\",\n        \u002F\u002F ...\n    ),\n    layout: WhatsNew.Layout(\n        contentSpacing: 80\n    )\n)\n```\n\nIf 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.\n\n```swift\n\u002F\u002F Verify WhatsNewViewController is available for presentation\nguard let whatsNewViewController = WhatsNewViewController(\n    whatsNew: WhatsNew(\n        version: \"1.0.0\",\n        \u002F\u002F ...\n    ),\n    versionStore: UserDefaultsWhatsNewVersionStore()\n) else {\n    \u002F\u002F Version of WhatsNew has already been presented\n    return\n}\n\n\u002F\u002F Present WhatsNewViewController\n\u002F\u002F Version will be automatically saved in the provided\n\u002F\u002F WhatsNewVersionStore when the WhatsNewViewController gets dismissed\nself.present(whatsNewViewController, animated: true)\n```\n","WhatsNewKit 是一个用于轻松展示应用程序新功能的 Swift 包。它支持 SwiftUI、UIKit 和 AppKit，并且可以在 iOS、macOS 和 visionOS 上运行，提供了自动和手动两种展示模式。此外，该工具包允许开发者根据需要自定义布局，确保新功能介绍与应用风格一致。适用于任何希望以优雅方式向用户介绍其最新特性的移动或桌面应用程序开发场景。","2026-06-11 03:09:22","top_language"]