Skip to content

Add trackConversion for sending conversion events#96

Open
randyriback wants to merge 4 commits into
Parsely:masterfrom
randyriback:rr-track-conversion
Open

Add trackConversion for sending conversion events#96
randyriback wants to merge 4 commits into
Parsely:masterfrom
randyriback:rr-track-conversion

Conversation

@randyriback
Copy link
Copy Markdown
Member

@randyriback randyriback commented May 14, 2026

Summary

  • Adds Parsely.trackConversion(url:conversionType:conversionLabel:...) for firing conversion events (newsletter signups, subscriptions, purchases, link clicks, lead capture, custom). Internally constructs an event with action="conversion" and merges _conversion_type and _conversion_label into extra_data, which the Parse.ly conversions backend (conversions-realtime) extracts and uses as the goal identity.
  • New ConversionType enum mirrors the categories accepted server-side (newsletter_signup, lead_capture, link_click, subscription, purchase, custom).
  • Reuses the existing event queue, flush timer, and mobileproxy POST — zero changes to the network layer, Event, RequestBuilder, or HttpClient.
  • Demo app gains two sandbox buttons used during end-to-end verification.

Test plan

Unit tests (87 passing, 3 new)

  • TrackTests.testConversion — verifies Track.conversion(...) enqueues an event with action == "conversion", that _conversion_type and _conversion_label are merged into extra_data, and that caller-supplied extra_data keys are preserved.
  • TrackTests.testConversionReservedKeysOverwriteCallerExtraData — verifies the reserved keys _conversion_type and _conversion_label cannot be overridden by caller-supplied extraData.
  • ParselyTrackerTests.testTrackConversion — verifies the public Parsely.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

  • Built and ran the Demo app in the iOS Simulator (iPhone 16 / iOS 18.6 and iPhone 17 Pro / iOS 26.4).
  • Tapped Track Pageview (sandbox) and Track Conversion (sandbox) against the sandbox.joshhanson.io apikey.
  • Verified the on-wire dict via os_log event dump — confirmed action: "conversion", data._conversion_type: "subscription", data._conversion_label: "...", caller-supplied keys (plan, source) preserved, parsely_site_uuid attached.
  • Confirmed events leave the device — Sending request to https://p1.parsely.com/mobileproxy logged after the 30s flush (also forced via Home/background lifecycle).
  • Confirmed conversions surface in the conversions report on dash.parsely.com for the sandbox.joshhanson.io apikey across multiple test runs and labels.

Testing screenshots

Xnip2026-05-14_20-27-05 Screenshot 2026-05-14 at 8 52 02 PM

randyriback and others added 2 commits May 14, 2026 18:49
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>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 public ConversionType enum for supported conversion categories.
  • Implements conversion event creation in Track.conversion(...) by emitting action = "conversion" and merging reserved conversion keys into extra_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.

Comment on lines +149 to +163
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(
Copy link
Copy Markdown
Contributor

@mokagio mokagio May 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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?

Comment thread Sources/ParselyTracker.swift
Copy link
Copy Markdown
Contributor

@mokagio mokagio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

Image

randyriback and others added 2 commits May 15, 2026 14:21
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants