TipKit-iOS

Hasan Ali Şişeci
Appcent
Published in
8 min readNov 20, 2023

If you don’t want any features to be overlooked in your feature-rich applications, here’s TipKit.

Introduce your new features with TipKit to make it easier for your users to familiarize themselves with your app and enhance the user experience.

Today, we will explore TipKit, introduced by Apple with iOS 17 during WWDC23. You might have encountered informative alerts on a button or a view in an app you downloaded from the App Store or in default apps on your iPhone after the iOS 17 update. The topic we will discuss today is precisely about these alerts, or more specifically, informative text boxes.

Yes, at this point, we’ve actually understood a bit more about what we’re going to do. You might have initially thought, “Why use a framework when we could have handled this with a simple view?” However, here the convenience provided by the framework lies not in the view itself but in the parameters, events, options, and rules used together to display these tooltip boxes. Otherwise, you’re absolutely right, we could have written a simple view :)

Let’s create a sample screen where we’ll add the simplest form of a TipView. The first step to creating a TipView is to prepare content for this tip box. First, we’ll create a struct using the Tip protocol. With the Tip protocol, we will define the text and visuals of the tip, and in the following steps, we will add rules to this structure.

Later on, within our view, we’ll create a variable from this struct and use it as a parameter inside the TipView provided by TipKit.

Inline TipView

In this example, we will use TipView inline, meaning it will be directly among our UI elements. This tooltip style allows the basic view to smoothly change the layout with animations to accommodate the tooltip view and its dismissal.

The second parameter we provide inside TipView, which is “arrowEdge,” determines the direction in which the arrow of TipView will point.

The “invalidate” operation we perform within the button permanently invalidates the tip and prevents it from being displayed. The “reason” parameter here expects a value of the “InvalidationReason” enum type. All cases of this enum are as follows:

  • .actionPerformed: The user performed the action described by the tip..
  • displayCountExceeded: The tip exceeded its maximum display count..
  • tipClosed: The user explicitly closed the tip view while it was displaying.

Additionally, the user can use the close button in the upper right corner of the tip view to dismiss the tip.

How to load Tips

Yes, we’ve prepared our view to show the tip to our user, but before tips are displayed, they need to be loaded into the application. The configure(_:) function, accessible through Tips, initializes and loads all tips used by the application. As a best practice, this operation is typically called once per app session, ideally within the init() method of the struct assigned to the App protocol with "@main".

And now, we can take a look at our example:

Popover TipView

If you prefer not to use an inline layout in your application or if there’s no need to interact with UI elements covered by the tooltip, you can use this tooltip style. The Popover displays the tooltip view on top of the current screen layout while obscuring UI elements underneath.

Additionally, following Apple’s recommendation when using a popover, it’s suggested to avoid adding images since the tooltip already points to the highlighted feature.

Firstly, let’s prepare a model for our Popover Tip example, and for this case, let’s not include an image.

And then, let’s prepare our view. Unlike in the inline case, we won’t use TipView here. Instead, we’re adding the tooltip as a UI element to a view using a popoverTip() modifier.

Then, let’s take a look at our screen:

Another feature of Popover tips is that they close when tapped anywhere on the screen but reappear when the screen is opened again. However, they won’t be shown again if closed using the close button on the tooltip or by invalidate it.

Actions

Now, let’s see how to add actions to the struct we created in accordance with the Tip protocol and how users can interact with these actions. As we saw earlier, we create our struct, and this time, in addition to title, message, and image, we add an array of actions named “actions.” This action struct expects an id and a title from us.

Then, inside TipView, using the action closure, we can decide on the actions to be taken based on the id of the action. Here, functions like redirecting to a browser with a URL or opening a bottom sheet are commonly called.

And with actions, we see a tooltip view on our screen like this:

Parameters and Rules

When we want to display a tip conditionally, we create parameters and rules within the struct we created from the Tip protocol.

Here, we use the Parameter property wrapper when adding our parameter. We also define our parameter as static and later access this value through our struct. When creating a rule with this parameter, we use the Rule macro within the rules array.

Next, let’s add a classic inline tip view. Then, let’s use our parameter through TipWithParameter to access our parameter using a button and apply our rule.

Then, let’s take a look at the resulting structure:

The point I want to emphasize here is that the rule runs until the user presses the close button on the TipView. After pressing this button, the TipView is no longer triggered.

Events

If you want to show tips based on user actions in your application, you can use events for this. For example, if the position of an existing button in your application has changed, and you’ve detected that users are clicking on the old location of the button, you may want to show a tooltip. In this case, you can create a variable of type Event within the struct created from the Tip protocol to track this.

When creating the rule, we refer to the event we defined. In this example, our tip will be displayed if the referenced event occurs three or more times.

Let’s then prepare the view side. Here, what we need to do is to access our event and use the donate() function. This function increases the donation count of our event value.

The reason we use Task here is that the donate() function is asynchronous. Another alternative that is not asynchronous is the sendDonation() function.

Then, here’s our view where we created a rule with the event:

Option

If you want to build logic based on the behavior of your tip, this time we can use structures called Option. The Tip protocol allows you to determine when the tip will be displayed using Option.

In our example, we set MaxDisplayCount for the tip as 2, thus avoiding displaying the tip to the user constantly. We also have an option called IgnoresDisplayFrequency. This controls whether a tip will adhere to the preconfigured display frequency interval and expects a boolean parameter. The default value is false.

Combining Rules

If we need interconnected rules, we should add rules that work together to our rules array. For example, if we have a page, and we want a tip to be displayed when a person enters this page both when logged in and at least three times, we need to create a rule for this.

This way, we can create a rule sequence using both a parameter and an event together.

Customizing TipView

TipKit offers the following methods to further customize the appearance and behavior of your tip. Since the explanations of these methods are available within Xcode, I won’t go into detail here.

Finally

Sure, here is the translation in technical English:

Lastly, there are two small points I’d like to mention. First and foremost, when performing the configure(_:) operation, within the configure method, we can provide an array of ConfigurationOption as a parameter.

In this array, we can include the following options:

  • displayFrequency: Specifies how often the tips will be displayed. Here, you can choose options such as hourly, daily, weekly, monthly, or immediate from an enum. The default is set to .immediate.
  • datastoreLocation: Allows you to specify a custom location for the tip data. The default is set to .applicationDefault.

Moving on to the second point, when a tip is no longer eligible for display, it becomes invisible in the application. While this makes sense during actual user interaction with the application, it can complicate testing the appearance and placement of tips during the code development process.

To facilitate testing when developing with TipKit, the following sample code utilizes various testing APIs to override the eligibility rules of a tip, and show or hide tips irrespective of their previous eligibility or status. These testing APIs include:

An important note is that the overrides mentioned above, such as showAllTipsForTesting() and resetDatastore(), should be called before invoking the configure(_:) function.

TipKit documentation: developer.apple.com/documentation/tipkit

That wraps up our discussion on TipKit. Until next time in another article! Happy coding!

--

--