Skip to content

fix(ios): declare PLYUserAttributeDelegate conformance on PurchaselyRN - #297

Merged
kherembourg merged 2 commits into
mainfrom
fix/ios-user-attribute-delegate-conformance
Sep 7, 2026
Merged

kherembourg merged 2 commits into
mainfrom
fix/ios-user-attribute-delegate-conformance

Conversation

@kherembourg

@kherembourg kherembourg commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

The warning

packages/purchasely/ios/PurchaselyRN.m line 581 gives the native SDK the
bridge module as the user attribute delegate:

[Purchasely setUserAttributeDelegate: self];

packages/purchasely/ios/PurchaselyRN.h line 12 does not declare
PLYUserAttributeDelegate on the class. The compiler reports:

packages/purchasely/ios/PurchaselyRN.m:581:43: warning: sending 'PurchaselyRN *const __strong' to parameter of incompatible type 'id<PLYUserAttributeDelegate> _Nonnull'
  581 |     [Purchasely setUserAttributeDelegate: self];
      |                                           ^~~~
note: passing argument to parameter 'userAttributeDelegate' here
 1419 | + (void)setUserAttributeDelegate:(id <PLYUserAttributeDelegate> _Nonnull)userAttributeDelegate;

Why it matters

The warning is a type safety hole, not only untidy output.

The compiler cannot verify the delegate contract on a class that does not
declare the protocol. PurchaselyRN implements two delegate methods:
onUserAttributeSetWithKey:type:value:source:processingLegalBasis: and
onUserAttributeRemovedWithKey:source:. Both bridge to the
USER_ATTRIBUTE_SET_LISTENER and USER_ATTRIBUTE_REMOVED_LISTENER events in
JavaScript.

Without the declaration the compiler gives no check at all on this contract.
The call site accepts any object, and a change in the protocol stays
invisible until a runtime test exercises the callback. The JavaScript
listener then does not receive events any more, and nothing in the build
reports it.

The declaration restores three compile-time checks:

  1. The call site type-checks. -Wprotocol and the argument type check both
    apply, so the warning disappears for the right reason.
  2. A signature drift on a selector that the bridge implements becomes a
    warning. The compiler compares each implemented method against the
    protocol declaration.
  3. A required method that a later SDK version adds to the protocol becomes a
    -Wprotocol warning on this class.

A pure rename in the native SDK stays silent, because all current methods
are optional. The declaration still makes the class role explicit, and it
gives the compiler the contract to check the two points above.

Why the fix is safe

All three methods of PLYUserAttributeDelegate are @objc optional. The
generated Objective-C header in the pinned pod (Purchasely 6.0.0,
Purchasely.framework/Headers/Purchasely-Swift.h line 1345) shows:

SWIFT_PROTOCOL("_TtP10Purchasely24PLYUserAttributeDelegate_")
@protocol PLYUserAttributeDelegate
@optional
- (void)onUserAttributeSetWithKey:(NSString * _Nonnull)key type:(enum PLYUserAttributeType)type value:(id _Nullable)value source:(enum PLYUserAttributeSource)source;
- (void)onUserAttributeSetWithKey:(NSString * _Nonnull)key type:(enum PLYUserAttributeType)type value:(id _Nullable)value source:(enum PLYUserAttributeSource)source processingLegalBasis:(enum PLYDataProcessingLegalBasis)processingLegalBasis;
- (void)onUserAttributeRemovedWithKey:(NSString * _Nonnull)key source:(enum PLYUserAttributeSource)source;
@end

The declaration adds no required method. The two selectors that the bridge
implements match the protocol exactly. The behaviour does not change.

Build evidence

Command, on the CocoaPods workspace:

xcodebuild -workspace example/ios/example.xcworkspace -scheme react-native-purchasely \
  -configuration Debug -destination 'generic/platform=iOS' \
  -derivedDataPath example/ios/DerivedData CODE_SIGNING_ALLOWED=NO build

Before the fix:

PurchaselyRN.m:581:43: warning: sending 'PurchaselyRN *const __strong' to parameter of incompatible type 'id<PLYUserAttributeDelegate> _Nonnull'
** BUILD SUCCEEDED **

After the fix:

(no PLYUserAttributeDelegate warning)
** BUILD SUCCEEDED **

The XCTest bundle react-native-purchasely-Unit-Tests also passes on a
booted simulator.

Scope

The change is one line in one header file. The pull request touches no other
file. It changes no behaviour on iOS, and it touches nothing on Android.

The header has never declared this protocol. The delegate call site arrived
in 5.0.4 (#173). The warning is confirmed against the pinned pod of 6.0.0,
so it ships in 6.0.0. Earlier releases are not checked against their own pod
pin. The warning is not related to the 6.1.0 work in #293.

Out of scope

The same build prints unrelated -Wdeprecated-declarations warnings, for
example on setThemeMode:. This pull request leaves them unchanged.

PurchaselyRN.m line 581 calls `[Purchasely setUserAttributeDelegate: self]`,
but the header did not declare the protocol. The compiler reported:

  warning: sending 'PurchaselyRN *const __strong' to parameter of
  incompatible type 'id<PLYUserAttributeDelegate> _Nonnull'

The class implements two of the three delegate methods and bridges them to
the USER_ATTRIBUTE_SET_LISTENER and USER_ATTRIBUTE_REMOVED_LISTENER events.
Without the declaration the compiler checks nothing on this contract.

All three protocol methods are `@objc optional` in the pinned pod
(Purchasely 6.0.0), so the declaration adds no required method and changes
no behaviour.

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

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR declares PurchaselyRN as conforming to PLYUserAttributeDelegate, aligning its type declaration with its existing delegate registration and callback implementations.

  • Restores compile-time protocol checking for user-attribute callbacks.
  • Removes the incompatible delegate argument warning without changing runtime behavior.

Confidence Score: 5/5

The PR appears safe to merge because the declaration matches the pinned SDK protocol and existing delegate implementation.

The focused type-conformance change introduces no behavioral, security, compatibility, or repository-rule issues.

Important Files Changed

Filename Overview
packages/purchasely/ios/PurchaselyRN.h Adds the missing PLYUserAttributeDelegate conformance to the native iOS bridge interface.

Reviews (1): Last reviewed commit: "fix(ios): declare PLYUserAttributeDelega..." | Re-trigger Greptile

The e2e-ios and e2e-android groups were global, so a run on one branch
cancelled the run on every other branch. PR #297 went red for that reason,
not for a test failure. Mirror the ci.yml pattern: key the group by ref and
cancel superseded pull_request runs only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@kherembourg
kherembourg merged commit 0609053 into main Sep 7, 2026
10 checks passed
@kherembourg
kherembourg deleted the fix/ios-user-attribute-delegate-conformance branch September 7, 2026 09:04
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