Skip to content

release: 2.2.4 — re-introduce SKAN on top of v2.2.1 baseline - #102

Closed
JakubMrozek wants to merge 16 commits into
v2.2.4-basefrom
v2.2.4
Closed

release: 2.2.4 — re-introduce SKAN on top of v2.2.1 baseline#102
JakubMrozek wants to merge 16 commits into
v2.2.4-basefrom
v2.2.4

Conversation

@JakubMrozek

@JakubMrozek JakubMrozek commented Jul 8, 2026

Copy link
Copy Markdown
Member

Why

Production Chai runs v2.2.1 (known-good). SKAN never activated on production, and the version that turns it on (2.2.2 → 2.2.3-rc) is blocked behind an Android WebView init-iframe issue Chai reported. Rather than ship the accumulated 2.2.2/2.2.3 changes, we restart the release from the known-good v2.2.1 tag and re-introduce the minimal SKAN code, reviewed line by line, to minimize regression surface on Chai's fragile WebView setup.

Base

This PR is intentionally based on v2.2.4-base — a branch pinned at exactly v2.2.1 — so the diff shows only what we add on top of production, not the 2.2.2/2.2.3 history. (A PR into main would misleadingly show 2.2.2's changes as reverted.)

Scope of this PR (so far)

  • Branch v2.2.4 created from v2.2.1
  • Changelog placeholder ## 2.2.4 (content to follow)
  • Re-introduce SKAN code incrementally
  • Bump pubspec.yaml version to 2.2.4 (must be ≥ 2.2.2 to cross the server skanSupported gate)
  • Test: emulator matching Vineet's config + demo app with Chai's WebView libraries
  • Team review

Plan reference

Notion log: SDK Flutter v2.2.4.

🤖 Generated with Claude Code

Begin the 2.2.4 line from the known-good production tag v2.2.1 and add a
placeholder changelog heading. SKAN code will be re-introduced on top of
this, reviewed incrementally. Content to follow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
JakubMrozek and others added 7 commits July 8, 2026 12:36
Cross the server skanSupported gate (Flutter >= 2.2.2) so the ad server
sends signed skan payloads. Only the version line changes; the Flutter
constraint (>=3.24.0) and flutter_lints (^5.0.0) are intentionally kept
at their v2.2.1 values.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kSdkVersion is sent as the reported SDK version in the preload request
(api.dart, use_preload_ads.dart). The ad server gates skanSupported on
sdk-flutter >= 2.2.2, so this must be >= 2.2.2 for signed skan payloads
to be returned. Set to 2.2.4 to match pubspec.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The SKAN native code (SKAdNetworkManager, SKStoreProductManager,
SKOverlayManager) imports StoreKit. Declare it explicitly in s.frameworks
so it links reliably now that SKAN is actually activated, rather than
relying on Swift implicit auto-linking. Deployment target intentionally
kept at iOS 12.0 (SKAN APIs are runtime-gated with @available).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port the 2.2.2 privacy manifest: NSPrivacyTracking=false, clear tracking
domains, DeviceID declared as AppFunctionality/non-tracking (matches
sdk-swift), and add the UserDefaults required-reason API (CA92.1).

SystemBootTime (8FFB.1) is intentionally retained: the Flutter native
AppInfoPlugin reads kp_proc.p_starttime, which sdk-swift does not — so
the manifests correctly differ on that one entry. Manifest reflects the
Flutter binary's actual API usage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port the tested 2.2.2 Skan/Bid model onto the 2.2.1 base: adds
Skan.toJson() (consumed by the SKOverlay/SKStoreProduct services),
ImpressionTrigger, and fidelities in Skan equality/hashCode. Additive
and non-breaking (superset of the 2.2.1 public API).

Extend bid_test.dart with 20 new tests covering the additions:
impressionTrigger parsing/defaults, Skan.toJson (required-only, null
omission, optionals, fidelities), Skan equality incl. fidelities, and
AttributionFidelity parse/equality. Full file: 50 tests, all passing.

Note: bid.dart:221 still reads json['akk'] (should be 'aak') — an
AAK-only bug, irrelevant to SKAN; left as-is for the SKAN-focused 2.2.4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bring the tested 2.2.2 SKAN wiring onto the 2.2.1 base as a single
compile-coupled + method-channel-coupled change:

- ad_format.dart: click -> fidelity-1 -> SKStoreProduct (else browser),
  impressionTrigger timing (immediate on ad-done / component on modal),
  pass full skan to the services, remove the message-driven
  skstoreproduct path. Excludes the non-SKAN '; null' evaluateJavascript
  tweak (kept at v2.2.1, consistent with kontext_webview.dart).
- types.dart: drop the skstoreproduct OpenIframeComponent value.
- interstitial_modal.dart: drop skstoreproduct message handling.
- sk_overlay_service / sk_store_product_service: send skan.toJson().
- Native plugins/managers read the skan payload; field mapping matches
  the production-proven KontextKit (SKOverlay + SKStoreProduct verified).
- SKAdNetworkManager: aligned with KontextKit — resolve
  nonce/timestamp/signature upfront (top-level else fidelity-0), validate
  all six required fields, guard-let instead of force-unwraps, drop
  parseFidelities, fail cleanly with MISSING_ARGUMENTS. Flutter SKAN was
  never live in production, so KontextKit is the source of truth.
- Port coupled 2.2.2 tests (ad_format_test, types_test).

flutter analyze: clean. Full test suite: green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cover the two previously-untested SKAN services via injectable isIOS +
mocked method channels: platform gating (non-iOS -> false, no channel
call), correct channel args (skan.toJson(); overlay nests under 'skan'
with position/dismissible, store-product sends the skan map directly),
return-value handling, the overlay itunesItem.isEmpty guard, and
PlatformException/UNSUPPORTED_IOS handling. 17 tests, all passing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JakubMrozek
JakubMrozek marked this pull request as ready for review July 8, 2026 14:19
JakubMrozek and others added 2 commits July 8, 2026 16:46
…o app

Show publishers the required SKAN setup: our ad-network id
(mp7rpxwdrx.skadnetwork) must be present in the host app's Info.plist
SKAdNetworkItems for Apple to allow SKAN attribution. Example-only;
does not affect the published SDK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire the AdsProvider onEvent callback to print each ad event
(filled/no-fill/render-started/completed/viewed/clicked/error) with its
key fields — a ready reference for publishers on consuming SDK events.
Example-only; does not affect the published SDK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@mbuckalew-kontext mbuckalew-kontext left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Automated review focused on correctness, silent failures, and store-compliance. Verified against the actual source at the PR head (not just the diff). Two items I'd treat as blocking (privacy manifest, silent StoreKit attribution loss); the rest are either quick fixes or need a decision/answer from the backend team. Note: the native refactor here is a net crash-safety improvementSKAdNetworkManager drops its old force-unwraps for a guarded binding — so none of the items below are crashes.

🤖 Generated with Claude Code

Comment thread ios/PrivacyInfo.xcprivacy Outdated
Comment thread ios/Classes/SKStoreProductManager.swift Outdated
Comment thread ios/Classes/SKStoreProductManager.swift Outdated
Comment thread lib/src/widgets/ad_format.dart
Comment thread lib/src/widgets/ad_format.dart
Comment thread ios/Classes/SKOverlayManager.swift
Comment thread example/ios/Runner/Info.plist
JakubMrozek and others added 6 commits July 9, 2026 12:05
Integrators must add mp7rpxwdrx.skadnetwork to their own app's
Info.plist SKAdNetworkItems or SKAN attribution silently never fires
(ads still serve). Addresses PR #102 review feedback; the example app's
plist serves as the working reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A non-numeric timestamp previously became 0 via 'Int(...) ?? 0',
producing an invalid signature and silent attribution loss. Parse it in
fidelity1Values (NSNumber or numeric string, else nil -> SKAN params
skipped, sheet still presents), mirroring SKOverlayManager and
KontextKit. Unreachable from our server (signs String(Date.now()) and
the wire schema enforces strings), but a DSP could theoretically send a
non-numeric string. Addresses PR #102 review feedback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…toreProduct

applySkanParams was void — on any invalid field it silently skipped the
SKAN params, the sheet still presented, and completion(true) suppressed
the Dart browser fallback: the store page opened unattributed with no
error surfaced. It now returns Bool and present() errors with
ATTRIBUTION_FAILED, which the existing Dart path turns into a browser
fallback — no Dart change needed. Mirrors KontextKit (identical error
semantics) and SKOverlayManager. Addresses PR #102 blocking review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…state

Minimal-delta decision: 2.2.1 is the proven production baseline and the
manifest is unrelated to the SKAN activation this release ships. Drops
the 2.2.2 manifest sync (and its changelog line) so the file no longer
appears in the release diff. The Swift-SDK-aligned manifest can be
revisited separately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Make isIOS injectable (same pattern as the other two SKAN services) and
add unit tests for the full impression lifecycle: payload field names,
arming only on successful init, start/end gated on readiness, error
swallowing (never throws), dispose disarming, call ordering, and slow
native responses resolving correctly — the Dart layer is latency-proof
regardless of when Apple's async completion returns.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@JakubMrozek

Copy link
Copy Markdown
Member Author

All commits from this PR are now on main (main was reset to the v2.2.4 head as planned — old main preserved in #103 / archive/main-pre-2.2.4). Released as v2.2.4. Closing since GitHub shows an empty diff instead of a merge.

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.

2 participants