Add trackConversion for sending conversion events#96
Conversation
Exposes a new public method on Parsely for firing conversion events (newsletter signups, subscriptions, purchases, link clicks, lead capture, and arbitrary custom conversions) using the existing event queue, flush, and mobileproxy pipeline. - `ConversionType` enum mirrors the categories accepted by the Parse.ly conversions backend (newsletter_signup, lead_capture, link_click, subscription, purchase, custom). - `trackConversion(url:conversionType:conversionLabel:...)` merges `_conversion_type` and `_conversion_label` into the event's `extra_data` alongside caller-supplied keys. Reserved keys can't be overridden by caller `extraData`. - The event is dispatched through `Track.conversion(...)` mirroring the existing `pageview(...)` shape — no changes to `Event`, the queue, the flush timer, or `RequestBuilder`. - New tests cover the queue path, the reserved-key guarantee, and the public Parsely.trackConversion entry point. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two buttons on the First tab — "Track Pageview (sandbox)" and "Track Conversion (sandbox)" — that fire against the `sandbox.joshhanson.io` apikey on a shared test URL. The pageview lets a session accrue history before the conversion fires so the conversions topology has something to attribute to. Used during the manual end-to-end verification of trackConversion: with Proxyman attached, confirm action=conversion and the _conversion_type / _conversion_label keys land in the on-wire payload, then watch dash.parsely.com for the conversion in the conversions report. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds first-class conversion tracking to the iOS SDK so apps can emit conversion events (e.g., subscription/purchase/newsletter signup) via the existing event pipeline.
Changes:
- Introduces
Parsely.trackConversion(...)and a publicConversionTypeenum for supported conversion categories. - Implements conversion event creation in
Track.conversion(...)by emittingaction = "conversion"and merging reserved conversion keys intoextra_data. - Adds unit tests and Demo app UI actions to exercise sandbox pageview + conversion flows; updates changelog.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| Tests/TrackTests.swift | Adds unit tests validating conversion event shape and reserved-key overwrite behavior. |
| Tests/ParselyTrackerTests.swift | Adds a public-API test ensuring Parsely.trackConversion enqueues a conversion event. |
| Sources/Track.swift | Adds Track.conversion(...) to construct and enqueue conversion events with reserved keys in extra_data. |
| Sources/ParselyTracker.swift | Adds ConversionType enum and trackConversion(...) public API wired into the event processor. |
| Demo/ParselyDemo/FirstViewController.swift | Adds sandbox pageview + conversion IBAction handlers for manual verification. |
| Demo/ParselyDemo/Base.lproj/Main.storyboard | Adds “Sandbox” label and buttons wired to new demo actions. |
| CHANGES.rst | Documents the new trackConversion API and ConversionType. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private func _trackConversion( | ||
| url: String, | ||
| conversionType: ConversionType, | ||
| conversionLabel: String, | ||
| urlref: String, | ||
| metadata: ParselyMetadata?, | ||
| extraData: Dictionary<String, Any>?, | ||
| siteId: String | ||
| ) { | ||
| var _siteId = siteId | ||
| if _siteId == "" { | ||
| _siteId = self.apikey | ||
| } | ||
| os_log("Tracking Conversion", log: OSLog.tracker, type: .debug) | ||
| track.conversion( |
There was a problem hiding this comment.
@randyriback I think this is a legit concern. However, I'm not sure logging an error and failing silently would be a good experience for the users.
I'd love to require a NonEmpty<String> but that might be too overbearing on the users, too.
Maybe we can accept the ambiguity for the time being and follow up at some point with a better SDK design. One that throws errors or that has DTOs with validation in the init as input parameters for the tracking calls.
What do you think?
mokagio
left a comment
There was a problem hiding this comment.
Looking good. The Copilot concern on validation and backend dropping events is legit, but I don't know how to handle it neatly here without restructuring the code.
Approving to unblock.
Notice that CI did not run on this PR, but I opened #97 to update the setup and verified it runs fine:
Co-authored-by: Gio Lodi <giovanni.lodi42@gmail.com>
Address review feedback (#PR): the doc promised the conversions backend drops events without a label, but the implementation would still enqueue and send them — a guaranteed no-op event from the customer's perspective. Now `_trackConversion` early-returns with a `.error`-level os_log when `conversionLabel.isEmpty`, so the SDK matches its own documentation and gives the developer a loud signal during integration instead of silently shipping events that will never appear in reporting. - Public doc updated to reflect the SDK-side skip. - New test `testTrackConversionWithEmptyLabelDoesNotEnqueue` verifies an empty label does not produce a queued event. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Parsely.trackConversion(url:conversionType:conversionLabel:...)for firing conversion events (newsletter signups, subscriptions, purchases, link clicks, lead capture, custom). Internally constructs an event withaction="conversion"and merges_conversion_typeand_conversion_labelintoextra_data, which the Parse.ly conversions backend (conversions-realtime) extracts and uses as the goal identity.ConversionTypeenum mirrors the categories accepted server-side (newsletter_signup,lead_capture,link_click,subscription,purchase,custom).mobileproxyPOST — zero changes to the network layer,Event,RequestBuilder, orHttpClient.Test plan
Unit tests (87 passing, 3 new)
TrackTests.testConversion— verifiesTrack.conversion(...)enqueues an event withaction == "conversion", that_conversion_typeand_conversion_labelare merged intoextra_data, and that caller-suppliedextra_datakeys are preserved.TrackTests.testConversionReservedKeysOverwriteCallerExtraData— verifies the reserved keys_conversion_typeand_conversion_labelcannot be overridden by caller-suppliedextraData.ParselyTrackerTests.testTrackConversion— verifies the publicParsely.trackConversion(...)entry point queues an event of the right shape via the standard event processor.Full suite (
make test): Executed 87 tests, with 0 failures (0 unexpected) on iPhone 16 / iOS 18.6.Manual end-to-end
iPhone 16/ iOS 18.6 andiPhone 17 Pro/ iOS 26.4).sandbox.joshhanson.ioapikey.os_logevent dump — confirmedaction: "conversion",data._conversion_type: "subscription",data._conversion_label: "...", caller-supplied keys (plan,source) preserved,parsely_site_uuidattached.Sending request to https://p1.parsely.com/mobileproxylogged after the 30s flush (also forced via Home/background lifecycle).dash.parsely.comfor thesandbox.joshhanson.ioapikey across multiple test runs and labels.Testing screenshots