Enable product view signals in an iOS app
The product view signal captures when a user views a product detail screen and tells marketers which products each contact is interested in, enabling segmentation by product and product category in Connect. The product can be physical, digital or a service like a tour or insurance package.
Availability: Premium and Ultimate
Languages: Swift and Objective-C
Implementation considerations
When to fire the signal
Fire the product view signal in viewDidLoad() once the product data is available. Use productView instead of pageView on product detail screens - productView carries the richer schema and is the semantically correct signal type for this context.
If your app shows multiple products simultaneously - for example, a shortlist of hotel rooms - fire one signal per product.
Relationship to the product configuration signal
The product view and product configuration signals work together:
productView- captures the product itself when the screen loadsproductConfiguration- captures user interactions on the product screen, such as selecting a color or size
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 |
|---|---|---|
productId | String | Unique product identifier (may match SKU) |
productName | String | Name of the product |
signalType | String | Signal type. Value: "productView". |
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. |
availability | String | Availability status (for example, "In Stock", "Out of Stock", "Back Order") |
brandName | String | Brand name of the product |
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 or difference between original and current price |
effect | String | Describes the effect of the signal on engagement. Use "positive" for product views. |
imageUrls | [String] | URLs of product images |
inventoryQuantity | Number | Number of units available |
model | String | Model number or description |
msrp | Number | Suggested retail price or original price |
name | String | A label to differentiate this signal from others (for example, "productView from iOS app"). Max 256 characters |
productCategory | String | Product category from your catalog |
productDescription | String | Description of the product |
productRating | Number | Rating of the product |
productStatus | String | Current lifecycle state (for example, "Active", "Discontinued", "Upcoming") |
productUrls | [String] | URLs of product pages |
promotionId | String | ID from a marketing campaign that led to this view |
shoppingCartUrl | String | URL or deep link to the shopping cart |
sku | String | SKU, if different from productId |
unitPrice | Number | Unit price of the product |
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).Pass
imageUrlsandproductUrlsas[String]arrays.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": "productView",
"productId": "SKU-12345",
"productName": "Wireless Headphones"
])#import <Connect/Connect.h>
[[ConnectCustomEvent sharedInstance] logSignal:@{
@"signalType": @"productView",
@"productId": @"SKU-12345",
@"productName": @"Wireless Headphones"
}];Complete example
This example fires a product view signal from a product detail screen when it loads. It includes pricing, category and optional contact mapping.
import Connect
override func viewDidLoad() {
super.viewDidLoad()
// Set up UI ...
var signal: [String: Any] = [
"signalType": "productView",
"productId": product.id,
"productName": product.name,
"unitPrice": product.price.doubleValue,
"productCategory": product.categoryName,
"currency": "USD",
"effect": "positive"
]
if UserSession.shared.isAuthenticated, let email = UserSession.shared.email {
signal["audience"] = ["Email Address": email]
}
let accepted = ConnectCustomEvent.sharedInstance().logSignal(signal)
NSLog("[Connect] productView signal accepted: \(accepted)")
}import Connect
struct ProductDetailScreen: View {
let product: Product
@State private var signalFired = false
var body: some View {
ProductDetailContent(product: product)
.onAppear {
guard !signalFired else { return }
signalFired = true
var signal: [String: Any] = [
"signalType": "productView",
"productId": product.id,
"productName": product.name,
"unitPrice": product.price.doubleValue,
"productCategory": product.categoryName,
"currency": "USD",
"effect": "positive"
]
if UserSession.shared.isAuthenticated, let email = UserSession.shared.email {
signal["audience"] = ["Email Address": email]
}
let accepted = ConnectCustomEvent.sharedInstance().logSignal(signal)
NSLog("[Connect] productView signal accepted: \(accepted)")
}
}
}
Note
.onAppearfires every time the view appears, including on back-navigation returns. The@Stateflag ensures the signal fires only once per view lifecycle.
#import <Connect/Connect.h>
- (void)viewDidLoad {
[super viewDidLoad];
// Set up UI ...
NSMutableDictionary *signal = [@{
@"signalType": @"productView",
@"productId": self.product.id,
@"productName": self.product.name,
@"unitPrice": @(self.product.price.doubleValue),
@"productCategory": self.product.categoryName,
@"currency": @"USD",
@"effect": @"positive"
} mutableCopy];
if ([[UserSession shared] isAuthenticated] && [[UserSession shared] email]) {
signal[@"audience"] = @{ @"Email Address": [[UserSession shared] email] };
}
BOOL accepted = [[ConnectCustomEvent sharedInstance] logSignal:signal];
NSLog(@"[Connect] productView signal accepted: %@", accepted ? @"true" : @"false");
}Verification
After running the app and opening a product detail screen, check the Xcode console for:
[Connect] productView 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.
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
