Skip to content

macOS Dialog that displays "What's New" information after an app update

License

Notifications You must be signed in to change notification settings

CleanCocoa/WhatsNewKit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

61 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

WhatsNewKit

Swift 5.x Version License Platform Carthage compatible

What's in here?

  • A convenient model to talk about Updates and Versions,
  • a UI to display your update notices in,
  • and UserDefaults extensions so the user only sees the messages when she needs to.

Requires macOS 10.11+

Overview

You tell WhatsNew about the current settings and then let it figure out if it needs to display the "What's New" update window or not.

func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Figures out settings & app version from UserDefaults.standard
    // and Bundle.main:
    let whatsNew = WhatsNew(configuration: .standardMain)

    // Set up information about the latest "What's New"-worthy version:
    let update = Update(version: Version(2, 2, 0), view: aViewWithAllInfos)

    // Show the update notice if the criteria match and store
    // the version information in UserDefaults.standard for the next run.
    whatsNew.displayIfNeededAndRegister(update: update)
}

Features

Keyboard controls

  • Use arrow keys to navigate
  • Use Escape or +. to close

Pagination controls stay put

The window may change its size to accommodate each update view, but the navigation controls stay in place on screen so your users don't have to awkwardly hunt down the "Next" button again and again.

Dark and Light Mode

v2.2.0 v1.8.0
Dark
Light

How to Use WhatsNewKit

Behavior

The main intention of WhatsNewKit is to display update information to existing users.

  1. Upon first launch of your app, WhatsNewKit is supposed to not display a "What's New" window.
  2. Existing users will only get new update notices exactly once.

This works with one or multiple updates that have been missed.

Set Up Details

There's not much you need to know that Xcode's code completion won't tell you, but here's a description anyway.

You can create WhatsNew.Configuration in 3 ways:

  • Manually, e.g. for testing or when you want to obtain the information from a different source: .init(isFirstLaunch:appVersion:lastWhatsNewVersion:)
  • Reading values from UserDefaults and Bundle: .init(userDefaults:appBundle:)
  • Reading values from the main bundle and standard user defaults: .standardMain

To create an Update, you use one of its designated initializers:

  • Update.init(version:windowTitle:view:) to pass it any NSView directly, or
  • Update.init(version:windowTitle:viewContainer:) to pass it a ViewContainer-conforming object, like a NSViewController you get from a Nib. (See below for Nib details.)

The parameter windowTitle is optional in all of these.

The resulting view will be displayed in the content section of the NSWindow of this framework

Armed with a Configuration and Update instances, you can create a WhatsNew instance and make it displayIfNeededAndRegister(updates:).

⚠️ Heads Up with the View Design ⚠️ Your views need to be laid-out very strict so that inserting them into the window using Auto Layout affects the window size appropriately. If your size or position constraints have a priority of NSLayoutPriorityWindowSizeStayPut, which is known to be 500, then the window will not size to fit the content. I found multi-line labels to be especially cumbersome and recommend you set an explicit size if you can.

How to Create Your View

You can pass any NSView subclass to Update.init(version:windowTitle:view:), no matter if it's loaded from a Storyboard, Nib, or created in code.

Nibs work especially well! 🎉

Look at the example app in this project for inspiration.

The trick is this:

  1. Create a UpdateViewFromNib wrapper: It loads a NSViewController from a Nib and obtains its main view.
    • UpdateViewFromNib.init(nibName:bundle:) behaves like the NSViewController initializer.
    • UpdateViewFromNib.init(version:bundle:) assumes the Nib file is called WhatsNew_vX_Y_Z, where X, Y, and Z are the major, minor, and patch version component of your Version object respectively. (Version(1,8,0) resolves to WhatsNew_v1_8_0.xib.)
  2. Use the variant Update.init(version:windowTitle:viewContainer:) to pass the UpdateViewFromNib object in as the viewContainer directly for your convenience.

Displaying the Update Notice

You can offer your app users an affordance, e.g. a menu item, to display what's new regardless of the setting.

  • Use WhatsNew.display(update:) to show exactly one version information.
  • Use WhatsNew.display(updates:) (note the plural!) to show multiple version informations.

Use WhatsNew.displayIfNeeded(update:) or WhatsNew.displayIfNeeded(updates:) to let WhatsNewKit figure out if the user needs to see any update info.

License

Copyright (c) 2019--2020 Christian Tietze. Distributed under the MIT License.