Enable removed-from-cart signals in an iOS app
The removed-from-cart signal captures when a user removes an item from their cart or selection - for example, a product, a flight or a hotel room. It helps identify friction points in the buying journey, trigger targeted re-engagement campaigns and enables segmentation by product and product category in Connect.
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 gesture or button tap - fire immediately when the user removes the item. Best for tracking intent, even if the action fails (for example, due to a network error).
- After cart update - fire after confirming the item was successfully removed. Ensures accuracy but may miss failed attempts.
For most implementations, firing on gesture or tap is recommended.
Multiple removal flows
Your app may have more than one path to remove an item from the cart:
- Swipe-to-delete gesture on a cart row
- Stepper decremented to zero
- Explicit remove button
Each flow may need a separate signal call if product data is structured differently. Make sure all flows are covered.
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. See the guide for your development language: Swift or Objective-C.
Method
func logSignal(_ data: [String: Any]?) -> BoolSends 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 - you do not need to construct a nested object.
- (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 - you do not need to construct a nested object.
Signal fields
Required
| Field | Type | Description |
|---|---|---|
itemQuantity | Number | Quantity of items removed from the cart |
productId | String | Unique product identifier (may match SKU) |
productName | String | Name of the product |
signalType | String | Signal type. Value: "removedFromCart". |
unitPrice | Number | Unit price of the product |
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 |
discount | Number | Discount amount applied to the item |
name | String | A label to differentiate this signal from others (for example, "removedFromCart from cart screen"). Max 256 characters |
productCategory | String | Product category from your catalog (useful for segmentation) |
productDescription | String | Description of the product |
promotionId | String | ID from a marketing campaign associated with this product |
shoppingCartUrl | String | URL or deep link to the shopping cart |
virtualCategory | String | Category based on navigation path (for example, "New arrivals", "Sale") |
Things to know
Number fields accept native Swift numeric types (
Int,Double,NSDecimalNumber).If a required field is missing or invalid, the entire signal is discarded. For fields that don't apply to your business, use a placeholder value.
Basic example
import Connect
ConnectCustomEvent.sharedInstance().logSignal([
"signalType": "removedFromCart",
"productId": "SKU-12345",
"productName": "Wireless Headphones",
"itemQuantity": 1,
"unitPrice": 299.99
])#import <Connect/Connect.h>
[[ConnectCustomEvent sharedInstance] logSignal:@{
@"signalType": @"removedFromCart",
@"productId": @"SKU-12345",
@"productName": @"Wireless Headphones",
@"itemQuantity": @1,
@"unitPrice": @299.99
}];Complete example
This example fires a removed-from-cart signal when a user swipes to delete a cart row, with dynamic product data and optional contact mapping.
import Connect
func tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath)
-> UISwipeActionsConfiguration?
{
let remove = UIContextualAction(style: .destructive, title: "Remove") { [weak self] _, _, completion in
let line = CartStore.shared.lineItems[indexPath.row]
CartStore.shared.removeLineItem(at: indexPath.row)
var signal: [String: Any] = [
"signalType": "removedFromCart",
"productId": line.product.id,
"productName": line.product.name,
"itemQuantity": line.quantity,
"unitPrice": line.product.price.doubleValue,
"productCategory": line.product.categoryName,
"currency": "USD"
]
if UserSession.shared.isAuthenticated, let email = UserSession.shared.email {
signal["audience"] = ["Email Address": email]
}
let accepted = ConnectCustomEvent.sharedInstance().logSignal(signal)
NSLog("[Connect] removedFromCart signal accepted: \(accepted)")
completion(true)
}
remove.image = UIImage(systemName: "trash")
return UISwipeActionsConfiguration(actions: [remove])
}#import <Connect/Connect.h>
- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView
trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
{
__weak typeof(self) weakSelf = self;
UIContextualAction *remove = [UIContextualAction
contextualActionWithStyle:UIContextualActionStyleDestructive
title:@"Remove"
handler:^(UIContextualAction *action, UIView *view, void (^completion)(BOOL)) {
CartLineItem *line = CartStore.shared.lineItems[indexPath.row];
[CartStore.shared removeLineItemAtIndex:indexPath.row];
NSMutableDictionary *signal = [@{
@"signalType": @"removedFromCart",
@"productId": line.product.id,
@"productName": line.product.name,
@"itemQuantity": @(line.quantity),
@"unitPrice": @(line.product.price.doubleValue),
@"productCategory": line.product.categoryName,
@"currency": @"USD"
} mutableCopy];
if ([[UserSession shared] isAuthenticated] && [[UserSession shared] email]) {
signal[@"audience"] = @{ @"Email Address": [[UserSession shared] email] };
}
BOOL accepted = [[ConnectCustomEvent sharedInstance] logSignal:signal];
NSLog(@"[Connect] removedFromCart signal accepted: %@", accepted ? @"true" : @"false");
completion(YES);
}];
remove.image = [UIImage systemImageNamed:@"trash"];
return [UISwipeActionsConfiguration configurationWithActions:@[remove]];
}Verification
After running the app and removing a cart item, check the Xcode console for:
[Connect] removedFromCart 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 and check that it is
true:let accepted = ConnectCustomEvent.sharedInstance().logSignal(signal). Afalsereturn indicates the SDK rejected the call. - Verify the
appKeyandpostURLmatch the Connect org you're checking.
Signal marked as invalid in Connect?
- Confirm all required fields are present:
signalType,productId,productName,itemQuantity,unitPrice.
Contact not created or updated?
- Verify attribute key names in
audiencematch exactly how they appear in Connect - including capitalization and spacing.
Related pages
Updated about 1 hour ago
