[in_app_purchase] Implement registerPlatform method for iOS implementation (#3851)

diff --git a/packages/in_app_purchase/in_app_purchase_ios/README.md b/packages/in_app_purchase/in_app_purchase_ios/README.md
index 025ed36..3ab0338 100644
--- a/packages/in_app_purchase/in_app_purchase_ios/README.md
+++ b/packages/in_app_purchase/in_app_purchase_ios/README.md
@@ -31,4 +31,7 @@
   ...
 ```
 
+## TODO
+- [ ] Add an example application demonstrating the use of the [in_app_purchase_ios] package (see also issue [flutter/flutter#81695](https://github.com/flutter/flutter/issues/81695)).
+
 [1]: ../in_app_purchase/in_app_purchase
\ No newline at end of file
diff --git a/packages/in_app_purchase/in_app_purchase_ios/lib/src/in_app_purchase_ios_platform.dart b/packages/in_app_purchase/in_app_purchase_ios/lib/src/in_app_purchase_ios_platform.dart
index bb2fd2b..5a2c408 100644
--- a/packages/in_app_purchase/in_app_purchase_ios/lib/src/in_app_purchase_ios_platform.dart
+++ b/packages/in_app_purchase/in_app_purchase_ios/lib/src/in_app_purchase_ios_platform.dart
@@ -23,10 +23,6 @@
 /// This translates various `StoreKit` calls and responses into the
 /// generic plugin API.
 class InAppPurchaseIosPlatform extends InAppPurchasePlatform {
-  /// Returns the singleton instance of the [InAppPurchaseIosPlatform] that should be
-  /// used across the app.
-  static InAppPurchaseIosPlatform get instance => _getOrCreateInstance();
-  static InAppPurchaseIosPlatform? _instance;
   static late SKPaymentQueueWrapper _skPaymentQueueWrapper;
   static late _TransactionObserver _observer;
 
@@ -44,22 +40,19 @@
   @visibleForTesting
   static SKTransactionObserverWrapper get observer => _observer;
 
-  static InAppPurchaseIosPlatform _getOrCreateInstance() {
-    if (_instance != null) {
-      return _instance!;
-    }
-
+  /// Registers this class as the default instance of [InAppPurchasePlatform].
+  static void registerPlatform() {
     // Register the [InAppPurchaseIosPlatformAddition] containing iOS
     // platform-specific functionality.
     InAppPurchasePlatformAddition.instance = InAppPurchaseIosPlatformAddition();
 
     // Register the platform-specific implementation of the idiomatic
     // InAppPurchase API.
-    _instance = InAppPurchaseIosPlatform();
+    InAppPurchasePlatform.setInstance(InAppPurchaseIosPlatform());
+
     _skPaymentQueueWrapper = SKPaymentQueueWrapper();
     _observer = _TransactionObserver(StreamController.broadcast());
     _skPaymentQueueWrapper.setTransactionObserver(observer);
-    return _instance!;
   }
 
   @override
diff --git a/packages/in_app_purchase/in_app_purchase_ios/test/in_app_purchase_ios_platform_test.dart b/packages/in_app_purchase/in_app_purchase_ios/test/in_app_purchase_ios_platform_test.dart
index a70e2d9..b15249c 100644
--- a/packages/in_app_purchase/in_app_purchase_ios/test/in_app_purchase_ios_platform_test.dart
+++ b/packages/in_app_purchase/in_app_purchase_ios/test/in_app_purchase_ios_platform_test.dart
@@ -18,19 +18,24 @@
   TestWidgetsFlutterBinding.ensureInitialized();
 
   final FakeIOSPlatform fakeIOSPlatform = FakeIOSPlatform();
+  late InAppPurchaseIosPlatform iapIosPlatform;
 
   setUpAll(() {
     SystemChannels.platform
         .setMockMethodCallHandler(fakeIOSPlatform.onMethodCall);
   });
 
-  setUp(() => fakeIOSPlatform.reset());
+  setUp(() {
+    InAppPurchaseIosPlatform.registerPlatform();
+    iapIosPlatform = InAppPurchasePlatform.instance as InAppPurchaseIosPlatform;
+    fakeIOSPlatform.reset();
+  });
 
   tearDown(() => fakeIOSPlatform.reset());
 
   group('isAvailable', () {
     test('true', () async {
-      expect(await InAppPurchaseIosPlatform.instance.isAvailable(), isTrue);
+      expect(await iapIosPlatform.isAvailable(), isTrue);
     });
   });
 
@@ -69,8 +74,7 @@
   group('restore purchases', () {
     test('should emit restored transactions on purchase stream', () async {
       Completer completer = Completer();
-      Stream<List<PurchaseDetails>> stream =
-          InAppPurchaseIosPlatform.instance.purchaseStream;
+      Stream<List<PurchaseDetails>> stream = iapIosPlatform.purchaseStream;
 
       late StreamSubscription subscription;
       subscription = stream.listen((purchaseDetailsList) {
@@ -80,7 +84,7 @@
         }
       });
 
-      await InAppPurchaseIosPlatform.instance.restorePurchases();
+      await iapIosPlatform.restorePurchases();
       List<PurchaseDetails> details = await completer.future;
 
       expect(details.length, 2);
@@ -103,8 +107,7 @@
       fakeIOSPlatform.transactions
           .insert(0, fakeIOSPlatform.createPurchasedTransaction('foo', 'bar'));
       Completer completer = Completer();
-      Stream<List<PurchaseDetails>> stream =
-          InAppPurchaseIosPlatform.instance.purchaseStream;
+      Stream<List<PurchaseDetails>> stream = iapIosPlatform.purchaseStream;
 
       late StreamSubscription subscription;
       subscription = stream.listen((purchaseDetailsList) {
@@ -113,7 +116,7 @@
           subscription.cancel();
         }
       });
-      await InAppPurchaseIosPlatform.instance.restorePurchases();
+      await iapIosPlatform.restorePurchases();
       List<PurchaseDetails> details = await completer.future;
       expect(details.length, 3);
       for (int i = 0; i < fakeIOSPlatform.transactions.length; i++) {
@@ -139,8 +142,7 @@
         () async {
       fakeIOSPlatform.receiptData = null;
       Completer completer = Completer();
-      Stream<List<PurchaseDetails>> stream =
-          InAppPurchaseIosPlatform.instance.purchaseStream;
+      Stream<List<PurchaseDetails>> stream = iapIosPlatform.purchaseStream;
 
       late StreamSubscription subscription;
       subscription = stream.listen((purchaseDetailsList) {
@@ -150,7 +152,7 @@
         }
       });
 
-      await InAppPurchaseIosPlatform.instance.restorePurchases();
+      await iapIosPlatform.restorePurchases();
       List<PurchaseDetails> details = await completer.future;
 
       for (PurchaseDetails purchase in details) {
@@ -166,7 +168,7 @@
           userInfo: {'message': 'errorMessage'});
 
       expect(
-          () => InAppPurchaseIosPlatform.instance.restorePurchases(),
+          () => iapIosPlatform.restorePurchases(),
           throwsA(
             isA<SKError>()
                 .having((error) => error.code, 'code', 123)
@@ -183,8 +185,7 @@
         () async {
       List<PurchaseDetails> details = [];
       Completer completer = Completer();
-      Stream<List<PurchaseDetails>> stream =
-          InAppPurchaseIosPlatform.instance.purchaseStream;
+      Stream<List<PurchaseDetails>> stream = iapIosPlatform.purchaseStream;
 
       late StreamSubscription subscription;
       subscription = stream.listen((purchaseDetailsList) {
@@ -198,8 +199,7 @@
           productDetails:
               AppStoreProductDetails.fromSKProduct(dummyProductWrapper),
           applicationUserName: 'appName');
-      await InAppPurchaseIosPlatform.instance
-          .buyNonConsumable(purchaseParam: purchaseParam);
+      await iapIosPlatform.buyNonConsumable(purchaseParam: purchaseParam);
 
       List<PurchaseDetails> result = await completer.future;
       expect(result.length, 2);
@@ -211,8 +211,7 @@
         () async {
       List<PurchaseDetails> details = [];
       Completer completer = Completer();
-      Stream<List<PurchaseDetails>> stream =
-          InAppPurchaseIosPlatform.instance.purchaseStream;
+      Stream<List<PurchaseDetails>> stream = iapIosPlatform.purchaseStream;
 
       late StreamSubscription subscription;
       subscription = stream.listen((purchaseDetailsList) {
@@ -226,8 +225,7 @@
           productDetails:
               AppStoreProductDetails.fromSKProduct(dummyProductWrapper),
           applicationUserName: 'appName');
-      await InAppPurchaseIosPlatform.instance
-          .buyConsumable(purchaseParam: purchaseParam);
+      await iapIosPlatform.buyConsumable(purchaseParam: purchaseParam);
 
       List<PurchaseDetails> result = await completer.future;
       expect(result.length, 2);
@@ -240,8 +238,8 @@
               AppStoreProductDetails.fromSKProduct(dummyProductWrapper),
           applicationUserName: 'appName');
       expect(
-          () => InAppPurchaseIosPlatform.instance
-              .buyConsumable(purchaseParam: purchaseParam, autoConsume: false),
+          () => iapIosPlatform.buyConsumable(
+              purchaseParam: purchaseParam, autoConsume: false),
           throwsA(isInstanceOf<AssertionError>()));
     });
 
@@ -251,8 +249,7 @@
       Completer completer = Completer();
       late IAPError error;
 
-      Stream<List<PurchaseDetails>> stream =
-          InAppPurchaseIosPlatform.instance.purchaseStream;
+      Stream<List<PurchaseDetails>> stream = iapIosPlatform.purchaseStream;
       late StreamSubscription subscription;
       subscription = stream.listen((purchaseDetailsList) {
         details.addAll(purchaseDetailsList);
@@ -268,8 +265,7 @@
           productDetails:
               AppStoreProductDetails.fromSKProduct(dummyProductWrapper),
           applicationUserName: 'appName');
-      await InAppPurchaseIosPlatform.instance
-          .buyNonConsumable(purchaseParam: purchaseParam);
+      await iapIosPlatform.buyNonConsumable(purchaseParam: purchaseParam);
 
       IAPError completerError = await completer.future;
       expect(completerError.code, 'purchase_error');
@@ -283,14 +279,13 @@
     test('should complete purchase', () async {
       List<PurchaseDetails> details = [];
       Completer completer = Completer();
-      Stream<List<PurchaseDetails>> stream =
-          InAppPurchaseIosPlatform.instance.purchaseStream;
+      Stream<List<PurchaseDetails>> stream = iapIosPlatform.purchaseStream;
       late StreamSubscription subscription;
       subscription = stream.listen((purchaseDetailsList) {
         details.addAll(purchaseDetailsList);
         purchaseDetailsList.forEach((purchaseDetails) {
           if (purchaseDetails.pendingCompletePurchase) {
-            InAppPurchaseIosPlatform.instance.completePurchase(purchaseDetails);
+            iapIosPlatform.completePurchase(purchaseDetails);
             completer.complete(details);
             subscription.cancel();
           }
@@ -300,8 +295,7 @@
           productDetails:
               AppStoreProductDetails.fromSKProduct(dummyProductWrapper),
           applicationUserName: 'appName');
-      await InAppPurchaseIosPlatform.instance
-          .buyNonConsumable(purchaseParam: purchaseParam);
+      await iapIosPlatform.buyNonConsumable(purchaseParam: purchaseParam);
       List<PurchaseDetails> result = await completer.future;
       expect(result.length, 2);
       expect(result.first.productID, dummyProductWrapper.productIdentifier);