Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Changelog
All notable changes to this package will be documented in this file.

## [Unreleased]

### Fixes
* `SuperwallEventInfo.EventType` was never populated: iOS serialized `String(describing: event)`, which on SuperwallKit versions without a `CustomStringConvertible` conformance dumps the case together with its associated values (`transactionComplete(transaction: ..., product: ...)`), and Android sends the snake_case `rawName` (`transaction_complete`). Neither matched the `EventType` enum, so every delegate event arrived as the default `FirstSeen` and any `switch` on it was dead. iOS now emits only the case name, and the C# parser accepts both camelCase and snake_case, logging a warning instead of failing silently when a name is still unknown. `EventType` gains the members both native SDKs already emit but C# lacked (`PaywallPageView`, `TestModeModalOpen`/`Close`, `CelExpressionResult`, `ErrorThrown`, `ReviewGranted`/`Denied`, `AttributionMatch`, `StripeCheckoutStart`/`Submit`/`Complete`/`Fail`), and the two placement names whose member is spelled differently (`deepLink_open` → `DeepLink`, `paywallWebviewLoad_processTerminated` → `PaywallWebviewProcessTerminated`) are mapped explicitly.

## [0.2.6]

### Dependencies
Expand Down
14 changes: 12 additions & 2 deletions Plugins/iOS/SuperwallUnityBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,18 @@ private func serializeEventInfo(_ info: SuperwallEventInfo) -> [String: Any] {
var dict: [String: Any] = [
"params": info.params
]
// Use the string representation of the event
dict["eventType"] = String(describing: info.event)
// `String(describing:)` is not a stable name for this enum. Recent SuperwallKit releases make
// `SuperwallEvent` CustomStringConvertible and describe it as its snake_case placement name
// ("transaction_complete"), but older 4.x releases fall back to Swift's default description,
// which for a case with associated values is the whole payload:
//
// transactionComplete(transaction: Optional(SuperwallKit.StoreTransaction), product: ..., ...)
//
// That never matched the C# `EventType` enum, so every delegate event arrived typed as the
// default (`FirstSeen`). Keep only the leading identifier: both shapes then parse on the C# side,
// which also normalises Android's snake_case `rawName`.
let described = String(describing: info.event)
dict["eventType"] = String(described.prefix(while: { $0 != "(" }))
return dict
}

Expand Down
28 changes: 26 additions & 2 deletions Runtime/Internal/BridgeCallbackHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,29 @@ private static long GetLong(Dictionary<string, object> data, string key, long de
return null;
}

/// <summary>
/// The two native bridges do not agree on the shape of an event name: iOS sends the Swift case
/// name (camelCase) or, on newer SuperwallKit, the snake_case placement name; Android sends
/// <c>SuperwallEvent.rawName</c>, which is snake_case (<c>transaction_complete</c>,
/// <c>freeTrial_start</c>). Stripping underscores makes all of them match the PascalCase
/// <see cref="EventType"/> members under a case-insensitive parse. Two placement names differ from
/// the member that was chosen for them and are mapped explicitly.
/// </summary>
internal static EventType? ParseEventType(string value)
{
if (string.IsNullOrEmpty(value)) return null;

var parsed = ParseEnum<EventType>(value) ?? ParseEnum<EventType>(value.Replace("_", string.Empty));
if (parsed.HasValue) return parsed;

switch (value.Replace("_", string.Empty).ToLowerInvariant())
{
case "deeplinkopen": return EventType.DeepLink;
case "paywallwebviewloadprocessterminated": return EventType.PaywallWebviewProcessTerminated;
default: return null;
}
}

private static SubscriptionStatus DeserializeSubscriptionStatus(Dictionary<string, object> data)
{
if (data == null) return SubscriptionStatus.CreateUnknown();
Expand Down Expand Up @@ -540,10 +563,11 @@ private static SuperwallEventInfo DeserializeSuperwallEventInfo(Dictionary<strin
var eventTypeStr = GetString(data, "eventType");
if (eventTypeStr != null)
{
// Try to parse the event type string to the enum
var parsed = ParseEnum<EventType>(eventTypeStr);
var parsed = ParseEventType(eventTypeStr);
if (parsed.HasValue)
info.EventType = parsed.Value;
else
Debug.LogWarning($"[Superwall] Unrecognised eventType '{eventTypeStr}'; leaving EventType at its default. Handlers switching on it will not run for this event.");
}

var paramsDict = GetDict(data, "params");
Expand Down
17 changes: 16 additions & 1 deletion Runtime/Models/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,22 @@ public enum EventType
ReviewRequested,
PermissionRequested,
PermissionGranted,
PermissionDenied
PermissionDenied,
// Names both native SDKs emit (SuperwallKit 4.16 / superwall-android 2.7) that had no member here,
// so they were reported with a warning and left at the default. Appended, never reordered: the
// enum is serialized by name, not by value, but consumers may still persist the ordinal.
PaywallPageView,
TestModeModalOpen,
TestModeModalClose,
CelExpressionResult,
ErrorThrown,
ReviewGranted,
ReviewDenied,
AttributionMatch,
StripeCheckoutStart,
StripeCheckoutSubmit,
StripeCheckoutComplete,
StripeCheckoutFail
}

[Serializable]
Expand Down
Loading