<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Abhinand R K</title>
    <description>The latest articles on Forem by Abhinand R K (@abhinandrk).</description>
    <link>https://forem.com/abhinandrk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F896771%2F9e14b789-22e0-4ce3-8fad-b5e17a04cd74.jpeg</url>
      <title>Forem: Abhinand R K</title>
      <link>https://forem.com/abhinandrk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/abhinandrk"/>
    <language>en</language>
    <item>
      <title>Flutter’s MethodChannel to invoke Activity in Android and View Controller in iOS</title>
      <dc:creator>Abhinand R K</dc:creator>
      <pubDate>Sun, 11 Jun 2023 10:59:59 +0000</pubDate>
      <link>https://forem.com/abhinandrk/flutters-methodchannel-to-invoke-activity-in-android-and-view-controller-in-ios-3f7j</link>
      <guid>https://forem.com/abhinandrk/flutters-methodchannel-to-invoke-activity-in-android-and-view-controller-in-ios-3f7j</guid>
      <description>&lt;p&gt;Flutter lets you use Dart’s inbuilt cross-platform standard library for general-purpose programming requirements (i.e., I/O, Math, etc.). It also offers the platform channels API to communicate with platform-specific code to use operating-system-level APIs or invoke code segments written in the native app development language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Getting Started&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12x0igv2aqgqcoq7jtlb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F12x0igv2aqgqcoq7jtlb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To trigger native screens from the Flutter side, we will create an Activity in Android and a View Controller in iOS. We can achieve this by utilizing the method channel. The process is as follows:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Android:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an Activity in Android to represent the native screen.&lt;/li&gt;
&lt;li&gt;Set up the method channel to listen for triggers from Flutter.&lt;/li&gt;
&lt;li&gt;Handle the method call in the method channel's method call handler to launch the native screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;iOS:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a View Controller in iOS to display the native screen.&lt;/li&gt;
&lt;li&gt;Set up the method channel to receive triggers from Flutter.&lt;/li&gt;
&lt;li&gt;Implement the method call handler to present or push the native screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By implementing this approach, we can seamlessly trigger the native screens from Flutter using the method channel. It's an exciting and powerful feature that allows seamless integration between Flutter and the native platforms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First Code&lt;/strong&gt;&lt;br&gt;
 I assume you already own your Flutter SDK, Android SDK, XCode (with iOS SDK absolutely), and integrate it with your IDE. So I start with creating new project like usual, here I use Android Studio as my IDE because Android Studio is my daily weapon as Native Android Engineer, you can use your IDE here. Create your first project, if you don’t know how to do it, please search in google like How to create Flutter project . After that Flutter will generate new code, see the code in lib folder and you can found main.dart as the only one files.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State&amp;lt;MyHomePage&amp;gt; createState() =&amp;gt; _MyHomePageState();
}

class _MyHomePageState extends State&amp;lt;MyHomePage&amp;gt; {
  static const platform = MethodChannel("com.valar.morghulis");

