Enable wishlist-item-removed signals in an iOS app

The wishlist-item-removed signal fires when a user removes a product from their wishlist or list of saved items. Use this signal to track changes in saved product lists, compare wishlist adds and removes and identify patterns in how contacts manage their saved items.

Availability: Pro, Premium and Ultimate

Languages: Swift and Objective-C


Implementation considerations

When to fire the signal

Decide when to send the signal based on your app's behavior:

  • On tap - fire as soon as the user taps to remove or un-save the item. Captures intent even if the action fails.
  • After confirmation - fire after confirming the item was removed. Ensures accuracy but may miss failed attempts.

For most implementations, firing on tap is recommended.

Multiple removal flows

Users can remove items from a favorites list from different screens in your app:

  • Product detail view controller - tapping a filled heart button to un-save
  • Favorites or saved items screen - a dedicated remove button per item
  • Product listing - an un-save icon on a product card

Each screen may have access to different amounts of product data. Make sure all flows are covered and fire one signal per item - including when a user clears all favorites at once.

Contact mapping

Include the audience field to attribute this signal to a contact in Connect. See How behavior signals update Connect data for identifier formats and contact resolution details.


Configuration

Before adding behavior signals, integrate the Connect SDK into your app. A recent beta (debug) version of the SDK is required (2.1.17 or later). See the guide for your development language: Swift or Objective-C.

Method

  func logSignal(_ data: [String: Any]?) -> Bool

Sends the signal to the Acoustic Connect endpoint. Initialize the Connect SDK before calling this method using ConnectSDK.shared.enable(with:).

Pass all fields as a flat [String: Any] dictionary. The SDK handles the signal structure automatically.

Signal fields

Required

FieldTypeDescription
productIdStringUnique product identifier. If missing, the signal is discarded.
signalTypeStringSignal type. Value: "wishlistItemRemoved".

Optional

FieldTypeDescription
audience[String: Any]Key-value pairs for contact mapping. Keys must match contact attribute names exactly as they appear in Connect, including capitalization and spacing.
categoryStringSignal category. Value: "Behavior".
currencyStringISO 4217 currency code (for example, "USD", "EUR"). Defaults to the currency set for your Connect subscription.
descriptionStringA description of the signal
effectStringDescribes the effect of the signal on engagement.
externalVariantIdStringIdentifier for the specific product variant removed, such as a size or color
nameStringA label to differentiate this signal from others (for example, "wishlistItemRemoved from favorites screen"). Max 256 characters
priceNumberPrice of the product at the time it was removed from the wishlist
productCategoryStringCategory or department the product belongs to
productNameStringDisplay name of the product
wishlistIdStringIdentifier for the specific wishlist the item was removed from, useful if a contact can maintain more than one list

Things to know

  • price accepts a native Swift numeric type (Double, NSDecimalNumber).

  • Optional fields enhance the signal but do not prevent processing if omitted.

Basic example

import Connect

ConnectCustomEvent.sharedInstance().logSignal([
    "signalType": "wishlistItemRemoved",
    "productId": "SKU-12345",
    "productName": "Linen Throw",
    "price": 89.99,
    "currency": "USD",
    "effect": "negative"
])
#import <Connect/Connect.h>

[[ConnectCustomEvent sharedInstance] logSignal:@{
    @"signalType": @"wishlistItemRemoved",
    @"productId": @"SKU-12345",
    @"productName": @"Linen Throw",
    @"price": @89.99,
    @"currency": @"USD",
    @"effect": @"negative"
}];

Complete examples

Product detail view controller (heart toggle)

This example fires a wishlist item removed signal from a product detail view controller when the user taps a filled heart button to un-save the product. The signal fires only when the item is being removed, determined by the return value of FavoritesStore.shared.toggle(_:).

import Connect

@objc private func toggleFavorite() {
    let isFav = FavoritesStore.shared.toggle(product.id)
    favoriteItem.image = UIImage(systemName: isFav ? "heart.fill" : "heart")
    favoriteItem.tintColor = isFav ? Theme.accent : Theme.primary

    if !isFav {
        // Item was just removed from favorites
        var signal: [String: Any] = [
            "signalType": "wishlistItemRemoved",
            "productId": product.id,
            "productName": product.name,
            "price": product.price.doubleValue,
            "productCategory": product.categoryName,
            "currency": "USD",
            "category": "Behavior",
            "effect": "negative",
            "name": "wishlistItemRemoved from product detail"
        ]

        if UserSession.shared.isAuthenticated, let email = UserSession.shared.email {
            signal["audience"] = ["Email Address": email]
        }

        let accepted = ConnectCustomEvent.sharedInstance().logSignal(signal)
        NSLog("[Connect] wishlistItemRemoved signal accepted: \(accepted)")
    }
}
#import <Connect/Connect.h>

