2017-11-24 1 views
0

저는 현재 Swift 기반 iOS 앱에서 인앱 구매를 제공하고 있습니다. 나는 무거운 짐을하는 스토어 클래스를 가지고 있고 저장소 뷰를 관리하는 StoreViewController를 가지고있다. 현재 구매 버튼을 누르면 응답 시간이 매우 오래 걸립니다 (최소 10 초 이상). 즉, 버튼을 반복해서 누르거나 구매를 포기할 수 있습니다. 버튼을 누르면 버튼이 비활성화되고 구입 프로세스가 계속 될 준비가 될 때까지 회 전자 또는 유사한 것을 표시하도록 변경하려고합니다. 내가 이미 가지고있는 코드에서이 작업을 수행하는 데 선호되는 방법/장소는 무엇입니까?앱 스토어가 인앱 구매로 응답하기를 기다리는 동안 '로딩 스피너'또는 유사한 방법을 실행하는 방법은 무엇입니까?

스토어 클래스 :

import Foundation 
import StoreKit 

protocol ClassStoreDelegate: class { 
    func storeUpdateReceived(store: Store) 
} 

class Store: NSObject, SKProductsRequestDelegate, SKPaymentTransactionObserver { 
    // Properties 
    weak var delegate: ClassStoreDelegate? 
    var list = [SKProduct]() 
    var p = SKProduct() 
    var localPrice: String? 
    var presentAlert = false 
    var alertTitle = "Alert" 
    var alertMessage = "Unfortunately something went wrong" 
    var purchaseSuccess = false 

    // Methods 
    // Calling the delegate method 
    func storeUpdate() { 
     delegate?.storeUpdateReceived(store: self) 
    } 

    // Buy the product 
    func buy(productId: String) { 
     var foundProduct = false 
     var counter = 0 
     while counter < list.count && !foundProduct { 
      if (list[counter].productIdentifier == productId){ 
       p = list[counter] 
       foundProduct = true 
      } 
      counter = counter + 1 
     } 
     buyProduct() 
    } 

