TipKit: "The filter function is not supported in this rule."

Greetings,

Working on adding some simple rules to my first TipKit tips. I'm following the example from the WWDC TipKit talk, which included a rule intended to ensure that the user had accessed a particular feature at least three times in the past five days. So it had code that looked like this:

#Rule(Self.enteredBackyardDetailView) {
    $0.donations
        .filter { $0.date > Date.now.addingTimeInterval(-5 * 60 * 60 * 24) }
        .count >= 3

I'm trying to do something similar -- essentially, I want to know if the user has been using this feature for at least a day. So I tried this:

#Rule(Self.viewedDetails) {
    $0.donations
        .filter { $0.date < Date.now.addingTimeInterval(-1 * 60 * 60 * 24) }
        .count > 0
            }

i.e., Is there at least one event that was donated more than a day ago?

However, Xcode flags the .filter line with the message "The filter function is not supported in this rule." Huh?

This smells to me a lot like the limitations that SwiftData has with not being able to do certain kinds of operations in predicates. But this was clearly intended to be supported in the design, since the exact example was shown in the WWDC session on TipKit.

So, am I missing something? Is there a different way to structure this so that I can use .filter, or is there some other way of expressing the condition without using filter?

I did try using .first { same expression } != nil, but Xcode then said that rules must include a count comparison...

Thanks!

Replies

Please show more code.

How did you declare viewedDetails ?

@available(iOS 17, *)
struct DetailsTappableItemsTip: Tip {
    
    static let viewedDetails: Event = Event(id: "viewedDetails")

    var title: Text {
        Text("Learn More About This")
    }
    
    var message: Text? {
        Text("Tip text here.")
    }
    
    var image: Image? {
        Image(.icInfo)
    }
    
    var rules: [Rule] {
        #Rule(Self.viewedDetails) {
            $0.donations
                .filter { $0.date < Date.now.addingTimeInterval(-1 * 60 * 60 * 24) }
                .count > 0
        }
    }
}

Just to follow up on this a bit, I did find that Apple apparently changed semantics since the WWDC video was made, and they introduced a DonationTimeRange structure that you can use like this:

#Rule(Self.viewedDetails) {
    $0.donations
        .donatedWithin(.days(5))
        .count > 0

Unfortunately, this doesn't provide a way to achieve what I want for one of my rules, which is to determine if the user has been using the app for at least a calendar day. You might think that you could construct an expression comparing the count of two .donatedWithin ranges:

#Rule(Self.viewedDetails) {
    $0.donations.donatedWithin(.weeks(52)).count > $0.donations.donatedWithin(.days(1)).count

But no, you can't. This doesn't work because the #Rule macro explicitly requires a count comparison. If you try to write it as a raw predicate expression, it will compile but assert at runtime.

So, at this point, I've given up trying to write the rule that I wanted to write. But perhaps someone will see the question and point out something that I've missed. Or perhaps Apple will see the question and understand that .donatedWithin doesn't meet everyone's needs.