Skip to content

Conversation

@facumenzella
Copy link
Contributor

@facumenzella facumenzella commented Aug 18, 2025

Motivation

A couple of weeks ago we realized we broke the public API. For better API stability and change control, the job should fail on any API modifications - whether breaking or non-breaking - to ensure all changes are intentional and properly reviewed.

With changes

Screenshot 2025-08-22 at 16 43 16

Without changes

Screenshot 2025-08-22 at 16 59 14

Description of Changes

Use https://www.adyen.com/knowledge-hub/swift-api-diff to detect API changes (only RevenueCat for now, RevenueCatUI coming next):

  • Add baseline swift interface files
  • Re-generate those on each PR. Compare them to the baselines
  • Added Adyen to RC orb (3.3.0)

When detecting changes, there's a lane to run locally that re-generates the swift interface files to replace the baselines. This makes reviewing public changes easier for everybody

On my mind

  1. I think that we could only store a hash for each file instead of the whole file; but that's an optimization that we can easily add later.
  2. Once we move this to other repos, we might consider moving some lanes to the shared plugin to reuse them
@emerge-tools
Copy link

emerge-tools bot commented Aug 18, 2025

📸 Snapshot Test

68 modified, 625 unchanged

Name Added Removed Modified Renamed Unchanged Errored Approval
RevenueCat
com.revenuecat.PaywallsTester.mac-catalyst-scaled-to-match-ipad
0 0 68 0 163 0 ⏳ Needs approval
RevenueCat
com.revenuecat.PaywallsTester.mac-catalyst-optimized-for-mac
0 0 0 0 231 0 N/A
RevenueCat
com.revenuecat.PaywallsTester
0 0 0 0 231 0 N/A

🛸 Powered by Emerge Tools

name: Update paywall templates
command: bundle exec fastlane ios update_paywall_preview_resources_commit

check-api-changes:
Copy link
Member

Choose a reason for hiding this comment

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

Would be great if we could add these to our CircleCI orb so we can use them in other repos (e.g. PHC) too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am thinking about moving the job to the orb, and the script to our fastlane plugin as an action so we can reuse everything

Copy link
Member

Choose a reason for hiding this comment

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

Even better, yes let's do that 😄

@RevenueCat RevenueCat deleted a comment from RCGitBot Aug 19, 2025
@RevenueCat RevenueCat deleted a comment from RCGitBot Aug 19, 2025
@RevenueCat RevenueCat deleted a comment from RCGitBot Aug 19, 2025
@RevenueCat RevenueCat deleted a comment from RCGitBot Aug 19, 2025
@RevenueCat RevenueCat deleted a comment from RCGitBot Aug 19, 2025
@RevenueCat RevenueCat deleted a comment from RCGitBot Aug 21, 2025
@RCGitBot
Copy link
Contributor

RCGitBot commented Aug 21, 2025

👁️‍🗨️ API DIFF

iOS Simulator

Status: ❌ Changes detected

⚠ We should not end up here - investigate how this happened

👀 2 public changes detected

❇️2 Additions

PaywallComponent

❇️ Added

public enum AnimationType: RevenueCat.PaywallComponentBase, Swift.String {
  case easeIn
  case easeInOut
  case easeOut
  case linear
  public init(from decoder: any Swift.Decoder) throws
  public init?(rawValue: Swift.String)
  public typealias RawValue = Swift.String
  public var rawValue: Swift.String { get }
}
public struct Animation: RevenueCat.PaywallComponentBase {
  public func encode(to encoder: any Swift.Encoder) throws -> Swift.Void
  public func hash(into hasher: inout Swift.Hasher) -> Swift.Void
  public init(from decoder: any Swift.Decoder) throws
  public let msDelay: Swift.Int { get }
  public let msDuration: Swift.Int { get }
  public let type: RevenueCat.PaywallComponent.AnimationType { get }
  public static func ==(
    a: RevenueCat.PaywallComponent.Animation,
    b: RevenueCat.PaywallComponent.Animation
  ) -> Swift.Bool
  public var hashValue: Swift.Int { get }
}

iOS

Status: ❌ Changes detected

⚠ We should not end up here - investigate how this happened

👀 2 public changes detected

❇️2 Additions

PaywallComponent

❇️ Added

public enum AnimationType: RevenueCat.PaywallComponentBase, Swift.String {
  case easeIn
  case easeInOut
  case easeOut
  case linear
  public init(from decoder: any Swift.Decoder) throws
  public init?(rawValue: Swift.String)
  public typealias RawValue = Swift.String
  public var rawValue: Swift.String { get }
}
public struct Animation: RevenueCat.PaywallComponentBase {
  public func encode(to encoder: any Swift.Encoder) throws -> Swift.Void
  public func hash(into hasher: inout Swift.Hasher) -> Swift.Void
  public init(from decoder: any Swift.Decoder) throws
  public let msDelay: Swift.Int { get }
  public let msDuration: Swift.Int { get }
  public let type: RevenueCat.PaywallComponent.AnimationType { get }
  public static func ==(
    a: RevenueCat.PaywallComponent.Animation,
    b: RevenueCat.PaywallComponent.Animation
  ) -> Swift.Bool
  public var hashValue: Swift.Int { get }
}

