Skip to content
This repository was archived by the owner on Oct 16, 2025. It is now read-only.

Commit b105644

Browse files
authored
Unify errors into OpenIapError (#7)
**Removed** `OpenIapFailure` and unified the error model into `OpenIapError`. All `throw`/`emit` points now use `OpenIapError.make(...)`, with tests and README updated. ⚠️ **Breaking change:** References to `OpenIapFailure` must be replaced with `OpenIapError`. Error handling should branch on the `code` property (e.g., `E_USER_CANCELLED`, `E_TRANSACTION_VALIDATION_FAILED`).
1 parent 8f5bf20 commit b105644

File tree

4 files changed

+58
-161
lines changed

4 files changed

+58
-161
lines changed

README.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -370,15 +370,17 @@ struct DiscountOffer {
370370
OpenIAP provides comprehensive error handling:
371371

372372
```swift
373-
enum OpenIapError: LocalizedError {
374-
case purchaseFailed(reason: String)
375-
case purchaseCancelled
376-
case purchaseDeferred
377-
case productNotFound(productId: String)
378-
case verificationFailed(reason: String)
379-
case storeKitError(error: Error)
380-
case notSupported
373+
// Unified error model
374+
struct OpenIapError: LocalizedError {
375+
let code: String
376+
let message: String
377+
let productId: String?
378+
379+
var errorDescription: String? { message }
381380
}
381+
382+
// Create errors with predefined codes
383+
let error = OpenIapError(code: "E_USER_CANCELLED", message: "User cancelled the purchase")
382384
```
383385

384386
## 🤝 Contributing

Sources/OpenIapError.swift

Lines changed: 3 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,7 @@
11
import Foundation
22

3-
public enum OpenIapFailure: LocalizedError {
4-
case productNotFound(id: String)
5-
case purchaseFailed(reason: String)
6-
case purchaseCancelled
7-
case purchaseDeferred
8-
case paymentNotAllowed
9-
case storeKitError(error: Error)
10-
case invalidReceipt
11-
case networkError
12-
case verificationFailed(reason: String)
13-
case restoreFailed(reason: String)
14-
case unknownError
15-
case notSupported
16-
17-
public var errorDescription: String? {
18-
switch self {
19-
case .productNotFound(let id):
20-
return "Product not found: \(id)"
21-
case .purchaseFailed(let reason):
22-
return "Purchase failed: \(reason)"
23-
case .purchaseCancelled:
24-
return "Purchase cancelled by user"
25-
case .purchaseDeferred:
26-
return "Purchase deferred"
27-
case .paymentNotAllowed:
28-
return "Payment not allowed"
29-
case .storeKitError(let error):
30-
return "StoreKit error: \(error.localizedDescription)"
31-
case .invalidReceipt:
32-
return "Invalid receipt"
33-
case .networkError:
34-
return "Network error occurred"
35-
case .verificationFailed(let reason):
36-
return "Verification failed: \(reason)"
37-
case .restoreFailed(let reason):
38-
return "Restore failed: \(reason)"
39-
case .unknownError:
40-
return "Unknown error occurred"
41-
case .notSupported:
42-
return "Feature not supported on this platform"
43-
}
44-
}
45-
}
46-
473
// MARK: - Unified Error Event + Codes
48-
public struct OpenIapError: Codable, Equatable {
4+
public struct OpenIapError: Codable, Equatable, LocalizedError {
495
public let code: String
506
public let message: String
517
public let productId: String?
@@ -55,6 +11,8 @@ public struct OpenIapError: Codable, Equatable {
5511
self.message = message
5612
self.productId = productId
5713
}
14+
15+
public var errorDescription: String? { message }
5816
}
5917

6018
public extension OpenIapError {
@@ -215,36 +173,6 @@ public extension OpenIapError {
215173
return OpenIapError(code: OpenIapError.E_EMPTY_SKU_LIST, message: "Empty SKU list provided")
216174
}
217175

218-
/// Create from OpenIapError
219-
init(from error: OpenIapFailure, productId: String? = nil) {
220-
switch error {
221-
case .purchaseCancelled:
222-
self.init(code: Self.E_USER_CANCELLED, message: "User cancelled the purchase flow", productId: productId)
223-
case .purchaseDeferred:
224-
self.init(code: Self.E_DEFERRED_PAYMENT, message: "Payment was deferred (pending family approval, etc.)", productId: productId)
225-
case .productNotFound(let id):
226-
self.init(code: Self.E_SKU_NOT_FOUND, message: "SKU not found: \(id)", productId: id)
227-
case .purchaseFailed(let reason):
228-
self.init(code: Self.E_SERVICE_ERROR, message: "Purchase failed: \(reason)", productId: productId)
229-
case .paymentNotAllowed:
230-
self.init(code: Self.E_IAP_NOT_AVAILABLE, message: "In-app purchase not allowed on this device", productId: productId)
231-
case .invalidReceipt:
232-
self.init(code: Self.E_RECEIPT_FAILED, message: "Receipt validation failed", productId: productId)
233-
case .networkError:
234-
self.init(code: Self.E_NETWORK_ERROR, message: "Network connection error", productId: productId)
235-
case .verificationFailed(let reason):
236-
self.init(code: Self.E_TRANSACTION_VALIDATION_FAILED, message: "Transaction validation failed: \(reason)", productId: productId)
237-
case .restoreFailed(let reason):
238-
self.init(code: Self.E_SERVICE_ERROR, message: "Restore failed: \(reason)", productId: productId)
239-
case .storeKitError(let error):
240-
self.init(code: Self.E_SERVICE_ERROR, message: "Store service error: \(error.localizedDescription)", productId: productId)
241-
case .notSupported:
242-
self.init(code: Self.E_FEATURE_NOT_SUPPORTED, message: "Feature not supported on this platform", productId: productId)
243-
case .unknownError:
244-
self.init(code: Self.E_UNKNOWN, message: "Unknown error occurred", productId: productId)
245-
}
246-
}
247-
248176
/// Check if error can be retried
249177
var canRetry: Bool {
250178
switch code {
@@ -279,35 +207,3 @@ public extension OpenIapError {
279207
}
280208
}
281209

282-
// MARK: - Mapping from thrown errors to codes
283-
public extension OpenIapFailure {
284-
/// OpenIAP string code that corresponds to this error case
285-
var code: String {
286-
switch self {
287-
case .purchaseCancelled:
288-
return OpenIapError.E_USER_CANCELLED
289-
case .purchaseDeferred:
290-
return OpenIapError.E_DEFERRED_PAYMENT
291-
case .productNotFound:
292-
return OpenIapError.E_SKU_NOT_FOUND
293-
case .purchaseFailed:
294-
return OpenIapError.E_SERVICE_ERROR
295-
case .paymentNotAllowed:
296-
return OpenIapError.E_IAP_NOT_AVAILABLE
297-
case .invalidReceipt:
298-
return OpenIapError.E_RECEIPT_FAILED
299-
case .networkError:
300-
return OpenIapError.E_NETWORK_ERROR
301-
case .verificationFailed:
302-
return OpenIapError.E_TRANSACTION_VALIDATION_FAILED
303-
case .restoreFailed:
304-
return OpenIapError.E_SERVICE_ERROR
305-
case .storeKitError:
306-
return OpenIapError.E_SERVICE_ERROR
307-
case .notSupported:
308-
return OpenIapError.E_FEATURE_NOT_SUPPORTED
309-
case .unknownError:
310-
return OpenIapError.E_UNKNOWN
311-
}
312-
}
313-
}

0 commit comments

Comments
 (0)