Skip to content

Commit

Permalink
remove remaining fatalErrors (#529)
Browse files Browse the repository at this point in the history
* replaced fatalError in dateExtensions with custom error

* replaced fatalError in Transactions initializer with failable initializer

* replaced fatalErrors in ProductsManager with logging and early returns

* swiftlint

* notValidDateFromComponents -> invalidDateComponents

* added the actual date components to the error message when throwing invalidDateComponents error
  • Loading branch information
aboedo authored Jun 16, 2021
1 parent 63e06bf commit c92487d
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 18 deletions.
19 changes: 17 additions & 2 deletions PurchasesCoreSwift/Misc/DateExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,23 @@

import Foundation

enum DateExtensionsError: Error {
case invalidDateComponents(_ dateComponents: DateComponents)
}

extension DateExtensionsError: CustomStringConvertible {
public var description: String {
switch self {
case .invalidDateComponents(let dateComponents):
return "invalid date components: \(dateComponents.description)"
}
}
}

extension Date {

// swiftlint:disable:next function_parameter_count
static func from(year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int) -> Date {
static func from(year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int) throws -> Date {
let calendar = Calendar(identifier: .gregorian)
var dateComponents = DateComponents()
dateComponents.year = year
Expand All @@ -17,7 +30,9 @@ extension Date {
dateComponents.hour = hour
dateComponents.minute = minute
dateComponents.second = second
guard let date = calendar.date(from: dateComponents) else { fatalError() }
guard let date = calendar.date(from: dateComponents) else {
throw DateExtensionsError.invalidDateComponents(dateComponents)
}
return date
}
}
17 changes: 13 additions & 4 deletions PurchasesCoreSwift/Purchasing/ProductsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,15 @@ extension ProductsManager: SKProductsRequestDelegate {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
queue.async { [self] in
Logger.rcSuccess(Strings.network.skproductsrequest_received_response)
guard let requestProducts = self.productsByRequests[request] else { fatalError("couldn't find request") }
guard let requestProducts = self.productsByRequests[request] else {
Logger.error("requested products not found for request: \(request)")
return
}
guard let completionBlocks = self.completionHandlers[requestProducts] else {
fatalError("couldn't find completion")
Logger.error("callback not found for failing request: \(request)")
return
}

self.completionHandlers.removeValue(forKey: requestProducts)
self.productsByRequests.removeValue(forKey: request)

Expand All @@ -76,9 +81,13 @@ extension ProductsManager: SKProductsRequestDelegate {
func request(_ request: SKRequest, didFailWithError error: Error) {
queue.async { [self] in
Logger.appleError(String(format: Strings.network.skproductsrequest_failed, error.localizedDescription))
guard let products = self.productsByRequests[request] else { fatalError("couldn't find request") }
guard let products = self.productsByRequests[request] else {
Logger.error("requested products not found for request: \(request)")
return
}
guard let completionBlocks = self.completionHandlers[products] else {
fatalError("couldn't find completion")
Logger.error("callback not found for failing request: \(request)")
return
}

self.completionHandlers.removeValue(forKey: products)
Expand Down
2 changes: 1 addition & 1 deletion PurchasesCoreSwift/Purchasing/TransactionsFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Foundation
subscriptionsData.flatMap { (productId: String, transactionData: [[String: Any]]) -> [Transaction] in
transactionData.map {
Transaction(with: $0, productId: productId, dateFormatter: dateFormatter)
}
}.compactMap { $0 }
}.sorted {
$0.purchaseDate < $1.purchaseDate
}
Expand Down
9 changes: 4 additions & 5 deletions PurchasesCoreSwift/Transaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@ import Foundation
super.init()
}

init(with serverResponse: [String: Any], productId: String, dateFormatter: DateFormatter) {
init?(with serverResponse: [String: Any], productId: String, dateFormatter: DateFormatter) {
guard let revenueCatId = serverResponse["id"] as? String,
let dateString = serverResponse["purchase_date"] as? String,
let purchaseDate = dateFormatter.date(from: dateString) else {
fatalError("""
Couldn't initialize Transaction from dictionary.
Reason: unexpected format. Dictionary: \(serverResponse).
""")
Logger.error("Couldn't initialize Transaction from dictionary. " +
"Reason: unexpected format. Dictionary: \(serverResponse).")
return nil
}

self.revenueCatId = revenueCatId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class AppleReceiptBuilderTests: XCTestCase {
let bundleId = "com.revenuecat.test"
let applicationVersion = "3.2.1"
let originalApplicationVersion = "1.2.2"
let creationDate = Date.from(year: 2020, month: 3, day: 23, hour: 15, minute: 5, second: 3)
let creationDate = try! Date.from(year: 2020, month: 3, day: 23, hour: 15, minute: 5, second: 3)

override func setUp() {
super.setUp()
Expand Down Expand Up @@ -61,7 +61,7 @@ class AppleReceiptBuilderTests: XCTestCase {
}

func testBuildGetsExpiresDate() {
let expirationDate = Date.from(year: 2020, month: 7, day: 4, hour: 5, minute: 3, second: 2)
let expirationDate = try! Date.from(year: 2020, month: 7, day: 4, hour: 5, minute: 3, second: 2)
let expirationDateContainer =
containerFactory.receiptAttributeContainer(attributeType: ReceiptAttributeType.expirationDate,
expirationDate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ class InAppPurchaseBuilderTests: XCTestCase {
let transactionId = "089230953203"
let originalTransactionId = "089230953101"
let productType = InAppPurchaseProductType.autoRenewableSubscription
let purchaseDate = Date.from(year: 2019, month: 5, day: 3, hour: 1, minute: 55, second: 1)
let originalPurchaseDate = Date.from(year: 2018, month: 6, day: 22, hour: 1, minute: 55, second: 1)
let expiresDate = Date.from(year: 2018, month: 6, day: 22, hour: 1, minute: 55, second: 1)
let cancellationDate = Date.from(year: 2019, month: 7, day: 4, hour: 7, minute: 1, second: 45)
let purchaseDate = try! Date.from(year: 2019, month: 5, day: 3, hour: 1, minute: 55, second: 1)
let originalPurchaseDate = try! Date.from(year: 2018, month: 6, day: 22, hour: 1, minute: 55, second: 1)
let expiresDate = try! Date.from(year: 2018, month: 6, day: 22, hour: 1, minute: 55, second: 1)
let cancellationDate = try! Date.from(year: 2019, month: 7, day: 4, hour: 7, minute: 1, second: 45)
let isInTrialPeriod = false
let isInIntroOfferPeriod = true
let webOrderLineItemId = Int64(897501072)
Expand Down
23 changes: 23 additions & 0 deletions PurchasesCoreSwiftTests/Purchasing/TransactionsFactoryTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,27 @@ class TransactionsFactoryTests: XCTestCase {
expect(list).to(beEmpty())
}

func testBuildsCorrectlyEvenIfSomeTransactionsCantBeBuilt() {
let subscriptionsData = [
"lifetime_access": [
[
"id": "d6c097ba74",
"is_sandbox": true,
"original_purchase_date": "2018-07-11T18:36:20Z",
"purchase_date": "2018-07-11T18:36:20Z",
"store": "app_store"
]
],
"invalid_non_transaction": [
[
"ioasgioaew": 0832
]
]
]

let nonSubscriptionTransactions = transactionsFactory.nonSubscriptionTransactions(withSubscriptionsData: subscriptionsData, dateFormatter: dateFormatter)
expect(nonSubscriptionTransactions.count) == 1
expect(nonSubscriptionTransactions.first!.productId) == "lifetime_access"
}

}

0 comments on commit c92487d

Please sign in to comment.