Repository navigation
Conversation
Add trackProductInsightsAgent{Focus,OutOfView,View,Views,QuestionClick,
QuestionSubmit,AnswerFeedback,ResultClick,AnswerView} with a public request
struct per event, plus the CIOProductInsightsAgentQuestion,
CIOProductInsightsAgentViewTimespan and CIOProductInsightsAgentFeedbackLabel
models.
- section is sent as a query param, resolved from the request, the config
default, then "Products"
- analyticsTags are merged with config.defaultAnalyticsTags
- view timespans are sent as ISO-8601 UTC date-times
- items and followUpQuestions are capped at 100; items never send quantity
- request-builder, worker and live integration tests for every event
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a Product Insights Agent Events subsection to README §12 and include the new request and model structs in the jazzy docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, live A/B tests - Use distinct clicked-item and seed values in result click tests and docs - Round view timespans to the nearest millisecond before formatting - Document position as zero-based - Add a live test per event with a userID and test cells set Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ISO8601DateFormatter already rounds to the nearest millisecond, so format dates directly and cover more millisecond values in the test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Add an internal CIOTrackProductInsightsAgentData protocol whose addCommonFields(to:) writes item_name, variation_id, thread_id and analytics_tags for all 9 events - Move the CIOItem body mapping to CIOItem.toDictionary() and reuse it in trackResultsImpressionView, which already sent the same shape Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tests - Move decorateRequest, httpMethod and httpBody into the CIOTrackProductInsightsAgentData protocol extension; each event now only provides eventFields() - Add a test per event that the request section overrides the config default - Rename CIOItem.toDictionary() to toImpressionDictionary() and document which events use that shape - Document the 1-1000 character limit on question fields - Define the timespan dates in the README example Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…et tests - Format view timespans with a fixed-format DateFormatter, since ISO8601DateFormatter's .withFractionalSeconds throws on iOS 11.0/11.1 - Resolve section and analytics tags for all 9 events in one private helper - Assert each event's body contains only keys accepted by the API - Document the 2000 character limit on answerText Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…comment - Test that the timespan formatter is UTC, en_US_POSIX and Gregorian, and that its output round-trips to the original instant - Correct the CIOItem.toImpressionDictionary() comment about purchase items Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The round-trip test needs an availability guard to avoid crashing on supported iOS 11.0 and 11.1 runtimes.
1 open issue
What changed in this PR
Adds Product Insights Agent tracking to the Swift SDK so native iOS apps can measure interactions on product detail pages.
Changes:
- Adds nine tracking methods, public request types, and supporting models.
- Centralizes shared request serialization, section resolution, and analytics-tag merging while preserving existing impression payloads.
- Adds request-builder, worker, and live integration tests, plus usage and API documentation.
| File | Description |
|---|---|
| README.md | Documents all nine events with Swift examples. |
| AutocompleteClientTests/FW/Logic/Worker/ConstructorIOTrackProductInsightsAgentViewTests.swift | Tests view payloads, configuration, and errors. |
| AutocompleteClientTests/FW/Logic/Worker/ConstructorIOTrackProductInsightsAgentViewsTests.swift | Tests visibility-timespan tracking and configuration. |
| AutocompleteClientTests/FW/Logic/Worker/ConstructorIOTrackProductInsightsAgentResultClickTests.swift | Tests recommendation clicks, including position zero. |
| AutocompleteClientTests/FW/Logic/Worker/ConstructorIOTrackProductInsightsAgentQuestionSubmitTests.swift | Tests question submissions and error handling. |
| AutocompleteClientTests/FW/Logic/Worker/ConstructorIOTrackProductInsightsAgentQuestionClickTests.swift | Tests question clicks and error handling. |
| AutocompleteClientTests/FW/Logic/Worker/ConstructorIOTrackProductInsightsAgentOutOfViewTests.swift | Tests out-of-view tracking and configuration. |
| AutocompleteClientTests/FW/Logic/Worker/ConstructorIOTrackProductInsightsAgentFocusTests.swift | Tests input-focus tracking and configuration. |
| AutocompleteClientTests/FW/Logic/Worker/ConstructorIOTrackProductInsightsAgentAnswerViewTests.swift | Tests answer views, empty text, and collection caps. |
| AutocompleteClientTests/FW/Logic/Worker/ConstructorIOTrackProductInsightsAgentAnswerFeedbackTests.swift | Tests answer feedback and configuration. |
| AutocompleteClientTests/FW/Logic/Worker/ConstructorIOIntegrationTests.swift | Adds live coverage for all nine events. |
| AutocompleteClientTests/FW/Logic/Request/TrackProductInsightsAgentViewsRequestBuilderTests.swift | Tests timespan formatting, round trips, and payload keys. |
| AutocompleteClientTests/FW/Logic/Request/TrackProductInsightsAgentViewRequestBuilderTests.swift | Verifies view request serialization. |
| AutocompleteClientTests/FW/Logic/Request/TrackProductInsightsAgentResultClickRequestBuilderTests.swift | Verifies click fields and position-zero serialization. |
| AutocompleteClientTests/FW/Logic/Request/TrackProductInsightsAgentQuestionSubmitRequestBuilderTests.swift | Verifies question-submit payloads. |
| AutocompleteClientTests/FW/Logic/Request/TrackProductInsightsAgentQuestionClickRequestBuilderTests.swift | Verifies question-click payloads. |
| AutocompleteClientTests/FW/Logic/Request/TrackProductInsightsAgentOutOfViewRequestBuilderTests.swift | Verifies out-of-view payloads. |
| AutocompleteClientTests/FW/Logic/Request/TrackProductInsightsAgentFocusRequestBuilderTests.swift | Verifies focus payloads. |
| AutocompleteClientTests/FW/Logic/Request/TrackProductInsightsAgentAnswerViewRequestBuilderTests.swift | Verifies answer payloads and collection limits. |
| AutocompleteClientTests/FW/Logic/Request/TrackProductInsightsAgentAnswerFeedbackRequestBuilderTests.swift | Verifies feedback payloads and both labels. |
| AutocompleteClient/FW/Logic/Worker/ConstructorIO.swift | Exposes nine methods with shared configuration handling. |
| AutocompleteClient/FW/Logic/Request/CIOTrackResultsImpressionViewData.swift | Reuses the shared impression-item serializer. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentViewsRequest.swift | Defines the public visibility-timespan request. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentViewsData.swift | Serializes questions and visibility timespans. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentViewRequest.swift | Defines the public view request. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentViewData.swift | Serializes view-event questions. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentResultClickRequest.swift | Defines recommendation-click parameters. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentResultClickData.swift | Serializes clicked and seed-product details. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentQuestionSubmitRequest.swift | Defines question-submission parameters. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentQuestionSubmitData.swift | Serializes submitted questions and topics. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentQuestionClickRequest.swift | Defines question-click parameters. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentQuestionClickData.swift | Serializes clicked questions and topics. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentOutOfViewRequest.swift | Defines out-of-view parameters. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentOutOfViewData.swift | Supplies the out-of-view endpoint. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentFocusRequest.swift | Defines input-focus parameters. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentFocusData.swift | Supplies the focus endpoint. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentData.swift | Centralizes common POST fields and serialization. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentAnswerViewRequest.swift | Defines answer-view parameters and documents limits. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentAnswerViewData.swift | Serializes answers and caps related collections. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentAnswerFeedbackRequest.swift | Defines answer-feedback parameters. |
| AutocompleteClient/FW/Logic/Request/CIOTrackProductInsightsAgentAnswerFeedbackData.swift | Serializes feedback labels and answer identifiers. |
| AutocompleteClient/FW/Logic/Request/CIOProductInsightsAgentViewTimespan.swift | Formats visibility intervals as UTC timestamps. |
| AutocompleteClient/FW/Logic/Request/CIOProductInsightsAgentQuestion.swift | Models question text and optional topics. |
| AutocompleteClient/FW/Logic/Request/CIOProductInsightsAgentFeedbackLabel.swift | Defines thumbs-up and thumbs-down values. |
| AutocompleteClient/FW/Logic/Request/CIOItem.swift | Adds reusable impression-item serialization. |
| AutocompleteClient/Constants/Constants.swift | Defines the nine tracking endpoints. |
| AutocompleteClient.xcodeproj/project.pbxproj | Registers new SDK and test sources. |
| .jazzy.yaml | Includes and groups the new public API documentation. |
Review effort: Balanced
| let parser = ISO8601DateFormatter() | ||
| parser.formatOptions = [.withInternetDateTime, .withFractionalSeconds] |
There was a problem hiding this comment.
Code Review
This PR adds 9 Product Insights Agent (PIA) tracking events to the Swift SDK following well-established patterns in the codebase. The implementation is thorough, well-documented, and comprehensively tested. The architecture is clean — a shared CIOTrackProductInsightsAgentData protocol eliminates duplication across all 9 events effectively.
Inline comments: 7 discussions added
Overall Assessment: ✅ Pass
| Protocol for the request data shared by every Product Insights Agent tracking event. | ||
| Conforming types only provide their URL and event-specific body fields. | ||
| */ | ||
| protocol CIOTrackProductInsightsAgentData: CIORequestData { |
There was a problem hiding this comment.
Suggestion: The protocol is declared internal (default), which is intentional since conforming types' Data structs are internal. However, it might be worth adding an explicit internal modifier or a comment clarifying this is intentional, since all public-facing types in this codebase consistently use explicit access modifiers. Purely cosmetic, but aids future maintainers scanning for the public surface.
This is a minor consistency nit — not blocking.
| } | ||
|
|
||
| dict["beacon"] = true | ||
| dict.merge(baseParams) { current, _ in current } |
There was a problem hiding this comment.
Suggestion: The merge strategy { current, _ in current } means eventFields() and the shared fields (item_id, beacon, etc.) win over baseParams. This is consistent with CIOTrackResultsImpressionViewData and presumably correct (base params shouldn't overwrite business logic fields). However, this silent "last-writer-loses" behaviour isn't obvious from context. A brief comment documenting the intent — e.g. // event fields take precedence over base params — would protect against future accidental reversals that could produce hard-to-debug 400 errors.
| // A fixed-format DateFormatter is used instead of ISO8601DateFormatter because setting | ||
| // `.withFractionalSeconds` throws an exception on iOS 11.0 and 11.1. DateFormatter is thread-safe, | ||
| // so a single shared instance is used for all timespans | ||
| static let dateFormatter: DateFormatter = { |
There was a problem hiding this comment.
Suggestion: DateFormatter is documented as thread-safe for string(from:) calls, and the comment in the code references this. That said, the comment currently says "DateFormatter is thread-safe, so a single shared instance is used for all timespans". Consider clarifying the specific thread-safety scope: DateFormatter is thread-safe for reading (formatting) after initial creation, but not for writes (changing dateFormat, locale, etc.). Since this formatter is static let with a fixed configuration and never mutated after creation, it is safe. A brief comment noting that the formatter must not be mutated after creation would guard against someone inadvertently adding formatter.dateFormat = ... elsewhere.
This is a documentation nit only — the current code is correct.
| // Builds the item object sent by trackResultsImpressionView and trackProductInsightsAgentAnswerView. | ||
| // Other events send different item shapes (purchase repeats each item `quantity` times and sends only item_id/variation_id, | ||
| // and ASA/search/browse/recommendations omit item_name) | ||
| func toImpressionDictionary() -> [String: String] { |
There was a problem hiding this comment.
Suggestion: toImpressionDictionary() is internal (no access modifier) but it's used by two callers: the existing CIOTrackResultsImpressionViewData and the new CIOTrackProductInsightsAgentAnswerViewData. The method's comment accurately describes the shared shape. Consider whether internal is the right default here versus making it fileprivate — but given cross-file usage is intentional, internal is correct.
One minor naming nit: the method is on CIOItem but the shape it produces is also used by PIA's answer_view event. The existing comment (// Builds the item object sent by trackResultsImpressionView and trackProductInsightsAgentAnswerView) has been updated to reflect the two callers, which is good. No change required.
| - completionHandler: The callback to execute on completion. | ||
| - makeData: Builds the event's request data from the resolved section and analytics tags | ||
| */ | ||
| private func trackProductInsightsAgent(sectionName: String?, analyticsTags: [String: String]?, completionHandler: TrackingCompletionHandler?, makeData: (String, [String: String]?) -> CIOTrackProductInsightsAgentData) { |
There was a problem hiding this comment.
Suggestion: The closure parameter label makeData is clear and the pattern matches how the ASA (Agent Search Assistant) events are structured. One thing worth noting for maintainability: the closure's parameter types (String, [String: String]?) are positional and undocumented. A future developer could accidentally transpose sectionName and an analytics tag key. Consider using a named-parameter closure type, e.g.:
makeData: (_ sectionName: String, _ analyticsTags: [String: String]?) -> CIOTrackProductInsightsAgentDataThis is a minor readability suggestion — the code is correct as-is and matches the SDK's existing style.

Summary
Adds the 9 Product Insights Agent (PIA) tracking events to the Swift SDK so native iOS apps can track PIA (CDX-708). Swift had no PIA tracking before this; Cencosud has more app traffic than web and can't measure PIA in-app. Android twin: CDX-709.
Changes
CIOTrackProductInsightsAgent<Event>Requestpassed totrackProductInsightsAgent<Event>(_:completionHandler:):Focus,OutOfView,View,Views,QuestionClick,QuestionSubmit,AnswerFeedback,ResultClick,AnswerView. Method names match JS.CIOProductInsightsAgentQuestion,CIOProductInsightsAgentViewTimespan, and theCIOProductInsightsAgentFeedbackLabelenum (.thumbsUp/.thumbsDown).CIOTrackProductInsightsAgentDataprotocol, which ownsdecorateRequest/httpMethod/httpBody. Each event only declares its URL andeventFields(). One privateConstructorIOhelper resolves the section and analytics tags for all 9.CIOItem.toImpressionDictionary()(internal), shared bytrackProductInsightsAgentAnswerViewand the existingtrackResultsImpressionView, which already sent this exact shape. The wire output oftrackResultsImpressionViewis unchanged..jazzy.yamlglobs and category for the new structs, and README §12 "Product Insights Agent Events".Key things to note
trackAgent*methods,sectionName/itemID/seedItemID/qnaResultID, not the newersectionnaming. This is intentional (decision C2 in the deviations doc).sectionis a query param on all 9, resolvedrequest.sectionName ?? config.defaultItemSectionName ?? "Products". It also ends up in the body via thebaseParamsmerge, the same way as the siblings. The server accepts that (live probe: 204).analyticsTagson all 9,question_topicon answer_view and insidequestions[], anditems/followUpQuestionscapped at 100.view_timespansare ISO-8601 UTC with ms (e.g.2026-10-22T02:40:05.123Z). These are formatted with a fixed-formatDateFormatterrather thanISO8601DateFormatter, because.withFractionalSecondsthrows on iOS 11.0/11.1 (the SDK minimum is 11.0). The output is byte-identical toISO8601DateFormatter, checked over 200k consecutive ms.itemIDis required on all 9. The spec marks it optional, but the server returns 400 when neitheritem_idnoritem_nameis sent.answerTextis limited to 2000 characters by the server, and longer bodies get a 400 that drops the whole event. That limit is documented; there is no client-side truncation. It may be worth a follow-up with the backend owners.question/answer_textsit in the body, soobfuscatePIIRequest(URL-only) doesn't see them. That's the same as every other body-based event, and JS doesn't do it either.Testing
position: 0sent,answerText: ""sent, 100 caps onitems/followUpQuestions, noquantity, timespan formatter pinned to UTC/en_US_POSIX/Gregorian, and round-trip to the original instant.userIDand A/BtestCellsto confirm the mergedui/ef-*body keys are accepted.IPHONEOS_DEPLOYMENT_TARGET=15.0on the command line. No project settings changed.Release
No version bump in this PR. It follows after merge, in its own "Version Bump" commit as usual.
🤖 Generated with Claude Code