---
updatedAt: 2026-08-10T16:53:05.000Z
---

# 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](https://developer.goacoustic.com/acoustic-connect/docs/how-behavior-signals-are-processed-in-connect) 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](https://developer.goacoustic.com/acoustic-connect/docs/integrate-connect-library-into-native-ios-app-swift) or [Objective-C](https://developer.goacoustic.com/acoustic-connect/docs/integrate-connect-sdk-into-ios-app-objective-c).

## Method

<Tabs>
  <Tab title="Swift">
    ```swift Swift
    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.
  </Tab>

  <Tab title="Objective-C">
    ```objectivec
    - (BOOL)logSignal:(NSDictionary *)values;
    ```

    Sends the signal to the Acoustic Connect endpoint. Initialize the Connect SDK before calling this method using `[[ConnectApplicationHelper sharedInstance] enableFramework:withPostMessageUrl:]`.

    Pass all fields as an `NSDictionary`. The SDK handles the signal structure automatically.
  </Tab>
</Tabs>

## Signal fields

### Required

| Field        | Type   | Description                                                     |
| ------------ | ------ | --------------------------------------------------------------- |
| `productId`  | String | Unique product identifier. If missing, the signal is discarded. |
| `signalType` | String | Signal type. Value: `"wishlistItemRemoved"`.                    |

### Optional

| Field               | Type            | Description                                                                                                                                           |
| ------------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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. |
| `category`          | String          | Signal category. Value: `"Behavior"`.                                                                                                                 |
| `currency`          | String          | ISO 4217 currency code (for example, `"USD"`, `"EUR"`). Defaults to the currency set for your Connect subscription.                                   |
| `description`       | String          | A description of the signal                                                                                                                           |
| `effect`            | String          | Describes the effect of the signal on engagement.                                                                                                     |
| `externalVariantId` | String          | Identifier for the specific product variant removed, such as a size or color                                                                          |
| `name`              | String          | A label to differentiate this signal from others (for example, `"wishlistItemRemoved from favorites screen"`). Max 256 characters                     |
| `price`             | Number          | Price of the product at the time it was removed from the wishlist                                                                                     |
| `productCategory`   | String          | Category or department the product belongs to                                                                                                         |
| `productName`       | String          | Display name of the product                                                                                                                           |
| `wishlistId`        | String          | Identifier for the specific wishlist the item was removed from, useful if a contact can maintain more than one list                                   |

<Callout icon="✅" theme="okay">
  ### Things to know

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

  - Optional fields enhance the signal but do not prevent processing if omitted.
</Callout>

## Basic example

```swift
import Connect

ConnectCustomEvent.sharedInstance().logSignal([
    "signalType": "wishlistItemRemoved",
    "productId": "SKU-12345",
    "productName": "Linen Throw",
    "price": 89.99,
    "currency": "USD",
    "effect": "negative"
])
```
```objectivec
#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(_:)`.

```swift
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)")
    }
}
```
```objectivec
#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.

```swift
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)")
}
```
```objectivec
#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](#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

* [Behavior signals in a native iOS app](https://developer.goacoustic.com/acoustic-connect/docs/behavior-signals-in-a-native-ios-app)
* [How behavior signals are processed in Connect](https://developer.goacoustic.com/acoustic-connect/docs/how-behavior-signals-are-processed-in-connect)