    func buyProduct() { 
     let pay = SKPayment(product: p) 
     SKPaymentQueue.default().add(self) 
     SKPaymentQueue.default().add(pay as SKPayment) 
    } 

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { 
     for transaction: AnyObject in transactions { 
      let trans = transaction as! SKPaymentTransaction 
      switch trans.transactionState { 
      case .purchased: 
       let prodID = p.productIdentifier 
       switch prodID { 
       case "volume2": 
        VolumeTableViewController.saveUnlocked(volumeUnlocked: 1) 
        purchaseSuccess = true 
        delegate?.storeUpdateReceived(store: self) 
       case "volume3": 
        VolumeTableViewController.saveUnlocked(volumeUnlocked: 2) 
        purchaseSuccess = true 
        delegate?.storeUpdateReceived(store: self) 
       case "volume4": 
        VolumeTableViewController.saveUnlocked(volumeUnlocked: 3) 
        purchaseSuccess = true 
        delegate?.storeUpdateReceived(store: self) 
       case "volume5": 
        VolumeTableViewController.saveUnlocked(volumeUnlocked: 4) 
        purchaseSuccess = true 
        delegate?.storeUpdateReceived(store: self) 
       case "all": 
        for i in 0...VolumeTableViewController.unlocked.count-1 { 
         VolumeTableViewController.saveUnlocked(volumeUnlocked: i) 
        } 
        purchaseSuccess = true 
        delegate?.storeUpdateReceived(store: self) 
       default: 
        delegate?.storeUpdateReceived(store: self) 
       } 
       queue.finishTransaction(trans) 
       break 
      case .failed: 
       alertTitle = "Something went wrong" 
       alertMessage = "Unfortunately the purchase failed" 
       presentAlert = true 
       delegate?.storeUpdateReceived(store: self) 
       queue.finishTransaction(trans) 
       break 
      case .restored: 
       SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction) 
       // Try to pop back after successful restore 
       purchaseSuccess = true 
       delegate?.storeUpdateReceived(store: self) 
       queue.finishTransaction(trans) 
       break 
      default: 
       break 
      } 
     } 
    } 
    // Restore products 
    func restore() { 
     SKPaymentQueue.default().add(self) 
     SKPaymentQueue.default().restoreCompletedTransactions() 
    } 

    func getProducts() { 
     if(SKPaymentQueue.canMakePayments()) { 
      let productID: NSSet = NSSet(objects: "volume2", "volume3", "volume4", "volume5", "all") 
      let request: SKProductsRequest = SKProductsRequest(productIdentifiers: productID as! Set<String>) 
      request.delegate = self 
      request.start() 
     } else { 
      alertTitle = "Something went wrong" 
      alertMessage = "Your phone does not appear to be set up for making purchases" 
      presentAlert = true 
      delegate?.storeUpdateReceived(store: self) 
     } 
    } 

    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { 
     let myProduct = response.products 
     for product in myProduct { 
      list.append(product) 
     } 
     delegate?.storeUpdateReceived(store: self) 
    } 

    func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { 
     let transactionsArray = queue.transactions 
     if (transactionsArray.isEmpty) { 
      alertTitle = "Something went wrong" 
      alertMessage = "We weren't able to find any previous purchases for your account" 
      presentAlert = true 
      delegate?.storeUpdateReceived(store: self) 
     } 
     else { 
      for transaction in transactionsArray { 
       let t: SKPaymentTransaction = transaction 
       let prodID = t.payment.productIdentifier as String 
       switch prodID { 
       case "volume2": 
        VolumeTableViewController.saveUnlocked(volumeUnlocked: 1) 
        delegate?.storeUpdateReceived(store: self) 
       case "volume3": 
        VolumeTableViewController.saveUnlocked(volumeUnlocked: 2) 
        delegate?.storeUpdateReceived(store: self) 
       case "volume4": 
        VolumeTableViewController.saveUnlocked(volumeUnlocked: 3) 
        delegate?.storeUpdateReceived(store: self) 
       case "volume5": 
        VolumeTableViewController.saveUnlocked(volumeUnlocked: 4) 
        delegate?.storeUpdateReceived(store: self) 
       case "all": 
        for i in 0...VolumeTableViewController.unlocked.count-1 { 
         VolumeTableViewController.saveUnlocked(volumeUnlocked: i) 
        } 
        delegate?.storeUpdateReceived(store: self) 
       default: 
        alertTitle = "Something went wrong" 
        alertMessage = "We weren't able to find the correct product" 
        presentAlert = true 
        delegate?.storeUpdateReceived(store: self) 
       } 
      } 
     } 
    } 
    // Format the price and display 
    func formatPrice(price: NSDecimalNumber) -> String { 
     let formatter = NumberFormatter() 
     formatter.locale = Locale.current 
     formatter.numberStyle = .currency 
     if let formattedPrice = formatter.string(from: price){ 
      localPrice = (" \(formattedPrice)") 
     } 
     return localPrice! 
    } 
} 

StoreViewController 클래스 :

let store = Store() // Global store instance 

import UIKit 

class StoreViewController: UIViewController, ClassStoreDelegate { 
    // Properties 
    /* let alert = UIAlertController(title: "Something went wrong", message: 
    "Unfortunately, something went wrong with your request", preferredStyle: UIAlertControllerStyle.alert) */ 
    // Actions 
    @IBAction func btn2(_ sender: UIButton) { 
     store.buy(productId: store.list[0].productIdentifier) 
    } 

    @IBAction func btn3(_ sender: UIButton) { 
     store.buy(productId: store.list[1].productIdentifier) 
    } 

    @IBAction func btn4(_ sender: UIButton) { 
     store.buy(productId: store.list[2].productIdentifier) 
    } 

    @IBAction func btn5(_ sender: UIButton) { 
     store.buy(productId: store.list[3].productIdentifier) 
    } 

    @IBAction func btnAll(_ sender: UIButton) { 
     store.buy(productId: store.list[4].productIdentifier) 
    } 

    @IBAction func btnRestore(_ sender: UIButton) { 
     store.restore() 
    } 
    // Outlets 
    @IBOutlet weak var btn2: UIButton! 
    @IBOutlet weak var btn3: UIButton! 
    @IBOutlet weak var btn4: UIButton! 
    @IBOutlet weak var btn5: UIButton! 
    @IBOutlet weak var btnAll: UIButton! 
    @IBOutlet weak var btnRestore: UIButton! 

    override func viewDidLoad() { 
     super.viewDidLoad() 
     // Disable buttons until prices loaded 
     btn2.isEnabled = false 
     btn3.isEnabled = false 
     btn4.isEnabled = false 
     btn5.isEnabled = false 
     btnAll.isEnabled = false 
     btnRestore.isEnabled = false 

     store.delegate = self // bind the delegate like this? 
     //  alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default)) // Set up the action for the alert button 

     // Get list of products for the store 
     self.navigationItem.title = "Store" 
     // update once the list of products is got from the store object 
     store.getProducts() 
    } 

