Advertisement
lcolli98

Untitled

Mar 25th, 2020
761
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Swift 12.19 KB | None | 0 0
  1. //
  2. //  AllCoursesViewController.swift
  3. //  AeroBuddy
  4. //
  5. //  Created by Luke Collister on 01/09/2019.
  6. //  Copyright © 2019 Luke Collister. All rights reserved.
  7. //
  8.  
  9. import UIKit
  10. import StoreKit
  11.  
  12. class AllCoursesViewController: UITableViewController, SKProductsRequestDelegate, SKPaymentTransactionObserver, CourseCellDelegate {
  13.  
  14.     // MARK: - Variable Declarations
  15.     var tempData = [Course]()
  16.  
  17.     var allCourses = [Course]()
  18.     var ownedCourses = [Course]()
  19.     var displayCourses = [Course]()
  20.     var products = [String: SKProduct]()
  21.     var mycourse: Course!
  22.    
  23.     var hasFinishedLoading = true
  24.    
  25.     let workingDG = DispatchGroup()
  26.    
  27.     var productsSet = Set<String>()
  28.  
  29.    
  30.     // MARK: - IAP Methods
  31.     var product_identifier: String?
  32.    
  33.     func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
  34.         for validProduct in response.products{
  35.            
  36.             products[validProduct.productIdentifier] = validProduct
  37.            
  38.             for course in displayCourses {
  39.                 if( course.product_identifier! == validProduct.productIdentifier ){
  40.                     course.price = validProduct.regularPrice
  41.                 }
  42.             }
  43.         }
  44.         DispatchQueue.main.async {
  45.             self.tableView.reloadData()
  46.         }
  47.     }
  48.    
  49.     func buyProduct(product: SKProduct) {
  50.         let payment = SKPayment(product: product)
  51.         SKPaymentQueue.default().add(payment)
  52.     }
  53.  
  54.     func request(_ request: SKRequest, didFailWithError error: Error) {
  55.         print("Error Fetching product information")
  56.     }
  57.  
  58.     func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
  59.        
  60.         for transaction: AnyObject in transactions {
  61.             if let trans:SKPaymentTransaction = transaction as? SKPaymentTransaction {
  62.                 switch trans.transactionState {
  63.                 case .purchased:
  64.                     SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
  65.                     // Handle the purchase
  66.                     UserDefaults.standard.set(true , forKey: "purchased")
  67.                     // Get the receipt if it's available
  68.                     if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL, FileManager.default.fileExists(atPath: appStoreReceiptURL.path) {
  69.  
  70.                         do {
  71.                             let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped)
  72.  
  73.                             let receiptString = receiptData.base64EncodedString(options: [])
  74.  
  75.                             // Read receiptData
  76.                             // Create POST request
  77.                             let url = URL(string: "http://fe01.kilosierracharlie.me/verifypayment")!
  78.                             var request = URLRequest(url: url)
  79.                             request.httpMethod = "POST"
  80.                             request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
  81.                             let parameters: [String: Any] = [
  82.                                 "receipt": receiptString
  83.                             ]
  84.                             request.httpBody = parameters.percentEncoded()
  85.  
  86.                             // Send the data
  87.                             let task = URLSession.shared.dataTask(with: request) { data, response, error in
  88.                                 guard let data = data, error == nil else {
  89.                                     print(error?.localizedDescription ?? "No data")
  90.                                     print("No data could be sent to the server. Please contact us.")
  91.                                     return
  92.                                 }
  93.                                 let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
  94.                                 if (responseJSON as? [String: Any]) != nil {
  95.                                     //TO DO: Refresh if error false, else throw error
  96.                                 }
  97.                             }
  98.                             task.resume()
  99.                         }
  100.                         catch {
  101.                             print("Couldn't read receipt data with error: " + error.localizedDescription)
  102.                         }
  103.                     }
  104.                     break;
  105.                    
  106.                 case .failed:
  107.                     print("Purchased Failed")
  108.                     SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
  109.                     print( transaction.error.debugDescription )
  110.                     break;
  111.  
  112.                 case .restored:
  113.                     SKPaymentQueue.default().restoreCompletedTransactions()
  114.  
  115.                     // Handle the purchase
  116.                     UserDefaults.standard.set(true , forKey: "purchased")
  117.                     //adView.hidden = true
  118.                     break;
  119.                 default:
  120.                     break;
  121.                 }
  122.             }
  123.         }
  124.     }
  125.    
  126.     @objc func userActReload(){
  127.        
  128.         if( hasFinishedLoading ){
  129.            
  130.             hasFinishedLoading = false
  131.            
  132.             displayCourses.removeAll()
  133.             ownedCourses.removeAll()
  134.             allCourses.removeAll()
  135.             self.tableView.reloadData()
  136.            
  137.            
  138.             let activityIndicator = MyIndicator(frame: CGRect(x: UIScreen.main.bounds.size.width/2 - 75, y: UIScreen.main.bounds.size.height/2 - 150, width: 150, height: 150), image: UIImage(named: "LOADING.png")!)
  139.             view.addSubview(activityIndicator)
  140.             activityIndicator.startAnimating()
  141.            
  142.             DispatchQueue.global(qos: .background).async {
  143.                 APIHandler.load( endpoint: .AllCourses, completion: {( data ) in
  144.                     for e in data {
  145.                         let tc : Course = e as! Course
  146.                         self.productsSet.insert( tc.product_identifier! )
  147.                         forOwned:
  148.                         for sub in tc.subjects! {
  149.                             if (sub.owned ?? false) {
  150.                                 tc.partOwned = true
  151.                                 break
  152.                             }
  153.                             for top in sub.topics! {
  154.                                 if (top.owned ?? false) {
  155.                                     tc.partOwned = true
  156.                                     break forOwned
  157.                                 }
  158.                             }
  159.                         }
  160.                         self.displayCourses.append( tc )
  161.                     }
  162.                    
  163.                     APIHandler.load( endpoint: .OwnedCourses, completion: {( data ) in
  164.                         for e in data {
  165.                             let tc : Course = e as! Course
  166.                             for o in self.displayCourses {
  167.                                 if o.id == tc.id {
  168.                                     o.owned = true
  169.                                 }
  170.                             }
  171.                         }
  172.  
  173.                         let productsRequest: SKProductsRequest = SKProductsRequest(productIdentifiers: self.productsSet)
  174.                         productsRequest.delegate = self
  175.                         productsRequest.start()
  176.                        
  177.                         DispatchQueue.main.async {
  178.                             self.tableView.reloadData()
  179.                             activityIndicator.stopAnimating()
  180.                             self.hasFinishedLoading = true
  181.                         }
  182.                     })
  183.                 })
  184.             }
  185.         }
  186.     }
  187.    
  188.     // MARK: - Default Methods
  189.     override func viewDidLoad() {
  190.         super.viewDidLoad()
  191.                
  192.         // Provide an empty backBarButton to hide the 'Back' text present by default in the back button.
  193.         self.navigationItem.setHidesBackButton(true, animated:true)
  194.        
  195.         // Add a right navigation bar button to allow refreshing of JSON
  196.         navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "arrow.clockwise"), style: .plain, target: self, action: #selector( userActReload ) )
  197.        
  198.         // IAP Code
  199.         SKPaymentQueue.default().add(self)
  200.            
  201.         userActReload()
  202.     }
  203.  
  204.     // MARK: - UITableViewMethods
  205.     override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  206.         return self.displayCourses.count
  207.     }
  208.    
  209.    
  210.     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  211.         let cell = tableView.dequeueReusableCell(withIdentifier: "CourseCell", for: indexPath) as! CourseCell
  212.         cell.course = displayCourses[indexPath.row]
  213.         if (cell.course?.owned == false) {
  214.             cell.backgroundColor = UIColor(red: 90/255, green: 90/255, blue: 90/255, alpha: 1)
  215.         } else {
  216.             //cell.backgroundColor = default
  217.         }
  218.         cell.delegate = self
  219.         return cell
  220.     }
  221.  
  222.  
  223.     // When the cell is pressed
  224.     override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  225.         // Determine which Course was selected
  226.         let selectedCourse = displayCourses[indexPath.row]
  227.         mycourse = selectedCourse
  228.        
  229.         // Load the subjects for this course
  230.         performSegue(withIdentifier: "courseToSubjectSegue", sender: self)
  231.     }
  232.    
  233.    
  234.     func cellButtonTapped(cell: CourseCell) {
  235.         // Trigger an alert to buy the course
  236.         let alert = UIAlertController(title: "Purchase Course?", message: "You do not own this course. Do you wish to purchase it? Pressing \"Yes\" will purchase *all* subjects and topics within the course.", preferredStyle: .alert)
  237.  
  238.         // Add the course product identifier to the set
  239.         let buttonPosition: CGPoint = cell.convert(CGPoint.zero, to: self.tableView)
  240.         let indexPath = self.tableView.indexPathForRow(at: buttonPosition)
  241.  
  242.         // Yes option
  243.         alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { action in
  244.            // IAP Code Here
  245.            if (SKPaymentQueue.canMakePayments()) {
  246.                 self.buyProduct(product: self.products[ self.displayCourses[indexPath!.row].product_identifier! ]! )
  247.            } else {
  248.                print("Can't make purchases")
  249.            }
  250.         }))
  251.         // No option
  252.         alert.addAction(UIAlertAction(title: "No", style: .cancel, handler: { action in
  253.             self.productsSet.removeAll()
  254.         }))
  255.  
  256.         self.present(alert, animated: true)
  257.     }
  258.    
  259.    
  260.     // MARK: - Segue Methods
  261.     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
  262.         if segue.identifier == "courseToSubjectSegue" {
  263.             if let destinationVC = segue.destination as? SubjectsViewController {
  264.                 destinationVC.courseVar = mycourse
  265.             }
  266.         }
  267.     }
  268. }
  269.  
  270.  
  271. // MARK: - Extensions
  272. extension Dictionary {
  273.     func percentEncoded() -> Data? {
  274.         return map { key, value in
  275.             let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
  276.             let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
  277.             return escapedKey + "=" + escapedValue
  278.         }
  279.         .joined(separator: "&")
  280.         .data(using: .utf8)
  281.     }
  282. }
  283.  
  284. extension CharacterSet {
  285.     static let urlQueryValueAllowed: CharacterSet = {
  286.         let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
  287.         let subDelimitersToEncode = "!$&'()*+,;="
  288.  
  289.         var allowed = CharacterSet.urlQueryAllowed
  290.         allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
  291.         return allowed
  292.     }()
  293. }
  294.  
  295. extension SKProduct {
  296.  
  297.     /// - returns: The cost of the product formatted in the local currency.
  298.     var regularPrice: String? {
  299.         let formatter = NumberFormatter()
  300.         formatter.numberStyle = .currency
  301.         formatter.locale = self.priceLocale
  302.         return formatter.string(from: self.price)
  303.     }
  304.  
  305. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement