Untitled

mail@pastecode.io avatar
unknown
swift
a year ago
3.9 kB
4
Indexable
Never
final class PaywallManager: NSObject {
  static let shared = PaywallManager()

  var isSubscribed: Bool {
    switch Superwall.shared.subscriptionStatus {
    case .active:
      return true
    default:
      return false
    }
  }

  /// Configures both the RevenueCat and Superwall SDKs.
  ///
  /// Call this on `application(_:didFinishLaunchingWithOptions:)`
  static func configure() {
    Superwall.configure(
      apiKey: Constants.superwallApiKey,
      purchaseController: shared
    )

    Purchases.configure(withAPIKey: Constants.revenueCatApiKey)

    Superwall.shared.logLevel = .error
    Purchases.logLevel = .error

    syncSubscriptionStatus()
  }

  /// Handles a deep link to open a paywall preview.
  ///
  /// [See here](https://docs.superwall.com/v3.0/docs/in-app-paywall-previews#handling-deep-links)
  /// for information on how to call this function in your app.
  static func handleDeepLink(_ url: URL) {
    Superwall.shared.handleDeepLink(url)
  }

  /// Updates the subscription status in response to customer info received from RevenueCat.
  private func updateSubscriptionStatus(using customerInfo: CustomerInfo) {
    if customerInfo.entitlements.active.isEmpty {
      #if DEBUG
        Superwall.shared.subscriptionStatus = .active
      #else
        Superwall.shared.subscriptionStatus = .inactive
      #endif

    } else {
      Superwall.shared.subscriptionStatus = .active
    }
  }

  // MARK: Sync Subscription Status

  /// Makes sure that Superwall knows the customers subscription status by
  /// changing `Superwall.shared.subscriptionStatus`
  static func syncSubscriptionStatus() {
    assert(Purchases.isConfigured, "You must configure RevenueCat before calling this method.")
    Task {
      for await customerInfo in Purchases.shared.customerInfoStream {
        // Gets called whenever new CustomerInfo is available
        let hasActiveSubscription = !customerInfo.entitlements.active.isEmpty // Why? -> https://www.revenuecat.com/docs/entitlements#entitlements
        if hasActiveSubscription {
          Superwall.shared.subscriptionStatus = .active
        } else {
          #if DEBUG
            Superwall.shared.subscriptionStatus = .active
          #else
            Superwall.shared.subscriptionStatus = .inactive
          #endif
        }
      }
    }
  }
}

// MARK: - PurchaseController

extension PaywallManager: PurchaseController {
  // MARK: Handle Restores

  /// Makes a restore with RevenueCat and returns `.restored`, unless an error is thrown.
  /// This gets called when someone tries to restore purchases on one of your paywalls.
  func restorePurchases() async -> RestorationResult {
    do {
      _ = try await Purchases.shared.restorePurchases()
      return .restored
    } catch {
      return .failed(error)
    }
  }

  // MARK: Handle Purchases

  /// Makes a purchase with RevenueCat and returns its result. This gets called when
  /// someone tries to purchase a product on one of your paywalls.
  func purchase(product: SKProduct) async -> PurchaseResult {
    do {
      let storeProduct = RevenueCat.StoreProduct(sk1Product: product)
      let revenueCatResult = try await Purchases.shared.purchase(product: storeProduct)
      if revenueCatResult.userCancelled {
        return .cancelled
      } else {
        return .purchased
      }
    } catch let error as ErrorCode {
      if error == .paymentPendingError {
        return .pending
      } else {
        return .failed(error)
      }
    } catch {
      return .failed(error)
    }
  }

  func purchases(_ purchases: Purchases,
                 readyForPromotedProduct product: RevenueCat.StoreProduct,
                 purchase startPurchase: @escaping StartPurchaseBlock)
  {
    startPurchase { _, info, error, cancelled in
      if let info = info, error == nil, !cancelled {
        self.updateSubscriptionStatus(using: info)
      }
    }
  }
}