- (void)toggleFavorite {
    BOOL isFav = [FavoritesStore.shared toggle:self.product.id];
    self.favoriteItem.image = [UIImage systemImageNamed:isFav ? @"heart.fill" : @"heart"];
    self.favoriteItem.tintColor = isFav ? Theme.accent : Theme.primary;

    if (!isFav) {
        // Item was just removed from favorites
        NSMutableDictionary *signal = [@{
            @"signalType": @"wishlistItemRemoved",
            @"productId": self.product.id,
            @"productName": self.product.name,
            @"price": @(self.product.price.doubleValue),
            @"productCategory": self.product.categoryName,
            @"currency": @"USD",
            @"category": @"Behavior",
            @"effect": @"negative",
            @"name": @"wishlistItemRemoved from product detail"
        } mutableCopy];

        if ([[UserSession shared] isAuthenticated] && [[UserSession shared] email]) {
            signal[@"audience"] = @{ @"Email Address": [[UserSession shared] email] };
        }

        BOOL accepted = [[ConnectCustomEvent sharedInstance] logSignal:signal];
        NSLog(@"[Connect] wishlistItemRemoved signal accepted: %@", accepted ? @"true" : @"false");
    }
}

Favorites screen (remove button)

This example fires a wishlist item removed signal from the favorites screen when the user removes an item from the list. Capture the product data before calling the removal method.

import Connect

private func removeFromFavorites(product: Product) {
    // Capture product data before removing
    var signal: [String: Any] = [
        "signalType": "wishlistItemRemoved",
        "productId": product.id,
        "productName": product.name,
        "price": product.price.doubleValue,
        "productCategory": product.categoryName,
        "currency": "USD",
        "category": "Behavior",
        "effect": "negative",
        "name": "wishlistItemRemoved from favorites screen"
    ]

    if UserSession.shared.isAuthenticated, let email = UserSession.shared.email {
        signal["audience"] = ["Email Address": email]
    }

    FavoritesStore.shared.toggle(product.id)

    let accepted = ConnectCustomEvent.sharedInstance().logSignal(signal)
    NSLog("[Connect] wishlistItemRemoved signal accepted: \(accepted)")
}
#import <Connect/Connect.h>

- (void)removeFromFavorites:(Product *)product {
    // Capture product data before removing
    NSMutableDictionary *signal = [@{
        @"signalType": @"wishlistItemRemoved",
        @"productId": product.id,
        @"productName": product.name,
        @"price": @(product.price.doubleValue),
        @"productCategory": product.categoryName,
        @"currency": @"USD",
        @"category": @"Behavior",
        @"effect": @"negative",
        @"name": @"wishlistItemRemoved from favorites screen"
    } mutableCopy];

    if ([[UserSession shared] isAuthenticated] && [[UserSession shared] email]) {
        signal[@"audience"] = @{ @"Email Address": [[UserSession shared] email] };
    }

    [FavoritesStore.shared toggle:product.id];

    BOOL accepted = [[ConnectCustomEvent sharedInstance] logSignal:signal];
    NSLog(@"[Connect] wishlistItemRemoved signal accepted: %@", accepted ? @"true" : @"false");
}

Best practices

  1. Capture product data before the removal action - after removal the product may no longer be easily accessible.
  2. Fire one signal per item removed, even when a user clears the entire favorites list at once.
  3. Always include an audience identifier - pairing removals with additions is what makes win-back analysis work.
  4. Omit price and currency rather than sending placeholder values.

Verification

After tapping the heart icon again to remove a saved product or swiping to remove an item from the Favorites screen, check the Xcode console for:

[Connect] wishlistItemRemoved signal accepted: true

The false value means the SDK rejected the call - see Troubleshooting below. Once the console confirms acceptance, verify the signal is available in Connect.


Troubleshooting

Signal not appearing in Connect?

  • Confirm the Connect SDK is initialized before any signal calls. See Method.
  • Capture the return value: let accepted = ConnectCustomEvent.sharedInstance().logSignal(signal). A false return indicates the SDK rejected the call.
  • Confirm productId is present - it is required and its absence discards the entire signal.
  • Verify the appKey and postURL match the Connect org you're checking.

Contact not created or updated?

  • Verify attribute key names in audience match exactly how they appear in Connect - including capitalization and spacing.

Related pages


Did this page help you?