Skip to content

[in_app_purchase_storekit] Replace deprecated offer code API#11223

Open
LouiseHsu wants to merge 17 commits intoflutter:mainfrom
LouiseHsu:offer-codes
Open

[in_app_purchase_storekit] Replace deprecated offer code API#11223
LouiseHsu wants to merge 17 commits intoflutter:mainfrom
LouiseHsu:offer-codes

Conversation

@LouiseHsu
Copy link
Copy Markdown
Contributor

@LouiseHsu LouiseHsu commented Mar 10, 2026

Fixes flutter/flutter#162914

Pre-Review Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools.
  • I read the [Tree Hygiene] page, which explains my responsibilities.
  • I read and followed the [relevant style guides] and ran [the auto-formatter].
  • I signed the [CLA].
  • The title of the PR starts with the name of the package surrounded by square brackets, e.g. [shared_preferences]
  • I [linked to at least one issue that this PR fixes] in the description above.
  • I followed [the version and CHANGELOG instructions], using [semantic versioning] and the [repository CHANGELOG style], or I have commented below to indicate which documented exception this PR falls under[^1].
  • I updated/added any relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or I have commented below to indicate which [test exemption] this PR falls under[^1].
  • All existing and new tests are passing.

@LouiseHsu LouiseHsu added the CICD Run CI/CD label Mar 12, 2026
@flutter-dashboard
Copy link
Copy Markdown

This pull request is not mergeable in its current state, likely because of a merge conflict. Pre-submit CI jobs were not triggered. Pushing a new commit to this branch that resolves the issue will result in pre-submit jobs being scheduled.

@LouiseHsu LouiseHsu added CICD Run CI/CD and removed CICD Run CI/CD labels Mar 12, 2026
@LouiseHsu LouiseHsu added CICD Run CI/CD and removed CICD Run CI/CD labels Mar 12, 2026
@LouiseHsu LouiseHsu added CICD Run CI/CD and removed CICD Run CI/CD labels Mar 12, 2026
@LouiseHsu LouiseHsu added CICD Run CI/CD and removed CICD Run CI/CD labels Mar 13, 2026
@LouiseHsu LouiseHsu changed the title [draft] offer codes [in_app_purchase_storekit] Replace deprecated offer code API Mar 25, 2026
@LouiseHsu LouiseHsu marked this pull request as ready for review March 25, 2026 20:25
gemini-code-assist[bot]

This comment was marked as outdated.

@LouiseHsu LouiseHsu marked this pull request as draft March 25, 2026 20:33
@LouiseHsu LouiseHsu added CICD Run CI/CD and removed CICD Run CI/CD labels Mar 31, 2026
@LouiseHsu LouiseHsu added CICD Run CI/CD and removed CICD Run CI/CD labels Apr 1, 2026
@github-actions github-actions bot removed the CICD Run CI/CD label Apr 6, 2026
@LouiseHsu LouiseHsu added the CICD Run CI/CD label Apr 7, 2026
@github-actions github-actions bot removed the CICD Run CI/CD label Apr 7, 2026
@LouiseHsu LouiseHsu added the CICD Run CI/CD label Apr 7, 2026
@github-actions github-actions bot removed the CICD Run CI/CD label Apr 8, 2026
@LouiseHsu LouiseHsu added the CICD Run CI/CD label Apr 14, 2026
@github-actions github-actions bot removed the CICD Run CI/CD label Apr 14, 2026
@LouiseHsu LouiseHsu marked this pull request as ready for review April 14, 2026 23:41
@LouiseHsu
Copy link
Copy Markdown
Contributor Author

@gemini-code-assist hewwo

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request adds support for offer codes in StoreKit 2 for the in_app_purchase_storekit package. Key changes include the implementation of presentOfferCodeRedeemSheet in the Swift backend, updates to the Dart platform addition to support StoreKit 2, and the addition of a redemption button in the example application. The iOS example was also updated to utilize scene delegates and implicit engine delegation. Review feedback highlights a potential issue where the Dart Future could hang if OS version requirements are not met in the Swift code, the need for a minimum Flutter version constraint in the plugin's pubspec, and the presence of accidental changes to the example app's bundle identifier and Xcode project version.