  _MyHomePageState() {
    platform.setMethodCallHandler(_methodHandler);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: &amp;lt;Widget&amp;gt;[
            const Text(
              'Take me to native screen',
            ),
            MaterialButton(
              child: Text("Navigate"),
              onPressed: _didTapButton,
              color: Colors.grey,
            ),
          ],
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  Future&amp;lt;Null&amp;gt; _didTapButton() async {
    var result = await platform.invokeMethod("method_name");
  }

  Future&amp;lt;dynamic&amp;gt; _methodHandler(MethodCall call) async {
    switch (call.method) {
      case "message":
        debugPrint(call.arguments);
        return new Future.value("");
    }
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is class will show as our Main Page, there is one button if you click it will trigger the _didTapButton() method. This method will use platform variable, which is the Bridge that use MethodChannel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kotlin Code
&lt;/h2&gt;

&lt;p&gt;To receive triggers from the Flutter side, we need to write code in Kotlin. The first step is to initialize the method channel handler, which will be called from Flutter. Please refer to the code below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

class MainActivity: FlutterActivity() {

    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
                call, result -&amp;gt;
            val intent = Intent(this, SecondActivity::class.java)
            startActivity(intent)
            result.success(true)
        }

    }

    companion object {
        const val CHANNEL = "com.valar.morghulis"
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the code above, replace channel name(com.valar.morghulis) with the actual name of your method channel. This code sets up the handler to receive method calls from Flutter and allows you to handle them accordingly in Kotlin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzuzo07r5uiplriahgq8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzuzo07r5uiplriahgq8.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's write some code in Xcode
&lt;/h2&gt;

&lt;p&gt;For Swift, we need to set up a method handler to receive triggers from Flutter, similar to what we did in Android.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;import UIKit&lt;br&gt;
import Flutter&lt;/p&gt;

&lt;p&gt;@UIApplicationMain&lt;br&gt;
@objc class AppDelegate: FlutterAppDelegate {&lt;br&gt;
  override func application(&lt;br&gt;
    _ application: UIApplication,&lt;br&gt;
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?&lt;br&gt;
  ) -&amp;gt; Bool {&lt;br&gt;
    GeneratedPluginRegistrant.register(with: self)&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  let controller : FlutterViewController = window?.rootViewController as! FlutterViewController

  let methodChannel = FlutterMethodChannel(name: "com.valar.morghulis", binaryMessenger:controller.binaryMessenger)

  methodChannel.setMethodCallHandler({
    (call: FlutterMethodCall, result: @escaping FlutterResult) -&amp;amp;gt; Void in

    if call.method == "method_name" {
        DispatchQueue.main.async {
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            let vc: CustomViewController = storyboard.instantiateViewController(withIdentifier: "CustonVC") as! CustomViewController
            controller.present(vc, animated: true, completion: nil)
        }
    } else {
      result(FlutterMethodNotImplemented)
      return
    }
  })

return super.application(application, didFinishLaunchingWithOptions: launchOptions)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;br&gt;
}&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Result&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzka2dxb3knhppnk07w7w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzka2dxb3knhppnk07w7w.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We studied the Flutter platform channels API and tested the MethodChannel class with practical examples in this tutorial.  This tutorial focused on calling Kotlin and Swift code from Flutter.&lt;/p&gt;

&lt;p&gt;We typically have to call Kotlin code either to reuse it or to call Android SDK functions. If you’ve migrated to Flutter from a native Android app, but you still have a lot of business logic written in a Kotlin/Java package or you need to reuse your Java-based web API’s business logic, you can use MethodChannel for calling your existing Kotlin code efficiently.&lt;/p&gt;

&lt;p&gt;We made use of method channel to trigger view controller in ios and Activity in android. I have not convered how to create activity and view controller since it is out of scope.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>In-App Purchase Implementation in iOS</title>
      <dc:creator>Abhinand R K</dc:creator>
      <pubDate>Sun, 24 Jul 2022 06:54:23 +0000</pubDate>
      <link>https://forem.com/abhinandrk/in-app-purchase-implementation-in-ios-37ce</link>
      <guid>https://forem.com/abhinandrk/in-app-purchase-implementation-in-ios-37ce</guid>
      <description>&lt;p&gt;In this blog post i will explain how to integrate In-App purchase in iOS App.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;An in-app purchase (or IAP) allows developers to charge users for specific functionality or content while using an app. Implementing IAPs is particularly compelling for several reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It’s an extra way to earn money, in addition to simply selling the app for a fee upfront. Some users are willing to spend a lot more on extra content or features.&lt;/li&gt;
&lt;li&gt;An app can be offered for free, which makes it a no-brainer download for most people. Free apps will typically get many more downloads than paid apps. If users enjoy the app, then they can purchase more content or functionality later.&lt;/li&gt;
&lt;li&gt;You can display advertisements to the user in a free app with an option to remove them by purchasing an IAP.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;You can only offer In-App Purchases for digital items(eg: Byju's,Netflix), and not for physical goods or services(eg: Amazon, Flipkart)&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is IAP?
&lt;/h2&gt;

&lt;p&gt;According to Apple, In-App Purchase allows you to offer users the opportunity to purchase in-app content and features. Purchases can be made within your app, or directly from the App Store. The StoreKit framework connects to the App Store on your app’s behalf to prompt for and securely process payments. The framework then notifies your app, which delivers the purchased products. To validate purchases, you can verify receipts on your server with the App Store or on the device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42lx7r1o2nj5nxwylngs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F42lx7r1o2nj5nxwylngs.png" alt="Different components in In-App Purchase flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of IAP
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Consumable&lt;/strong&gt;: It’s when you buy something to progress in your app, for example, life in a game. You basically buy it once, use it, and afterward it is possible to buy it again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-consumable&lt;/strong&gt;: Users buy these items one time, can be used in the future for free. On reinstalling, changing devices these products will not be lost. If the user loses, might be able to download it again for free by restoring in-app purchases. For example: upgrading the app to the pro version, removing ads, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Non-renewing subscriptions&lt;/strong&gt;: The user will be able to use these items for a fixed period of time, these items can be purchase again after the subscription end. For example: a sport session pass for one, three, or six months.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auto-renewable subscriptions&lt;/strong&gt;:  The user can buy these item for a specified period of time, It’ll automatically renew when the period has passed. For example: Ongoing services (Netflix, Hulu Plus, etc.), Magazine subscriptions etc.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In this blog we will be implementing Non-renewing subscriptions&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Creating In-App Purchase Products
&lt;/h2&gt;

&lt;p&gt;When offering IAPs you must first add an entry for each individual purchase within App Store Connect. If you’ve ever listed an app for sale in the store, it’s a similar process and includes things like choosing a pricing tier for the purchase. When the user makes a purchase, the App Store handles the complex process of charging the user and replies with data about such operation.&lt;/p&gt;

&lt;p&gt;Now, while viewing your app’s entry in App Store Connect, click on the Features tab and then select In-App Purchases. To add a new IAP product, click the + to the right of In-App Purchases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66ntrnghql342m4wefyv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66ntrnghql342m4wefyv.jpg" alt="Create In-App Purchase product"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll see the following dialog appear: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxki7fngkzdq4b6y1lr0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxki7fngkzdq4b6y1lr0c.png" alt="Different types of In-App purchase products"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, fill out the details for the IAP as follows&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reference Name: A nickname identifying the IAP within iTunes Connect. This name does not appear anywhere in the app. The title of the RazeFace you’ll be unlocking with this purchase is Swift Shopping, so enter that here.&lt;/li&gt;
&lt;li&gt;Product ID: This is a unique string identifying the IAP. Usually it’s best to start with the Bundle ID and then append a unique name specific to this purchasable item. For example, you can use: com.iq.colearn.swiftshopping.&lt;/li&gt;
&lt;li&gt;Cleared for Sale: Enables or disables the sale of the IAP. You want to enable it!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Price Tier: The cost of the IAP. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;App Store Connect may complain that you’re missing metadata for your IAP. Before you submit your app for review, you’re required to add a screenshot of the IAP at the bottom of this page. The screenshot is used only for Apple’s review and does not appear in your App Store listing.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Sandbox User Creation
&lt;/h2&gt;

&lt;p&gt;Now, we need to create a sandbox user, to create navigate to Users and Roles in iTunes connect account and choose the Sandbox Testers section. It is very important to add sandbox user to test the IAP services, by sandbox user you can make transactions for free.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3xyer34byt0ws2pqomv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3xyer34byt0ws2pqomv.png" alt="Sandbox user creation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Always remember to use an email that is not associated with any Apple ID and also it should be a valid email id.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Project Configuration
&lt;/h2&gt;

&lt;p&gt;Head over to the Capabilities tab and scroll down to In-App Purchase and toggle the switch to ON.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jtra4ncs3v7fk6gtmok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5jtra4ncs3v7fk6gtmok.png" alt="Toggle in-App purchase capability in Project configuration section in Xcode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code in Swift
&lt;/h2&gt;

&lt;p&gt;We will be using the StoreKit framework provided by Apple which is surprisingly easy to implement. Fundamentally, there is a PaymentQueue where all transactions will end up. You will have then to listen to this queue and act accordingly. The flow should look like this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohv30o1riro9r0746f1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fohv30o1riro9r0746f1g.png" alt="Flow chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, we will be making a separate class to handle StoreKit functions. Create a InAppPurchaseHandler.swift file to write In app purchase related code. It will be a singleton class.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import StoreKit

class MyStoreKitDelegate: NSObject {

    let monthlySubID = "MyApp.sub.allaccess.monthly"
    let yearlySubID = "MyApp.sub.allaccess.yearly"
    var products: [String: SKProduct] = [:]

    func fetchProducts() {
        let productIDs = Set([monthlySubID, yearlySubID])
        let request = SKProductsRequest(productIdentifiers: productIDs)
        request.delegate = self
        request.start()
    }

    func purchase(productID: String) {
        if let product = products[productID] {
            let payment = SKPayment(product: product)
            SKPaymentQueue.default().add(payment)
        }
    }

    func restorePurchases() {
        SKPaymentQueue.default().restoreCompletedTransactions()
    }
}

extension MyStoreKitDelegate: SKProductsRequestDelegate {

    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        response.invalidProductIdentifiers.forEach { product in
            print("Invalid: \(product)")
        }

        response.products.forEach { product in
            print("Valid: \(product)")
            products[product.productIdentifier] = product
        }
    }

    func request(_ request: SKRequest, didFailWithError error: Error) {
        print("Error for request: \(error.localizedDescription)")
    }

}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let’s go over it. Some key concepts are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SKPaymentQueue&lt;/strong&gt;: Queue where all transactions are being held for further processing&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SKProduct&lt;/strong&gt;: Product declared in Itunes Connect with all the available information&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SKPayment&lt;/strong&gt;: Intent of purchase of a product&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SKPaymentTransaction&lt;/strong&gt;: Event regarding a SKProduct&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SKProductRequest&lt;/strong&gt;: Request to fetch information about SKProducts giving the products ID&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SKProductResponse&lt;/strong&gt;: Response containing the desired products. It consists of two lists: products and invalid product identifiers. The first one will contain the successfully fetched SKProducts, the second one all the identifiers that fail to correlate to a SKProduct. If you are getting invalid identifiers, here is a troubleshooting list that could help you out.&lt;/p&gt;

&lt;p&gt;Now, firstly, with the following lines, you can easily make a SKProductRequest.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

let request = SKProductsRequest(productIdentifiers: productIDs)
request.delegate = self
request.start()


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Make sure you implement SKProductsRequestDelegate since the following method is the callback.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There you will get all the SKProducts available for your app. To make a purchase go ahead and create a SKPayment.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

let payment = SKPayment(product: product)
SKPaymentQueue.default().add(payment)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;&amp;gt; Make sure you always fetch the products even before allowing the user to make a purchase. These SKProducts will have the price localized inside them so you should display it. This will make sure you always have the data in your app up to date, as having the wrong price in the app can cause Apple to remove it from the App Store.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  External event section
&lt;/h2&gt;

&lt;p&gt;You may also notice in the flow an External event section. Transactions can happen outside your App. Say your user changes their subscription type in the system settings, or your deferred transaction was approved by the user’s parents, you won’t be able to tell unless you are expecting them. For that reason, always, in the AppDelegate, at the start of your app, subscribe the app to the PaymentQueue. This way you will make sure you won’t miss any event.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

class AppDelegate: UIResponder, UIApplicationDelegate, SKPaymentTransactionObserver {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -&amp;gt; Bool {
        SKPaymentQueue.default().add(self)
        //...
        return true
    }

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        for transaction in transactions {
            switch transaction.transactionState {
            case .failed:
                queue.finishTransaction(transaction)
                print("Transaction Failed \(transaction)")
            case .purchased, .restored:
                queue.finishTransaction(transaction)
                print("Transaction purchased or restored: \(transaction)")
            case .deferred, .purchasing:
                print("Transaction in progress: \(transaction)")
            }
        }
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Validating Receipts with the App Store
&lt;/h2&gt;

&lt;p&gt;Paid software has always presented a problem where some users try to use the software without buying it or fraudulently access in-app purchases. Receipts provide a tool to confirm those purchases. They accomplish this by providing a record of sales. The App Store generates a receipt in the app bundle any time a user purchases your app, makes an in-app purchase, or updates the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fetch the Receipt Data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To retrieve the receipt data from the app on the device, use the appStoreReceiptURL method of NSBundle to locate the app’s receipt, and encode the data in Base64. Send this Base64-encoded data to your server.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// Get the receipt if it's available
if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
    FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {

    do {
        let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
        print(receiptData)

        let receiptString = receiptData.base64EncodedString(options: [])

        // Read receiptData
    }
    catch { print("Couldn't read receipt data with error: " + error.localizedDescription) }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Validate Receipt&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is two verifyReceiptURL for gathering the information from iTunes for both sandbox and live. The verifyReceiptURL API having 2 params. One is receipt-data which contains local receipt stored data and the second one is the password where you have to pass Shared Secret.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sandbox: &lt;a href="https://sandbox.itunes.apple.com/verifyReceipt" rel="noopener noreferrer"&gt;https://sandbox.itunes.apple.com/verifyReceipt&lt;/a&gt;&lt;br&gt;
Live: &lt;a href="https://buy.itunes.apple.com/verifyReceipt" rel="noopener noreferrer"&gt;https://buy.itunes.apple.com/verifyReceipt&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Handling Refund Notifications
&lt;/h2&gt;

&lt;p&gt;App Store Server sends near real-time notifications when customers receive refunds for in-app purchases. If you offer content across multiple platforms, for example, gems or coins for games, and you update player account balances on your server, receiving refund notifications is important. Respond to refund notifications by interpreting and handling the refund information, and informing customers in the app of any actions you take as a result of the refund.&lt;/p&gt;

&lt;p&gt;When the App Store processes a refund, the App Store Server sends a REFUND notification to your server, at the URL you configured. Your server must respond to the post with a 200 response code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The REFUND notification applies to consumable, non-consumable, and non-renewing subscriptions only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Firstly, sign in to your iOS device using the created Sandbox user account, then run your application on the real device and initiate the transaction. Don’t worry about the price that is being displayed in the Alert window just go further. Nothing will be charged as you are a sandbox user for the application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In App Purchases can not be tested on iOS simulator. So, Please use real device.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>inapppurchase</category>
      <category>xcode</category>
    </item>
  </channel>
</rss>