macOS

Status: ❌ Changes detected

⚠ We should not end up here - investigate how this happened

👀 2 public changes detected

❇️2 Additions

PaywallComponent

❇️ Added

public enum AnimationType: RevenueCat.PaywallComponentBase, Swift.String {
  case easeIn
  case easeInOut
  case easeOut
  case linear
  public init(from decoder: any Swift.Decoder) throws
  public init?(rawValue: Swift.String)
  public typealias RawValue = Swift.String
  public var rawValue: Swift.String { get }
}
public struct Animation: RevenueCat.PaywallComponentBase {
  public func encode(to encoder: any Swift.Encoder) throws -> Swift.Void
  public func hash(into hasher: inout Swift.Hasher) -> Swift.Void
  public init(from decoder: any Swift.Decoder) throws
  public let msDelay: Swift.Int { get }
  public let msDuration: Swift.Int { get }
  public let type: RevenueCat.PaywallComponent.AnimationType { get }
  public static func ==(
    a: RevenueCat.PaywallComponent.Animation,
    b: RevenueCat.PaywallComponent.Animation
  ) -> Swift.Bool
  public var hashValue: Swift.Int { get }
}

watchOS Simulator

Status: ❌ Changes detected

⚠ We should not end up here - investigate how this happened

👀 2 public changes detected

❇️2 Additions

PaywallComponent

❇️ Added

public enum AnimationType: RevenueCat.PaywallComponentBase, Swift.String {
  case easeIn
  case easeInOut
  case easeOut
  case linear
  public init(from decoder: any Swift.Decoder) throws
  public init?(rawValue: Swift.String)
  public typealias RawValue = Swift.String
  public var rawValue: Swift.String { get }
}
public struct Animation: RevenueCat.PaywallComponentBase {
  public func encode(to encoder: any Swift.Encoder) throws -> Swift.Void
  public func hash(into hasher: inout Swift.Hasher) -> Swift.Void
  public init(from decoder: any Swift.Decoder) throws
  public let msDelay: Swift.Int { get }
  public let msDuration: Swift.Int { get }
  public let type: RevenueCat.PaywallComponent.AnimationType { get }
  public static func ==(
    a: RevenueCat.PaywallComponent.Animation,
    b: RevenueCat.PaywallComponent.Animation
  ) -> Swift.Bool
  public var hashValue: Swift.Int { get }
}

watchOS

Status: ❌ Changes detected

⚠ We should not end up here - investigate how this happened

👀 2 public changes detected

❇️2 Additions

PaywallComponent

❇️ Added

public enum AnimationType: RevenueCat.PaywallComponentBase, Swift.String {
  case easeIn
  case easeInOut
  case easeOut
  case linear
  public init(from decoder: any Swift.Decoder) throws
  public init?(rawValue: Swift.String)
  public typealias RawValue = Swift.String
  public var rawValue: Swift.String { get }
}
public struct Animation: RevenueCat.PaywallComponentBase {
  public func encode(to encoder: any Swift.Encoder) throws -> Swift.Void
  public func hash(into hasher: inout Swift.Hasher) -> Swift.Void
  public init(from decoder: any Swift.Decoder) throws
  public let msDelay: Swift.Int { get }
  public let msDuration: Swift.Int { get }
  public let type: RevenueCat.PaywallComponent.AnimationType { get }
  public static func ==(
    a: RevenueCat.PaywallComponent.Animation,
    b: RevenueCat.PaywallComponent.Animation
  ) -> Swift.Bool
  public var hashValue: Swift.Int { get }
}

@RevenueCat RevenueCat deleted a comment from RCGitBot Aug 21, 2025
@facumenzella facumenzella added the danger-bypass-size-limit Apply this label to bypass Dangerbot's size limit. label Aug 22, 2025
@RevenueCat-Danger-Bot
Copy link

2 Warnings
⚠️ Size check is being bypassed due to the presence of the label "danger-bypass-size-limit"
⚠️ Size increase: 1181.45 KB

Generated by 🚫 Danger

@facumenzella facumenzella marked this pull request as ready for review August 22, 2025 14:59
@facumenzella facumenzella requested a review from a team August 22, 2025 15:13
@facumenzella facumenzella changed the title Test swiftinterface for RevenueCat Aug 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

danger-bypass-size-limit Apply this label to bypass Dangerbot's size limit. pr:other

5 participants