Month: February 2022

  • Create local notification in Swift

    Create local notification in Swift

    Create local notification in Swift

    To create a local notification in Swift, you will need to use the UserNotifications framework. Here is an example of how to do this:

    import UserNotifications
    
    // Create the notification content
    let content = UNMutableNotificationContent()
    content.title = "Title of your notification"
    content.body = "Body of your notification"
    
    // Set the trigger for the notification
    let date = Date().addingTimeInterval(10) // 10 seconds from now
    let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date)
    let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)
    
    // Create the request for the notification
    let request = UNNotificationRequest(identifier: "YourNotificationIdentifier", content: content, trigger: trigger)
    
    // Add the request to the notification center
    UNUserNotificationCenter.current().add(request) { (error) in
        if let error = error {
            print("Error adding notification request: \(error)")
        }
    }
    

    This code will create a notification that will be triggered 10 seconds from the time it is added to the notification center. When the notification is triggered, it will display the title and body that you set in the content object.

    Spread the love
  • Swift Inheritance

    Swift Inheritance

    Swift Inheritance

    In Swift, a class can inherit properties and methods from another class, which is known as inheritance. This allows the child class (also known as the subclass) to reuse the functionality of the parent class (also known as the superclass), and to extend or modify that functionality as needed.

    Here is an example of how to define a superclass and a subclass in Swift:

    class Vehicle {
        var numberOfWheels: Int
        var color: String
    
        init(numberOfWheels: Int, color: String) {
            self.numberOfWheels = numberOfWheels
            self.color = color
        }
    
        func startEngine() {
            print("Vroom!")
        }
    }
    
    class Bicycle: Vehicle {
        var hasBasket: Bool
    
        init(numberOfWheels: Int, color: String, hasBasket: Bool) {
            self.hasBasket = hasBasket
            super.init(numberOfWheels: numberOfWheels, color: color)
        }
    
        override func startEngine() {
            // Bicycles don't have engines, so we'll just print a message instead
            print("Pedaling along...")
        }
    }
    

    In this example, the Vehicle class is the superclass, and the Bicycle class is the subclass. The Bicycle class inherits the numberOfWheels and color properties, as well as the startEngine() method from the Vehicle class. It also adds its own hasBasket property and overrides the startEngine() method to provide its own implementation.

    You can then create instances of the Bicycle class and use them like this:

    let myBike = Bicycle(numberOfWheels: 2, color: "red", hasBasket: true)
    myBike.startEngine()  // Output: "Pedaling along..."
    

    In this code, the myBike variable is an instance of the Bicycle class, which inherits the properties and methods of the Vehicle class and provides its own implementation of the startEngine() method. When you call the startEngine() method on the myBike instance, it uses the implementation provided by the Bicycle class, which prints the message “Pedaling along…”.

    Spread the love
  • Auto-renewable subscriptions with SwiftUI

    Auto-renewable subscriptions with SwiftUI

    Auto-renewable subscriptions with SwiftUI

    Read about App Store Server Integration in Laravel

    Auto-renewable subscriptions provide access to content, services, or premium features in your app on an ongoing basis. They automatically renew at the end of their duration until the user chooses to cancel. Subscriptions are available on iOS, iPadOS, macOS, watchOS, and tvOS.

    Great subscription apps justify the recurring payment by providing ongoing value and continually innovating the app experience. If you’re considering implementing the subscription model, plan to regularly update your app with feature enhancements or expanded content.

    To offer subscriptions, youʼll need to configure them in App Store Connect and use StoreKit APIs in your app. You’ll also need to assign each subscription to a subscription group (a group of subscriptions with different access levels, prices, and durations that people can choose from), then add details such as a name, price, and description. This information displays in the In-App Purchases section of your app’s product page on the App Store. Ensure that the subscriptions are available across all device types that your app supports. Consider allowing a way for subscribers to see the status of their subscription within your app, along with upgrade, crossgrade, and downgrade options, as well as a way to easily manage or turn off their auto-renewable subscription. Make sure to follow our design and review guidelines.

    To get ready:

    Before creating any subscription make sure you have done all these steps and all are showing Active status. 

    Auto-renewable subscriptions with SwiftUI

    Go here https://appstoreconnect.apple.com/agreements/#

    Creating subscriptions

    Go to app detail page and click on the In-App purchase link under Feature. 

    Select Auto-Renewable Subscription and click the Create button.

    Auto-renewable subscriptions with SwiftUI

    SwiftUI View-Model

    //
    //  AppStoreManager.swift
    //  ARSubscription
    //
    //  Created by Smin Rana on 2/1/22.
    //
    
    import SwiftUI
    import StoreKit
    import Combine
    
    class AppStoreManager: NSObject, ObservableObject, SKProductsRequestDelegate, SKPaymentTransactionObserver {
    
        @Published var products = [SKProduct]()
        
        override init() {
            super.init()
            
            SKPaymentQueue.default().add(self)
        }
        
        func getProdcut(indetifiers: [String]) {
            print("Start requesting products ...")
            let request = SKProductsRequest(productIdentifiers: Set(indetifiers))
            request.delegate = self
            request.start()
        }
        
        func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
            print("Did receive response")
                    
            if !response.products.isEmpty {
                for fetchedProduct in response.products {
                    DispatchQueue.main.async {
                        self.products.append(fetchedProduct)
                    }
                }
            }
            
            for invalidIdentifier in response.invalidProductIdentifiers {
                print("Invalid identifiers found: \(invalidIdentifier)")
            }
        }
        
        func request(_ request: SKRequest, didFailWithError error: Error) {
            print("Request did fail: \(error)")
        }
        
        // Transaction
        
        @Published var transactionState: SKPaymentTransactionState?
        
        func purchaseProduct(product: SKProduct) {
            if SKPaymentQueue.canMakePayments() {
                let payment = SKPayment(product: product)
                SKPaymentQueue.default().add(payment)
            } else {
                print("User can't make payment.")
            }
        }
        
        struct PaymentReceiptResponseModel: Codable {
            var status: Int
            var email: String?
            var password: String?
            var message: String?
        }
        
        func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
            for transaction in transactions {
                switch transaction.transactionState {
                case .purchasing:
                    self.transactionState = .purchasing
                case .purchased:
                    print("===============Purchased================")
                    UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
                    if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
                        FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {
    
                        do {
                            let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
                            let receiptString = receiptData.base64EncodedString(options: [])
                            
                            // TODO: Send your receiptString to the server and verify with Apple
                            // receiptString should be sent to server as JSON
                            // {
                            //    "receipt" : receiptString
                            // }
                            
                            self.transactionState = .purchased // only if server sends successful response
                        }
                        catch { print("Couldn't read receipt data with error: " + error.localizedDescription) }
                    }
                case .restored:
                    UserDefaults.standard.setValue(true, forKey: transaction.payment.productIdentifier)
    
                    queue.finishTransaction(transaction)
                    print("==================RESTORED State=============")
                    self.transactionState = .restored
                case .failed, .deferred:
                    print("Payment Queue Error: \(String(describing: transaction.error))")
                    queue.finishTransaction(transaction)
                    self.transactionState = .failed
                default:
                    print(">>>> something else")
                    queue.finishTransaction(transaction)
                }
            }
        }
        
        func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
            print("===============Restored================")
            if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,
                FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {
    
                do {
                    let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
                    let receiptString = receiptData.base64EncodedString(options: [])
                    
                    // TODO: Send your receiptString to the server and verify with Apple
                    // receiptString should be sent to server as JSON
                    // {
                    //    "receipt" : receiptString
                    // }
                    
                    
                    self.transactionState = .purchased // only if server sends successful response
                }
                catch { print("Couldn't read receipt data with error: " + error.localizedDescription) }
            }
        }
        
        func restorePurchase() {
            SKPaymentQueue.default().restoreCompletedTransactions()
        }
    }
    
    

    Testing with .storekit file on simulator:

    https://youtu.be/e7Tflo6AUH4

    Server side with PHP and Laravel:

    Create app specific shared secret

    Auto-renewable subscriptions with SwiftUI

    PHP and Laravel

    <?php 
    
    // Laravel and GuzzleHTTP\Client
    
    function verifyReceiptWithApple() {
            $input = file_get_contents('php://input');
            $request = json_decode($input);
    
            $d = $request->receipt;
            $secret = 'your_app_purchase_secret';
    
            //$url = 'https://sandbox.itunes.apple.com/verifyReceipt';
            $url = 'https://buy.itunes.apple.com/verifyReceipt';
    
            // Replace with curl if you are not using Laravel
            $client = new Client([
                'headers' => [ 'Content-Type' => 'application/json' ]
            ]);
            
            $response = $client->post($url,
                ['body' => json_encode(
                    [
                        'receipt-data' => $d,
                        'password' => $secret,
                        'exclude-old-transactions' => false
                    ]
                )]
            );
    
            $json = json_decode($response->getBody()->getContents());
            if ($json->status == 0) {
    
                $email = "";
    
                // Get original transaction id
                $receipts = $json->receipt->in_app;
                if (!empty($receipts) && count($receipts) > 0) {
                    $first_receipt = $receipts[0];
                    if ($first_receipt->in_app_ownership_type == "PURCHASED") {
                        $original_transaction_id = $first_receipt->original_transaction_id;
    
                        // Create email address with transaction id 
                        // Create new user if not exists
                        $email = $original_transaction_id.'@domain.com';
                        $have_user = "check_with_your_database";
                        if (!$have_user) {
                            // New purchase -> user not found
                        } else {
                            // Restore purchase -> user found 
                            
                        }
                    }
                }
    
                return response()->json(["status" => 1, "message" => "Receipt is verified"]);
            } else {
                return response()->json(["status" => 0, "message" => "Invalid receipt"]);
            }
        }

    Things to include in-app purchase

    • Value proposition
    • Call to action
    • Clear terms
    • Signup
    • Multiple tiers
    • Log in
    • Restore
    • Terms and conditions

    Read more

    App store distribution and marketing: https://developer.apple.com/videos/app-store-distribution-marketing

    Auto-Renewable Subscriptions: https://developer.apple.com/design/human-interface-guidelines/in-app-purchase/overview/auto-renewable-subscriptions/

    Architecting for subscriptions: https://developer.apple.com/videos/play/wwdc2020/10671/

    Download full source code

    Spread the love