4

I'm adding iOS inapp subscription to my app. I'm following this great tutorial from Ray Wenderlich page:

https://www.raywenderlich.com/659-in-app-purchases-auto-renewable-subscriptions-tutorial

Basically the tutorial follow these steps:

  1. purchase subscription
  2. receive the receipt
  3. verificate the receipt in Apple (this should be done via backend but for simplicity the tutorial does it in the app).
  4. once the receipt has been verificated Apple responds with a JSON parsed receipt.
  5. the app stores locally the purchased items and associates them with a sessionID (a UUID created).

After completing the tutorial it seems that everything is working well but I'm having difficulties with 2 issues.

Each time the user enters in the app, this checks if it has a valid sessionID and a valid receipt:

guard SubscriptionService.shared.currentSessionId != nil,
    SubscriptionService.shared.hasReceiptData else {
    showRestoreAlert()
    return
  }
  loadSelfies()

hasReceiptData does this:

  var hasReceiptData: Bool {
    return loadReceipt() != nil
  }

  private func loadReceipt() -> Data? {
    guard let url = Bundle.main.appStoreReceiptURL else {
      return nil
    }

    do {
      let data = try Data(contentsOf: url)
      return data
    } catch {
      print("Error loading receipt data: \(error.localizedDescription)")
      return nil
    }
  }
}

These methods only check if the receipt exists, nothing else.

To check the currentSessionId this is the method:

  var currentSessionId: String? {
    didSet {
      NotificationCenter.default.post(name: SubscriptionService.sessionIdSetNotification, object: currentSessionId)
    }
  }

And the value of currentSessionId is set when Apple responds the receipt verification:

  func uploadReceipt(completion: ((_ success: Bool) -> Void)? = nil) {
    if let receiptData = loadReceipt() {
      SelfieService.shared.upload(receipt: receiptData) { [weak self] (result) in
        guard let strongSelf = self else { return }
        switch result {
        case .success(let result):
          strongSelf.currentSessionId = result.sessionId
          strongSelf.currentSubscription = result.currentSubscription
          completion?(true)
        case .failure(let error):
          print(" Receipt Upload Failed: \(error)")
          completion?(false)
        }
      }
    }
  }

Well, my doubts are these:

  1. Each time the user enters in the app it only checks if currentSessionId and the receipt exists, nothing else, so if they exist but the subscription is expired the app doesn't know it and the user keeps using it without any active subscription. What should I do to avoid this case? Store locally in UserDefaults the parsed JSON receipt and check the expiration date?

  2. And since currentSessionId is not stored permanently, if I kill the app and relaunch it again, currentSessionId is null, the first check fails and the user cannot use the app. What is the best way to manage this? Again store locally in UserDefaults the currentSessionID?

EDIT

Looking at some similar questions, it seems that each time the user enters in the app, before checking for the currentSessionID and the receipt I have to validate the locally stored receipt, is this right?

Wonton
  • 1,033
  • 16
  • 33

0 Answers0