Skip to content

refactor: move webview product and identity parsing to Swift - #931

Merged
thomson-t merged 2 commits into
workstation/swift-migrationfrom
refactor/webview-product-identity-parsing
Sep 4, 2026
Merged

thomson-t merged 2 commits into
workstation/swift-migrationfrom
refactor/webview-product-identity-parsing

Conversation

@nickolas-dimitrakas

@nickolas-dimitrakas nickolas-dimitrakas commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Background

Second slice of MPConvertJS: product and identity JSON parsing.

What Has Changed

  • Added productFieldsFromJSON: and identityRequestFromJSON: to MPConvertJSFields.swift. 262 → 210 SLOC.
  • Unlike promotion fields (refactor: move webview JSON field reading to Swift #929), product fields keep per-field if guards in the .mMPProduct has nonnull string/number properties and a scalar position, so "absent" and "nil" aren't interchangeable here.
  • Preserved: string prices coerce via -doubleValue (non-numeric strings → 0); position reads via -unsignedIntValue; product attributes require both key and value to be strings; a missing/non-array UserIdentities vs. a single malformed identity entry log and fail differently (the Swift result reports which); last-write-wins on duplicate identity types.
  • Hardened: a non-dictionary entry in UserIdentities previously crashed (-[NSString objectForKeyedSubscript:]); now reports a malformed entry instead.

Validation

  • abi-guard PASSED
  • trunk check clean
  • ObjC 1004 / Swift 650 tests green; new Swift coverage 21/21
  • tvOS: CI only

Stack

Depends on #929.

Checklist

  • Self-review completed
  • Tests added or updated
  • Tested locally

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

🐦 Swift Migration Progress

Production implementation code at f5694a2110ad compared with 60c55d484d14.

Area Goal Progress Base This PR Swift SLOC Objective-C remaining Change
Core SDK Short term — in scope █████▍░░░░ 52.75% 53.29% 8,293 7,268 🚀 +0.54 pp
Core SDK Long term — all Objective-C ███▉░░░░░░ 38.38% 38.82% 8,293 13,071 🚀 +0.44 pp
SDK kit infrastructure Short term — in scope ██████▊░░░ 66.96% 66.96% 2,021 997 ➖ 0.00 pp
SDK kit infrastructure Long term — all Objective-C ████░░░░░░ 39.95% 39.95% 2,021 3,038 ➖ 0.00 pp
Standalone kits Short term — in scope ▏░░░░░░░░░ 1.06% 1.06% 152 14,218 ➖ 0.00 pp
Standalone kits Long term — all Objective-C ▏░░░░░░░░░ 1.06% 1.06% 152 14,218 ➖ 0.00 pp

Objective-C retained by design: Core SDK 5,803 · SDK kit infrastructure 2,041 · Standalone kits 0.

This PR's code movement

Area Swift lines added Objective-C lines removed
Core SDK 148 96
SDK kit infrastructure 0 0
Standalone kits 0 0
How this is measured
  • Current composition uses production source lines of code (SLOC) from cloc; comments and blank lines are excluded.
  • Short term — in scope excludes the Objective-C the migration will not delete, so 100% is the end of this project: every in-scope implementation gone.
  • Long term — all Objective-C keeps the full denominator. Reaching 100% there means the public API itself becomes Swift, which is a breaking change reserved for a future major release.
  • The gap between the two rows is the retained public/kit contract, runtime-identity, and boundary-glue surface listed in Tools/swift-migration-retained-objc.txt.
  • Retained wrappers keep their Objective-C interface but still shed logic to Swift. That thinning moves the long-term row and the retained figure, not the short-term row.
  • Both revisions are measured with the manifest from the head revision, so a manifest edit does not by itself move the reported change. A retained file this pull request renamed or deleted still counts as retained at the base.
  • Pull request movement uses physical additions/deletions from git diff base...head --numstat; it counts retained files too and is intentionally separate from SLOC totals.
  • Core excludes SDK kit infrastructure and vendored libraries. Standalone kits include only files below Kits/**/Sources.
  • Tests, examples, headers, build outputs, vendored libraries, and the MParticle/Sources Swift overlay are excluded.
  • Objective-C++ (.mm) is included in the Objective-C figures and removed counts.

Generated with cloc 2.10. This report is informational and does not gate migration direction.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown

📦 SDK Size Impact Report

Measures how much the SDK adds to an app's size (with-SDK minus without-SDK).

Metric Target Branch This PR Change
App Bundle Impact 2.48 MB 2.50 MB +20 KB
Executable Impact 848 bytes 848 bytes +N/A
XCFramework Size 6.82 MB 6.84 MB +20 KB

➡️ SDK size impact change is minimal.

Raw measurements

Target branch (workstation/swift-migration):

{"baseline_app_size_kb":84,"baseline_executable_size_bytes":75464,"with_sdk_app_size_kb":2624,"with_sdk_executable_size_bytes":76312,"sdk_impact_kb":2540,"sdk_executable_impact_bytes":848,"xcframework_size_kb":6980}

This PR:

{"baseline_app_size_kb":84,"baseline_executable_size_bytes":75464,"with_sdk_app_size_kb":2644,"with_sdk_executable_size_bytes":76312,"sdk_impact_kb":2560,"sdk_executable_impact_bytes":848,"xcframework_size_kb":7000}

@nickolas-dimitrakas
nickolas-dimitrakas marked this pull request as ready for review September 3, 2026 18:42
@nickolas-dimitrakas
nickolas-dimitrakas requested a review from a team as a code owner September 3, 2026 18:42
@cursor

cursor Bot commented Sep 3, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes webview-driven product and identity parsing on a compatibility-sensitive path; behavior is largely preserved and tested, with one intentional hardening for malformed identity payloads.

Overview
Moves webview bridge parsing for products and identity API requests out of MPConvertJS.m into MPConvertJSFields, shrinking the Objective-C converter while keeping MPProduct / MPIdentityApiRequest construction in .m.

Swift adds productFieldsFromJSON: (optional fields, string price coercion via doubleValue, string-only attribute pairs) and identityRequestFromJSON: with typed outcomes (missingUserIdentities vs malformedEntry) so ObjC can still log only on a missing/non-array UserIdentities and return nil silently for a bad array entry. Identity pairs are applied in order with last-write-wins when the top-level Identity/Type duplicates a type from the array.

+product: and +identityApiRequest: now call those helpers; product assignment stays guard-based so absent JSON does not overwrite nonnull properties or assign position when the value is not an NSNumber.

Behavior fix: a non-dictionary element in UserIdentities no longer crashes; it is treated as a malformed entry. New unit tests cover product/identity edge cases.

Reviewed by Cursor Bugbot for commit f5694a2. Bugbot is set up for automated code reviews on this repo. Configure here.

@nickolas-dimitrakas
nickolas-dimitrakas force-pushed the refactor/webview-product-identity-parsing branch from 508847b to 5e9863a Compare September 3, 2026 19:28
@nickolas-dimitrakas
nickolas-dimitrakas force-pushed the refactor/webview-product-identity-parsing branch 3 times, most recently from 8449bf6 to 471b6ed Compare September 3, 2026 21:03
thomson-t
thomson-t previously approved these changes Sep 3, 2026
Base automatically changed from refactor/delete-migrated-objc-wrappers to workstation/swift-migration September 4, 2026 14:48
@BrandonStalnaker
BrandonStalnaker force-pushed the refactor/webview-product-identity-parsing branch from 471b6ed to f127c9c Compare September 4, 2026 14:48
nickolas-dimitrakas and others added 2 commits September 4, 2026 12:19
Second slice of MPConvertJS, after the promotion and transaction-attribute
readers. MPConvertJS.m goes from 262 to 210 sloc; 307 before the pair.

+productFieldsFromJSON: replaces ten guarded reads, and +identityRequestFromJSON:
replaces the UserIdentities walk. Constructing MPProduct and
MPIdentityApiRequest stays in the .m, as does every MPILogError.

Unlike the promotion reader, product assignments stay guarded in the .m. name,
sku and quantity are declared nonnull on MPProduct and position is a scalar, so
"absent" and "nil" are not interchangeable here the way they were for
MPPromotion, whose setters remove their key on nil. The DTO therefore exposes
every field as optional, including position as an NSNumber the .m only reads
when non-nil.

Behaviours preserved deliberately, each with a test:

- Price has two branches. An NSNumber passes through; a string goes through
  -doubleValue, which yields 0 for text that is not a number, so "abc" still
  produces 0 rather than being dropped.
- Position was read with -unsignedIntValue, not -unsignedIntegerValue.
- Product attributes required both the key and the value to be strings.
- The identity payload has two distinct failure modes that are not
  interchangeable: a missing or non-array UserIdentities logged before
  returning nil, while a single malformed entry returned nil silently and
  discarded the identities already applied. The Swift result reports which, so
  the .m keeps the log at its original site.
- The top-level Identity/Type pair is applied after the array, so a duplicated
  type keeps last-write-wins. The pairs are returned in application order.
- requestWithEmptyUser is still called before validation, in case it ever does
  more than allocate.

One hardening: an entry that is not a dictionary crashed the ObjC, which typed
the enumeration block NSDictionary * and subscripted it unguarded. It now
reports a malformed entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xcodebuild analyze flagged price/position/quantity's presence checks as
pointer-to-bool conversions. The existing comment already documents the
intent as a presence check, not a truthiness check (0 is a valid price,
position, or quantity) — implicit bool happened to read the same as
!= nil for typical values but was the wrong check to write.
@thomson-t
thomson-t force-pushed the refactor/webview-product-identity-parsing branch from f127c9c to f5694a2 Compare September 4, 2026 16:19
@thomson-t
thomson-t merged commit 7722ee3 into workstation/swift-migration Sep 4, 2026
79 checks passed
@thomson-t
thomson-t deleted the refactor/webview-product-identity-parsing branch September 4, 2026 16:20
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