Skip to content
This repository has been archived by the owner on May 6, 2024. It is now read-only.

Commit

Permalink
chore: handle when course sku details not available on app store (#1822)
Browse files Browse the repository at this point in the history
  • Loading branch information
saeedbashir committed Feb 15, 2024
1 parent 5a1cb81 commit 8773d75
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 36 deletions.
8 changes: 4 additions & 4 deletions Source/CourseDashboardAccessErrorView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Foundation
protocol CourseDashboardAccessErrorViewDelegate: AnyObject {
func findCourseAction()
func upgradeCourseAction(course: OEXCourse, coursePrice: String, price: NSDecimalNumber?, currencyCode: String?, completion: @escaping ((Bool)->()))
func coursePrice(cell: CourseDashboardAccessErrorView, price: String?, elapsedTime: Int)
func coursePrice(cell: CourseDashboardAccessErrorView, price: String?, error: PurchaseError?, elapsedTime: Int)
}

class CourseDashboardAccessErrorView: UIView {
Expand Down Expand Up @@ -224,20 +224,20 @@ class CourseDashboardAccessErrorView: UIView {
let startTime = CFAbsoluteTimeGetCurrent()
DispatchQueue.main.async { [weak self] in
self?.upgradeButton.startShimeringEffect()
PaymentManager.shared.fetchPrroduct(courseSku) { [weak self] product in
PaymentManager.shared.fetchPrroduct(courseSku) { [weak self] product, error in
guard let weakSelf = self else { return }

if let product = product, let coursePrice = product.localizedPrice {
let elapsedTime = CFAbsoluteTimeGetCurrent() - startTime
weakSelf.localizedCoursePrice = coursePrice
weakSelf.price = product.price
weakSelf.currencyCode = product.priceLocale.currencyCode
weakSelf.delegate?.coursePrice(cell: weakSelf, price: coursePrice, elapsedTime: elapsedTime.millisecond)
weakSelf.delegate?.coursePrice(cell: weakSelf, price: coursePrice, error: nil, elapsedTime: elapsedTime.millisecond)
weakSelf.upgradeButton.setPrice(coursePrice)
weakSelf.upgradeButton.stopShimmerEffect()
}
else {
weakSelf.delegate?.coursePrice(cell: weakSelf, price: nil, elapsedTime: 0)
weakSelf.delegate?.coursePrice(cell: weakSelf, price: nil, error: error, elapsedTime: 0)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Source/EnrolledCoursesViewController+CourseUpgrade.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ extension EnrolledCoursesViewController {

guard let courseSku = course.sku, environment.serverConfig.iapConfig?.enabledforUser == true else { return }

PaymentManager.shared.fetchPrroduct(courseSku) { [weak self] product in
PaymentManager.shared.fetchPrroduct(courseSku) { [weak self] product, _ in
if let product = product {
self?.upgradeCourse(course: course, localizedCoursePrice: product.localizedPrice, price: product.price, currencyCode: product.priceLocale.currencyCode)
}
Expand Down
22 changes: 12 additions & 10 deletions Source/NewCourseDashboardViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -428,12 +428,12 @@ extension NewCourseDashboardViewController: CourseDashboardAccessErrorViewDelega
redirectToDiscovery()
}

func coursePrice(cell: CourseDashboardAccessErrorView, price: String?, elapsedTime: Int) {
func coursePrice(cell: CourseDashboardAccessErrorView, price: String?, error: PurchaseError?, elapsedTime: Int) {
if let price = price {
trackPriceLoadDuration(price: price, elapsedTime: elapsedTime)
}
else {
trackPriceLoadError(cell: cell)
trackPriceLoadError(cell: cell, error: error)
}
}

Expand Down Expand Up @@ -500,24 +500,26 @@ extension NewCourseDashboardViewController {
environment.analytics.trackCourseUpgradeTimeToLoadPrice(courseID: courseID, pacing: pacing, coursePrice: price, screen: screen, elapsedTime: elapsedTime)
}

private func trackPriceLoadError(cell: CourseDashboardAccessErrorView) {
private func trackPriceLoadError(cell: CourseDashboardAccessErrorView, error: PurchaseError?) {
guard let course = course, let courseID = course.course_id else { return }
environment.analytics.trackCourseUpgradeLoadError(courseID: courseID, pacing: pacing, screen: screen)
showCoursePriceErrorAlert(cell: cell)
showCoursePriceErrorAlert(cell: cell, error: error)
}

private func showCoursePriceErrorAlert(cell: CourseDashboardAccessErrorView) {
private func showCoursePriceErrorAlert(cell: CourseDashboardAccessErrorView, error: PurchaseError?) {
guard let topController = UIApplication.shared.topMostController() else { return }

let alertController = UIAlertController().showAlert(withTitle: Strings.CourseUpgrade.FailureAlert.alertTitle, message: Strings.CourseUpgrade.FailureAlert.priceFetchErrorMessage, cancelButtonTitle: nil, onViewController: topController) { _, _, _ in }


alertController.addButton(withTitle: Strings.CourseUpgrade.FailureAlert.priceFetchError) { [weak self] _ in
cell.fetchCoursePrice()
self?.environment.analytics.trackCourseUpgradeErrorAction(courseID: self?.course?.course_id ?? "" , blockID: "", pacing: self?.pacing ?? "", coursePrice: "", screen: self?.screen ?? .none, errorAction: CourseUpgradeHelper.ErrorAction.reloadPrice.rawValue, upgradeError: "price", flowType: CourseUpgradeHandler.CourseUpgradeMode.userInitiated.rawValue)
if error != .productNotExist {
alertController.addButton(withTitle: Strings.CourseUpgrade.FailureAlert.priceFetchError) { [weak self] _ in
cell.fetchCoursePrice()
self?.environment.analytics.trackCourseUpgradeErrorAction(courseID: self?.course?.course_id ?? "" , blockID: "", pacing: self?.pacing ?? "", coursePrice: "", screen: self?.screen ?? .none, errorAction: CourseUpgradeHelper.ErrorAction.reloadPrice.rawValue, upgradeError: "price", flowType: CourseUpgradeHandler.CourseUpgradeMode.userInitiated.rawValue)
}
}

alertController.addButton(withTitle: Strings.cancel, style: .default) { [weak self] _ in
let cancelButtonTitle = error == .productNotExist ? Strings.ok : Strings.cancel
alertController.addButton(withTitle: cancelButtonTitle, style: .default) { [weak self] _ in
cell.hideUpgradeButton()
self?.environment.analytics.trackCourseUpgradeErrorAction(courseID: self?.course?.course_id ?? "" , blockID: "", pacing: self?.pacing ?? "", coursePrice: "", screen: self?.screen ?? .none, errorAction: CourseUpgradeHelper.ErrorAction.close.rawValue, upgradeError: "price", flowType: CourseUpgradeHandler.CourseUpgradeMode.userInitiated.rawValue)
}
Expand Down
9 changes: 5 additions & 4 deletions Source/PaymentManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ enum PurchaseError: String {
case basketError // basket API returns error
case checkoutError // checkout API returns error
case verifyReceiptError // verify receipt API returns error
case productNotExist // product not existed on app appstore
case generalError // general error

var errorString: String {
Expand Down Expand Up @@ -153,16 +154,16 @@ enum PurchaseError: String {
}
}

func fetchPrroduct(_ identifier: String, completion: ((SKProduct?) -> Void)? = nil) {
func fetchPrroduct(_ identifier: String, completion: ((SKProduct?, PurchaseError?) -> Void)? = nil) {
SwiftyStoreKit.retrieveProductsInfo([identifier]) { result in
if let product = result.retrievedProducts.first {
completion?(product)
completion?(product, nil)
}
else if let _ = result.invalidProductIDs.first {
completion?(nil)
completion?(nil, .productNotExist)
}
else {
completion?(nil)
completion?(nil, .generalError)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion Source/ProfileOptionsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,7 @@ extension ProfileOptionsViewController: RestorePurchasesCellDelegate {

guard let courseSku = course.sku, environment.serverConfig.iapConfig?.enabledforUser == true else { return }

PaymentManager.shared.fetchPrroduct(courseSku) { [weak self] product in
PaymentManager.shared.fetchPrroduct(courseSku) { [weak self] product, _ in
if let product = product {
self?.upgradeCourse(course: course, localizedCoursePrice: product.localizedPrice, price: product.price, currencyCode: product.priceLocale.currencyCode, indicator: indicator)
}
Expand Down
18 changes: 10 additions & 8 deletions Source/ValuePropComponentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ class ValuePropComponentView: UIView {

DispatchQueue.main.async { [weak self] in
self?.upgradeButton.startShimeringEffect()
PaymentManager.shared.fetchPrroduct(courseSku) { [weak self] product in
PaymentManager.shared.fetchPrroduct(courseSku) { [weak self] product, error in
if let product = product, let localizedPrice = product.localizedPrice {
let endTime = CFAbsoluteTimeGetCurrent() - startTime
self?.localizedCoursePrice = localizedPrice
Expand All @@ -197,7 +197,7 @@ class ValuePropComponentView: UIView {
self?.upgradeButton.setPrice(localizedPrice)
} else {
self?.trackLoadError()
self?.showCoursePriceErrorAlert()
self?.showCoursePriceErrorAlert(error: error)
}
}
}
Expand Down Expand Up @@ -225,17 +225,19 @@ class ValuePropComponentView: UIView {
environment.analytics.trackCourseUpgradeLoadError(courseID: courseID, blockID: blockID, pacing: pacing, screen: .courseComponent)
}

private func showCoursePriceErrorAlert() {
private func showCoursePriceErrorAlert(error: PurchaseError?) {
guard let topController = UIApplication.shared.topMostController() else { return }

let alertController = UIAlertController().showAlert(withTitle: Strings.CourseUpgrade.FailureAlert.alertTitle, message: Strings.CourseUpgrade.FailureAlert.priceFetchErrorMessage, cancelButtonTitle: nil, onViewController: topController) { _, _, _ in }

alertController.addButton(withTitle: Strings.CourseUpgrade.FailureAlert.priceFetchError) { [weak self] _ in
self?.fetchCoursePrice()
self?.environment.analytics.trackCourseUpgradeErrorAction(courseID: self?.courseID ?? "" , blockID: self?.blockID ?? "", pacing: self?.pacing ?? "", coursePrice: "", screen: .courseComponent, errorAction: CourseUpgradeHelper.ErrorAction.reloadPrice.rawValue, upgradeError: "price", flowType: CourseUpgradeHandler.CourseUpgradeMode.userInitiated.rawValue)
if error != .productNotExist {
alertController.addButton(withTitle: Strings.CourseUpgrade.FailureAlert.priceFetchError) { [weak self] _ in
self?.fetchCoursePrice()
self?.environment.analytics.trackCourseUpgradeErrorAction(courseID: self?.courseID ?? "" , blockID: self?.blockID ?? "", pacing: self?.pacing ?? "", coursePrice: "", screen: .courseComponent, errorAction: CourseUpgradeHelper.ErrorAction.reloadPrice.rawValue, upgradeError: "price", flowType: CourseUpgradeHandler.CourseUpgradeMode.userInitiated.rawValue)
}
}

alertController.addButton(withTitle: Strings.cancel, style: .default) { [weak self] _ in
let cancelButtonTitle = error == .productNotExist ? Strings.ok : Strings.cancel
alertController.addButton(withTitle: cancelButtonTitle, style: .default) { [weak self] _ in
self?.upgradeButton.stopShimmerEffect()
self?.upgradeButton.updateVisibility(visible: false)
self?.environment.analytics.trackCourseUpgradeErrorAction(courseID: self?.courseID ?? "" , blockID: self?.blockID ?? "", pacing: self?.pacing ?? "", coursePrice: "", screen: .courseComponent, errorAction: CourseUpgradeHelper.ErrorAction.close.rawValue, upgradeError: "price", flowType: CourseUpgradeHandler.CourseUpgradeMode.userInitiated.rawValue)
Expand Down
18 changes: 10 additions & 8 deletions Source/ValuePropDetailViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ class ValuePropDetailViewController: UIViewController, InterfaceOrientationOverr

DispatchQueue.main.async { [weak self] in
self?.upgradeButton.startShimeringEffect()
PaymentManager.shared.fetchPrroduct(courseSku) { [weak self] product in
PaymentManager.shared.fetchPrroduct(courseSku) { [weak self] product, error in
if let product = product {
let endTime = CFAbsoluteTimeGetCurrent() - startTime
self?.localizedCoursePrice = product.localizedPrice
Expand All @@ -109,7 +109,7 @@ class ValuePropDetailViewController: UIViewController, InterfaceOrientationOverr
self?.upgradeButton.setPrice(product.localizedPrice ?? "")
} else {
self?.trackLoadError()
self?.showCoursePriceErrorAlert()
self?.showCoursePriceErrorAlert(error: error)
}
}
}
Expand All @@ -135,18 +135,20 @@ class ValuePropDetailViewController: UIViewController, InterfaceOrientationOverr
environment.analytics.trackCourseUpgradeLoadError(courseID: courseID, blockID: blockID, pacing: pacing, screen: screen)
}

private func showCoursePriceErrorAlert() {
private func showCoursePriceErrorAlert(error: PurchaseError?) {
guard let topController = UIApplication.shared.topMostController() else { return }

let alertController = UIAlertController().showAlert(withTitle: Strings.CourseUpgrade.FailureAlert.alertTitle, message: Strings.CourseUpgrade.FailureAlert.priceFetchErrorMessage, cancelButtonTitle: nil, onViewController: topController) { _, _, _ in }


alertController.addButton(withTitle: Strings.CourseUpgrade.FailureAlert.priceFetchError) { [weak self] _ in
self?.fetchCoursePrice()
self?.environment.analytics.trackCourseUpgradeErrorAction(courseID: self?.course.course_id ?? "" , blockID: self?.blockID ?? "", pacing: self?.pacing ?? "", coursePrice: "", screen: self?.screen ?? .none, errorAction: CourseUpgradeHelper.ErrorAction.reloadPrice.rawValue, upgradeError: "price", flowType: CourseUpgradeHandler.CourseUpgradeMode.userInitiated.rawValue)
if error != .productNotExist {
alertController.addButton(withTitle: Strings.CourseUpgrade.FailureAlert.priceFetchError) { [weak self] _ in
self?.fetchCoursePrice()
self?.environment.analytics.trackCourseUpgradeErrorAction(courseID: self?.course.course_id ?? "" , blockID: self?.blockID ?? "", pacing: self?.pacing ?? "", coursePrice: "", screen: self?.screen ?? .none, errorAction: CourseUpgradeHelper.ErrorAction.reloadPrice.rawValue, upgradeError: "price", flowType: CourseUpgradeHandler.CourseUpgradeMode.userInitiated.rawValue)
}
}

alertController.addButton(withTitle: Strings.cancel, style: .default) { [weak self] _ in
let cancelButtonTitle = error == .productNotExist ? Strings.ok : Strings.cancel
alertController.addButton(withTitle: cancelButtonTitle, style: .default) { [weak self] _ in
self?.upgradeButton.stopShimmerEffect()
self?.upgradeButton.isHidden = true
self?.environment.analytics.trackCourseUpgradeErrorAction(courseID: self?.course.course_id ?? "" , blockID: self?.blockID ?? "", pacing: self?.pacing ?? "", coursePrice: "", screen: self?.screen ?? .none, errorAction: CourseUpgradeHelper.ErrorAction.close.rawValue, upgradeError: "price", flowType: CourseUpgradeHandler.CourseUpgradeMode.userInitiated.rawValue)
Expand Down

0 comments on commit 8773d75

Please sign in to comment.