Define rules for displaying tips in TipKit

AsyncLearn
5 min readOct 23, 2023

--

You can define rules to control when a tip should be displayed. TipKit offers two ways to do this: based on Parameters and on Events.

If you still don’t know what TipKit is and want to learn how to use it, I recommend this article on our blog before continuing.

SwiftUI Example App

As a demonstration, create an iOS project with SwiftUI in Xcode 15 and replace the content of the ContentView.swift file with the following code:

import SwiftUI
import TipKit

// 1
struct CountTip: Tip {
var title: Text {
Text("Press the text to count")
}

var message: Text? {
Text("The text will change when you tap it.")
}

var image: Image? {
Image(systemName: "hand.tap.fill")
}
}

// 2
struct ContentView: View {
@State var number = 0

var countTip = CountTip()

var body: some View {
VStack {
TipView(countTip)

Text("\(number)")
.onTapGesture {
number += 1
}
.font(.title)
}
.padding()
}
}

# Preview {
ContentView()
}

This code defines:

  1. A tip with a title, message, and image.
  2. A SwiftUI view with the CountTip tip and counts the number of times the text is pressed. It uses a state variable number to store the count of times the text is pressed and displays the number in a title format.

Open the structure that conforms to the App protocol, add import TipKit, and the following code after the definition of the main view inside the WindowGroup:

.task {
try? Tips.resetDatastore()
try? Tips.configure()
}

In this code, you enable TipKit and reset all tips to their initial state to allow displaying the tips each time you run the app on the simulator or device.

When you run the project, the example app should look as follows:

SwiftUI view example

If you want to test the tips from the Xcode Canvas, add the above code within the #Preview of the ContentView, like this:

# Preview {
ContentView()
.task {
try? Tips.resetDatastore()
try? Tips.configure()
}
}

Parameters

Rules based on parameters relate to the app’s state, which means their value persists unless you use Tips.resetDatastore().

In the example app, to add a parameter that displays the tip when the text is pressed, go to the CountTip structure and add the following code after the image variable:

// 1
@Parameter
static var isTextTapped: Bool = false

// 2
var rules: [Rule] {
[
// 3
#Rule(Self.$isTextTapped) {
// 4
$0 == true
}
]
}
  1. The isTextTapped variable of type Bool with the @Parameter property wrapper.
  2. The rules property, which belongs to the Tip protocol, contains an array of rules that determine when the tip should be displayed.
  3. The isTextTapped parameter is added using the #Rule macro.
  4. The condition for this rule is defined, which in this case means that the isTextTapped parameter must be equal to true.

Go to the ContentView view and add the following code inside the .onTapGesture modifier of the Text:

CountTip.isTextTapped.toggle()

This code toggles the value of the isTextTapped parameter. When it's true, the tip is displayed; otherwise, it's hidden.

When you run the project and press the Text, the example app should show/hide the tip:

Tip with parameter-based rules

If you want the value of isTextTapped to return to its initial state (in this case false) the first time it's referenced, use the .transient option in the @Parameter property wrapper, like this:

@Parameter(.transient)

Events

Rules based on events are related to user interactions. For example, to show the CountTip when the user has pressed the text more than 5 times, go to the CountTip structure and add the following after the isTextTapped parameter:

static let didTriggerTapTextEvent = Event(id: "didTriggerTapTextEvent")

This code defines the didTriggerTapTextEvent event with an identifier to track the number of times the user has pressed the text.

Now, replace the rules variable with this code:

// 1
var rules: [Rule] {
[
// 2
#Rule(Self.didTriggerTapTextEvent) {
// 3
$0.donations.count > 5
}
]
}
  1. The rules property, which belongs to the Tip protocol, contains an array of rules that determine when the tip should be displayed.
  2. The didTriggerTapTextEvent event is added using the #Rule macro.
  3. The condition for this rule is defined, which in this case means that the didTriggerTapTextEvent event must have occurred more than 5 times. The donations property keeps track of the number of times an event has occurred and other related information.

Go to the ContentView view and add the following code inside the .onTapGesture modifier of the Text:

Task { await CountTip.didTriggerTapTextEvent.donate() }

This code asynchronously registers the didTriggerTapTextEvent event when the text is pressed.

When you run the project and press the Text more than 5 times, the example app should display the tip:

Tip with event-based rules

You can specify that the event occurs within a time period using the donatedWithin(_:) function. For example:

#Rule(Self.didTriggerTapTextEvent) {
$0.donations.donatedWithin(.week).count > 5
}

This rule sets the tip to display when the didTriggerTapTextEvent event has occurred more than 5 times in the last week. Instead of .week, you can use .minute, .hour, .day, or a variant with a specific value.

You can also be more specific with events, as they support associated values, such as a data model:

static let didTriggerTapTextEvent = Event<MyModel>(id: "didTriggerTapTextEvent")

You can use this event to create a rule that only displays the tip when some property of MyModel meets certain requirements.

Combining Rules

As you have seen, the rules property of the Tip protocol can contain an array of rules, so you can add as many parameters and events as you like and combine them. When combining rules, all of them must meet their conditions to display the tip:

var rules: [Rule] {
[
#Rule(Self.$isTextTapped) {
$0 == true
},
#Rule(Self.didTriggerTapTextEvent) {
$0.donations.count > 5
}
]
}

In this example, both rules must be met to display the tip: the isTextTapped parameter must be true, and the didTriggerTapTextEvent event must have occurred more than 5 times.

If you want to read the Spanish version of this article, you can find it here: https://asynclearn.com/blog/reglas-tipkit/

--

--

AsyncLearn

Stay up-to-date in the world of mobile applications with our specialised blog.