Comment on lines +324 to +367
func presentOfferCodeRedeemSheet(completion: @escaping (Result<Void, Error>) -> Void) {
#if os(iOS)
if #available(iOS 16.0, *) {
guard let windowScene = self.registrar?.viewController?.view.window?.windowScene else {
let error = PigeonError(
code: "storekit2_missing_key_window_scene",
message: "Failed to fetch key window scene",
details: "registrar.viewController.view.window.windowScene returned nil."
)
completion(.failure(error))
return
}
Task { @MainActor in
do {
try await AppStore.presentOfferCodeRedeemSheet(in: windowScene)
completion(.success(()))
} catch {
completion(.failure(error))
}
}
}
#elseif os(macOS)
if #available(macOS 15.0, *) {
guard let viewController = self.registrar?.viewController else {
let error = PigeonError(
code: "storekit2_missing_view_controller",
message: "Failed to fetch view controller",
details: "registrar.viewController returned nil."
)
completion(.failure(error))
return
}

Task { @MainActor in
do {
try await AppStore.presentOfferCodeRedeemSheet(from: viewController)
completion(.success(()))
} catch {
completion(.failure(error))
}
}
}
#endif
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The completion handler is not called if the OS version requirements are not met (iOS < 16.0 or macOS < 15.0), or if the platform is neither iOS nor macOS. This will cause the Dart Future to hang indefinitely. An error should be returned in these cases to ensure the caller receives a response.

  func presentOfferCodeRedeemSheet(completion: @escaping (Result<Void, Error>) -> Void) {
    #if os(iOS)
      if #available(iOS 16.0, *) {
        guard let windowScene = self.registrar?.viewController?.view.window?.windowScene else {
          let error = PigeonError(
            code: "storekit2_missing_key_window_scene",
            message: "Failed to fetch key window scene",
            details: "registrar.viewController.view.window.windowScene returned nil."
          )
          completion(.failure(error))
          return
        }
        Task { @MainActor in
          do {
            try await AppStore.presentOfferCodeRedeemSheet(in: windowScene)
            completion(.success(()))
          } catch {
            completion(.failure(error))
          }
        }
      } else {
        completion(.failure(PigeonError(
          code: "storekit2_unsupported_version",
          message: "Offer code redemption via StoreKit 2 requires iOS 16.0 or later.",
          details: nil
        )))
      }
    #elseif os(macOS)
      if #available(macOS 15.0, *) {
        guard let viewController = self.registrar?.viewController else {
          let error = PigeonError(
            code: "storekit2_missing_view_controller",
            message: "Failed to fetch view controller",
            details: "registrar.viewController returned nil."
          )
          completion(.failure(error))
          return
        }

        Task { @MainActor in
          do {
            try await AppStore.presentOfferCodeRedeemSheet(from: viewController)
            completion(.success(()))
          } catch {
            completion(.failure(error))
          }
        }
      } else {
        completion(.failure(PigeonError(
          code: "storekit2_unsupported_version",
          message: "Offer code redemption via StoreKit 2 requires macOS 15.0 or later.",
          details: nil
        )))
      }
    #else
      completion(.failure(PigeonError(
        code: "storekit2_unsupported_platform",
        message: "Offer code redemption via StoreKit 2 is not supported on this platform.",
        details: nil
      )))
    #endif
  }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is fine bc users should not be on storekit 2 below ios 15

Comment on lines 7 to 8
environment:
sdk: ^3.9.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The plugin now utilizes new Flutter APIs such as registrar.addSceneDelegate and FlutterSceneLifeCycleDelegate. A minimum Flutter version constraint should be added to the environment section to prevent compilation failures for users on older Flutter SDKs.

environment:
  sdk: ^3.9.0
  flutter: ">=3.38.0"

"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.inAppPurchaseExample;
PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.inAppPurchaseExample9;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The bundle identifier has been changed to include a '9' at the end. This appears to be a local testing change and should be reverted to maintain the correct identifier for the example app.

				PRODUCT_BUNDLE_IDENTIFIER = dev.flutter.plugins.in_app_purchaseExample;

classes = {
};
objectVersion = 60;
objectVersion = 54;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The objectVersion has been downgraded from 60 to 54. This typically occurs when a project is opened in an older version of Xcode. It should be reverted to 60 to maintain consistency with the repository's development environment.

	objectVersion = 60;

@gemini-code-assist
Copy link
Copy Markdown

Hello! How can I help you with this pull request today?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[in_app_purchase][iOS] Consider adding support for offerCodeRedemption(isPresented:onCompletion:)

1 participant