SvenTiigi / WhatsNewKit

Showcase your awesome new app features 📱
https://sventiigi.github.io/WhatsNewKit/
MIT License
3.87k stars 190 forks source link

Don't show Whats New Sheet on initial download #86

Closed FPST-08 closed 3 months ago

FPST-08 commented 3 months ago

Is your feature request related to a problem?

First off big thanks for this package. I am not sure whether this is actually a bug or a missing feature. Every time I run an app (whether mine or your example) with a freshly reseted simulator the sheet is presented. This is a big problem for my app since I need to display a different sheet on initial install. Currently I cannot test if this behavior persists on a real device. Some more details: I use automatic presentation. The environment is set in the app struct and the sheet modifier is applied to the TabView in ContentView. I use the UserDefaults Store.

What solution would you like?

I would love if there was an option to not show the sheet when the app is opened for the first time.

What alternatives have you considered?

I tried applying the modifier in different locations but that obviously had no effect.

Any additional context?

No response

SvenTiigi commented 3 months ago

Hi @FPST-08,

If you use the UserDefaultsWhatsNewVersionStore, the information about presented versions is stored in the UserDefaults and a property list file on disk. Resetting the simulator contents will remove this file and all other contents, resulting in the loss of information about which version has been presented. Consequently, the sheet will be presented "again" because there is no available information that the version has already been presented.

If you wish to further customize the behavior, consider writing your own implementation of a WhatsNewVersionStore or WhatsNewEnvironment.

FPST-08 commented 3 months ago

Hi @SvenTiigi, that makes sense. Why are you saving this information both in UserDefaults and a plist file? I played a bit around with your package and I think this might be the best way. I am sorry for not making this a pull request.
This is a part from the file View+WhatsNewSheet. ` public extension View {

/// Presents a WhatsNewView using the given WhatsNew object as a data source for the sheet’s content.
/// - Parameters:
///   - whatsNew: A Binding to an optional WhatsNew object
///   - versionStore: The optional WhatsNewVersionStore. Default value `nil`
///   - layout: The WhatsNew Layout. Default value `.default`
///   - onDismiss: The closure to execute when dismissing the sheet. Default value `nil`
func sheet(
    whatsNew: Binding<WhatsNew?>,
    versionStore: WhatsNewVersionStore? = nil,
    layout: WhatsNew.Layout = .default,
    onDismiss: (() -> Void)? = nil,
    hideOnInitialInstall: Bool = false
) -> some View {
    self.modifier(
        ManualWhatsNewSheetViewModifier(
            whatsNew: whatsNew,
            versionStore: versionStore,
            layout: layout,
            onDismiss: onDismiss,
            hideOnInitialInstall: hideOnInitialInstall
        )
    )
}

}

// MARK: - ManualWhatsNewSheetViewModifier

/// A Manual WhatsNew Sheet ViewModifier private struct ManualWhatsNewSheetViewModifier: ViewModifier {

// MARK: Properties

/// A Binding to an optional WhatsNew object
let whatsNew: Binding<WhatsNew?>

/// The optional WhatsNewVersionStore
let versionStore: WhatsNewVersionStore?

/// The WhatsNew Layout
let layout: WhatsNew.Layout

/// The closure to execute when dismissing the sheet
let onDismiss: (() -> Void)?

// Whether or not the sheet should be presented on initial launch
let hideOnInitialInstall: Bool

// MARK: ViewModifier

/// Gets the current body of the caller.
/// - Parameter content: The Content
func body(
    content: Content
) -> some View {
    // Check if a WhatsNew object is available
    if let whatsNew = self.whatsNew.wrappedValue {
        // Check if the WhatsNew Version has already been presented
        if self.versionStore?.hasPresented(whatsNew.version) == true {
            // Show content
            content
        } else if hideOnInitialInstall && ((self.versionStore?.presentedVersions.isEmpty) != nil) {
            content
                .onAppear {
                    versionStore?.save(presentedVersion: whatsNew.version)
                }
        } else {
            // Show WhatsNew Sheet
            content.sheet(
                item: self.whatsNew,
                onDismiss: self.onDismiss
            ) { whatsNew in
                WhatsNewView(
                    whatsNew: whatsNew,
                    versionStore: self.versionStore,
                    layout: self.layout
                )
            }
        }
    } else {
        // Otherwise show content
        content
    }
}

} ` I'll later try to use the intended GitHub tools for this but currently this is the best I can do. Is this something you would consider to implement?

SvenTiigi commented 3 months ago

Why are you saving this information both in UserDefaults and a plist file?

The UserDefaults API saves all key-value pairs in a property list by definition.

Happy to hear that this solution works for you đź‘Ť

As this implementation relies on if-statements within the body of a ViewModifier, I'm not considering to include it in this package which used by many other developers. This approach can lead to various issues, as described here.

I recommend using the automatic presentation mode and overriding the WhatsNewEnvironment to implement a custom logic that determines which WhatsNew object should be presented, as described here.