    // Running the delegate update 
    func storeUpdateReceived(store: Store) { 
     if ((store.list.count) > 0) { 
         btnRestore.setTitle("Restore", for: .normal) 

      if store.list[0].productIdentifier == "all" { 
           btn2.setTitle(store.list[0].localizedTitle + " " + store.formatPrice(price: store.list[0].price), for: .normal) 
      } 
      if store.list[1].productIdentifier == "volume2" { 
       if VolumeTableViewController.unlocked[1] { 
        btn3.isHidden = true 
       } else { 
        btn3.setTitle(store.list[1].localizedTitle + " " + store.formatPrice(price: store.list[1].price), for: .normal) 
       } 
      } 
         if store.list[2].productIdentifier == "volume3" { 
       if VolumeTableViewController.unlocked[2] { 
        btn4.isHidden = true 
       } else { 
        btn4.setTitle(store.list[2].localizedTitle + " " + store.formatPrice(price: store.list[2].price), for: .normal) 
       } 
      } 
      if store.list[3].productIdentifier == "volume4" { 
       if VolumeTableViewController.unlocked[3] { 
        btn5.isHidden = true 
       } else { 
        btn5.setTitle(store.list[3].localizedTitle + " " + store.formatPrice(price: store.list[3].price), for: .normal) 
       } 
      } 
      if store.list[4].productIdentifier == "volume5" { 
       if VolumeTableViewController.unlocked[4] { 
        btnAll.isHidden = true 
       } else { 
        btnAll.setTitle(store.list[4].localizedTitle + " " + store.formatPrice(price: store.list[4].price), for: .normal) 
       } 
      } 

      // Now enable the buttons 
      btn2.isEnabled = true 
      btn3.isEnabled = true 
      btn4.isEnabled = true 
      btn5.isEnabled = true 
      btnAll.isEnabled = true 
      btnRestore.isEnabled = true 
     } 
     if store.purchaseSuccess { 
     performSegueToReturnBack() 
      store.purchaseSuccess = false 
     } 
    } 

    // method to go back when complete 
    func performSegueToReturnBack() { 
     if let nav = self.navigationController { 
      nav.popViewController(animated: true) 
     } else { 
      self.dismiss(animated: true, completion: nil) 
     } 
    } 

    override func didReceiveMemoryWarning() { 
     super.didReceiveMemoryWarning() 
     // Dispose of any resources that can be recreated. 
    } 
} 

답변

2

당신이 버튼을 누를 때 그게

그런 다음 버튼을 비활성화 그래서 그것을 변경하려면 구매 인터페이스를 숨기거나 버튼을 비활성화 시키십시오. 그는 단추를 사다. 구매 프로세스가 당신은 할 수 없습니다

을 계속할 준비가 될 때까지

및 스피너 또는 이와 유사한 표시됩니다. 무슨 일이 일어나는지 알 길이 없습니다. "구매 프로세스"는 앱 외부의 사용자와 런타임 간의 상호 교환입니다. 당신은 무슨 일이 일어나고 있는지 알지 못합니다. 사용자가 구매를 취소하거나 사용자의 비밀번호 나 상점 계정에 문제가있는 경우 발생한 일을 알리는 이벤트는 발생하지 않습니다. 따라서 특별한 "대기"인터페이스를 설정하면 영원히 남을 수 있습니다.

기본적으로 질문의 제목은 깊은 오해를 나타냅니다. 이 아니며 "대기 중"입니다. 앱의 삶을 계속 따라 가십시오. 구매 프로세스는 이며 비동기식이며 앱의 경우 인 경우 입니다. 결제 대기열에서 앱에 문제가 있음을 알리는 경우 응답합니다. 그게 다야.

(Apple's guidance about this 참조하십시오, 그들은 지적으로, 어떤 상황에서 지불 대기열에서 메시지 일 동안 도착하지 않을 수 있습니다 당신은 그것을 위해 "대기"아주 바보 보이는 것!.)

그래서 올바른 절차는 우리가 먼저 말했던 것. 사용자가 구매를 사용하거나 구매 인터페이스를 사용 중지하거나 중단하면 그뿐입니다.

+0

내 구매 인터페이스는 제시된보기 컨트롤러 여야합니다. 사용자가 Buy를 누르면 View Controller가 닫히고 제품 지불 대기열에 들어갑니다. 그게 다야. – matt

관련 문제