Skip to content

Commit

Permalink
Merge pull request #610 from bizz84/develop
Browse files Browse the repository at this point in the history
Some Bug Fixes & Improvements
  • Loading branch information
Sam-Spencer authored Jan 7, 2021
2 parents c4658da + 70761d2 commit 8fa93ef
Show file tree
Hide file tree
Showing 22 changed files with 160 additions and 96 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

All notable changes to this project will be documented in this file.

* Make `ProductsInfoController`'s `retrieveProductsInfo` thread safe ([#405]https://github.com/bizz84/SwiftyStoreKit/pull/495), related issues: [#344](https://github.com/bizz84/SwiftyStoreKit/issues/344) and [#468](https://github.com/bizz84/SwiftyStoreKit/issues/468)

## [0.15.0](https://github.com/bizz84/SwiftyStoreKit/releases/tag/0.15.0) Update project to Swift 5, Xcode 10.2

* Update project to Swift 5 ([#457](https://github.com/bizz84/SwiftyStoreKit/pull/457)), related issue: [#456](https://github.com/bizz84/SwiftyStoreKit/issues/456)
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let package = Package(
// Products define the executables and libraries produced by a package, and make them visible to other packages.
.library(
name: "SwiftyStoreKit",
targets: ["SwiftyStoreKit"]),
targets: ["SwiftyStoreKit"])
],
dependencies: [
// Dependencies declare other packages that this package depends on.
Expand All @@ -22,6 +22,6 @@ let package = Package(
dependencies: []),
.testTarget(
name: "SwiftyStoreKitTests",
dependencies: ["SwiftyStoreKit"]),
dependencies: ["SwiftyStoreKit"])
]
)
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,8 @@ It would be great to showcase apps using SwiftyStoreKit here. Pull requests welc
* [Talk Dim Sum](https://itunes.apple.com/us/app/talk-dim-sum/id953929066) - Your dim sum companion
* [Sluggard](https://itunes.apple.com/app/id1160131071) - Perform simple exercises to reduce the risks of sedentary lifestyle
* [Debts iOS](https://debts.ivanvorobei.by/ios) & [Debts macOS](https://debts.ivanvorobei.by/macos) - Track amounts owed
* [Botcher](https://itunes.apple.com/us/app/id1522337788) - Good for finding something to do
* [Hashr](https://apps.apple.com/app/id1166499829) - Generate unique password hashes based on website and master password

A full list of apps is published [on AppSight](https://www.appsight.io/sdk/574154).

Expand Down
18 changes: 15 additions & 3 deletions Sources/SwiftyStoreKit/InAppProductQueryRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,20 @@ public protocol InAppRequest: class {
func cancel()
}

protocol InAppProductRequest: InAppRequest { }
protocol InAppProductRequest: InAppRequest {
var hasCompleted: Bool { get }
var cachedResults: RetrieveResults? { get }
}

class InAppProductQueryRequest: NSObject, InAppProductRequest, SKProductsRequestDelegate {

private let callback: InAppProductRequestCallback
private let request: SKProductsRequest

private(set) var cachedResults: RetrieveResults?

var hasCompleted: Bool { cachedResults != nil }

deinit {
request.delegate = nil
}
Expand All @@ -52,6 +59,7 @@ class InAppProductQueryRequest: NSObject, InAppProductRequest, SKProductsRequest
func start() {
request.start()
}

func cancel() {
request.cancel()
}
Expand All @@ -61,8 +69,12 @@ class InAppProductQueryRequest: NSObject, InAppProductRequest, SKProductsRequest

let retrievedProducts = Set<SKProduct>(response.products)
let invalidProductIDs = Set<String>(response.invalidProductIdentifiers)
performCallback(RetrieveResults(retrievedProducts: retrievedProducts,
invalidProductIDs: invalidProductIDs, error: nil))
let results = RetrieveResults(
retrievedProducts: retrievedProducts,
invalidProductIDs: invalidProductIDs, error: nil
)
self.cachedResults = results
performCallback(results)
}

func requestDidFinish(_ request: SKRequest) {
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftyStoreKit/OS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public struct SKError: Error {
self._nsError = _nsError
}

var code: Code {
public var code: Code {
return Code(rawValue: _nsError.code) ?? .unknown
}

Expand Down
28 changes: 26 additions & 2 deletions Sources/SwiftyStoreKit/PaymentQueueController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ extension SKPaymentTransactionState: CustomDebugStringConvertible {
}
}

struct EntitlementRevocation {
let callback: ([String]) -> Void

init(callback: @escaping ([String]) -> Void) {
self.callback = callback
}
}

class PaymentQueueController: NSObject, SKPaymentTransactionObserver {

private let paymentsController: PaymentsController
Expand All @@ -97,7 +105,9 @@ class PaymentQueueController: NSObject, SKPaymentTransactionObserver {
private let completeTransactionsController: CompleteTransactionsController

unowned let paymentQueue: PaymentQueue


private var entitlementRevocation: EntitlementRevocation?

deinit {
paymentQueue.remove(self)
}
Expand Down Expand Up @@ -145,6 +155,15 @@ class PaymentQueueController: NSObject, SKPaymentTransactionObserver {
paymentsController.append(payment)
}

func onEntitlementRevocation(_ revocation: EntitlementRevocation) {
guard entitlementRevocation == nil else {
print("SwiftyStoreKit.onEntitlementRevocation() should only be called once when the app launches. Ignoring this call")
return
}

self.entitlementRevocation = revocation
}

func restorePurchases(_ restorePurchases: RestorePurchases) {
assertCompleteTransactionsWasCalled()

Expand Down Expand Up @@ -206,7 +225,7 @@ class PaymentQueueController: NSObject, SKPaymentTransactionObserver {
* Failed transactions only ever belong to queued payment requests.
* restoreCompletedTransactionsFailedWithError is always called when a restore purchases request fails.
* paymentQueueRestoreCompletedTransactionsFinished is always called following 0 or more update transactions when a restore purchases request succeeds.
* A complete transactions handler is require to catch any transactions that are updated when the app is not running.
* A complete transactions handler is required to catch any transactions that are updated when the app is not running.
* Registering a complete transactions handler when the app launches ensures that any pending transactions can be cleared.
* If a complete transactions handler is missing, pending transactions can be mis-attributed to any new incoming payments or restore purchases.
*
Expand All @@ -233,6 +252,11 @@ class PaymentQueueController: NSObject, SKPaymentTransactionObserver {
}
}

func paymentQueue(_ queue: SKPaymentQueue, didRevokeEntitlementsForProductIdentifiers productIdentifiers: [String]) {

self.entitlementRevocation?.callback(productIdentifiers)
}

func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) {

}
Expand Down
12 changes: 12 additions & 0 deletions Sources/SwiftyStoreKit/ProductsInfoController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,21 @@ class ProductsInfoController: NSObject {
}
inflightRequests[productIds] = InAppProductQuery(request: request, completionHandlers: [completion])
request.start()

return request

} else {

inflightRequests[productIds]!.completionHandlers.append(completion)

let query = inflightRequests[productIds]!

if query.request.hasCompleted {
query.completionHandlers.forEach {
$0(query.request.cachedResults!)
}
}

return inflightRequests[productIds]!.request
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,3 @@ public extension SKProductDiscount {
}

}

2 changes: 1 addition & 1 deletion Sources/SwiftyStoreKit/SwiftyStoreKit+Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public enum VerifySubscriptionResult {
case notPurchased
}

public enum SubscriptionType {
public enum SubscriptionType: Hashable {
case autoRenewable
case nonRenewing(validDuration: TimeInterval)
}
Expand Down
13 changes: 13 additions & 0 deletions Sources/SwiftyStoreKit/SwiftyStoreKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ public class SwiftyStoreKit {

paymentQueueController.completeTransactions(CompleteTransactions(atomically: atomically, callback: completion))
}

fileprivate func onEntitlementRevocation(completion: @escaping ([String]) -> Void) {

paymentQueueController.onEntitlementRevocation(EntitlementRevocation(callback: completion))
}

fileprivate func finishTransaction(_ transaction: PaymentTransaction) {

Expand Down Expand Up @@ -187,6 +192,14 @@ extension SwiftyStoreKit {

sharedInstance.completeTransactions(atomically: atomically, completion: completion)
}

/// Entitlement revocation notification
/// - Parameter completion: handler for result (list of product identifiers revoked)
@available(iOS 14, tvOS 14, OSX 11, watchOS 7, macCatalyst 14, *)
public class func onEntitlementRevocation(completion: @escaping ([String]) -> Void) {

sharedInstance.onEntitlementRevocation(completion: completion)
}

/// Finish a transaction
///
Expand Down
Loading

0 comments on commit 8fa93ef

Please sign in to comment.