SwiftyStoreKit icon indicating copy to clipboard operation
SwiftyStoreKit copied to clipboard

Crash in ProductsInfoController.retrieveProductsInfo

Open carbonimax opened this issue 5 years ago • 7 comments

Platform

  • [x] iOS
  • [ ] macOS
  • [ ] tvOS

In app purchase type

  • [ ] Consumable
  • [x] Non-consumable
  • [ ] Auto-Renewable Subscription
  • [ ] Non-Renewing Subscription

Environment

  • [x] Sandbox
  • [x] Production

Version

0.15.0

Report

Issue summary

sometimes, it crash when it retrieve products info.

1  SwiftyStoreKit                 0x101497258 partial apply for closure #1 in InAppProductQueryRequest.performCallback(_:) (InAppProductQueryRequest.swift:76)
    2  SwiftyStoreKit                 0x1014a0f34 thunk for @escaping @callee_guaranteed () -> () (<compiler-generated>)
3  libdispatch.dylib              0x2320bca38 _dispatch_call_block_and_release + 24
4  libdispatch.dylib              0x2320bd7d4 _dispatch_client_callout + 16
5  libdispatch.dylib              0x23209d9e4 _dispatch_main_queue_callback_4CF$VARIANT$armv81 + 1008
6  CoreFoundation                 0x23260dec0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
7  CoreFoundation                 0x232608df8 __CFRunLoopRun + 1924
8  CoreFoundation                 0x232608354 CFRunLoopRunSpecific + 436
9  GraphicsServices               0x23480879c GSEventRunModal + 104
10 UIKitCore                      0x25ebf3b68 UIApplicationMain + 212
11 app.                               0x100438400 main (main.m:14)
12 libdyld.dylib                  0x2320ce8e0 start + 4```

carbonimax avatar May 21 '19 12:05 carbonimax

I have the same issue!

gianpispi avatar May 28 '19 08:05 gianpispi

Same here

tamir-maoz avatar Jun 02 '19 06:06 tamir-maoz

+1

RamblinWreck77 avatar Jul 18 '19 15:07 RamblinWreck77

inflightRequests should be thread safe

astrokin avatar Sep 22 '19 21:09 astrokin

Do you have concrete simple steps to reproduce/simulate this bug?

gerchicov-bp avatar Dec 26 '19 07:12 gerchicov-bp

Hello all! Please check out the latest commit on the develop branch to see if this issue has been resolved (thanks to @cipolleschi and PR #495).

Sam-Spencer avatar Aug 15 '20 19:08 Sam-Spencer

https://github.com/bizz84/SwiftyStoreKit/commit/70761d29fceb2c0d68a93dd3f26c81db4c0321ea is reverting the https://github.com/bizz84/SwiftyStoreKit/pull/495 So the crash still can accure. Also there are 3 force unwraps! It's bad idea.

Like a workaround you can downgrade to old implementation (like i did):

class ProductsInfoController: NSObject {
    struct InAppProductQuery {
        let request: InAppProductRequest
        var completionHandlers: [InAppProductRequestCallback]
    }

    // As we can have multiple inflight requests, we store them in a dictionary by product ids
    private let inflightRequestsQueue = DispatchQueue(label: "InflightRequestsQueue", attributes: .concurrent)
    private var threadInflightRequests: [Set<String>: InAppProductQuery] = [:]
    private var inflightRequests: [Set<String>: InAppProductQuery] {
        get {
            return inflightRequestsQueue.sync {
                return threadInflightRequests
            }
        }
        set {
            inflightRequestsQueue.async(flags: .barrier) {
                self.threadInflightRequests = newValue
            }
        }
    }

    func retrieveProductsInfo(_ productIds: Set<String>, completion: @escaping (RetrieveResults) -> Void) {
        guard !Thread.current.isMainThread else {
            DispatchQueue.global().async { [weak self] in
                self?.retrieveProductsInfo(productIds, completion: completion)
            }
            return
        }
        if inflightRequests[productIds] == nil {
            let request = InAppProductQueryRequest(productIds: productIds) { results in
                if let query = self.inflightRequests[productIds] {
                    for completion in query.completionHandlers {
                        completion(results)
                    }
                    self.inflightRequests[productIds] = nil
                } else {
                    // should not get here, but if it does it seems reasonable to call the outer completion block
                    completion(results)
                }
            }
            inflightRequests[productIds] = InAppProductQuery(request: request, completionHandlers: [completion])
            request.start()
        } else {
            inflightRequests[productIds]?.completionHandlers.append(completion)
        }
    }

    func cancelAllInflightRequests() {
        guard !Thread.current.isMainThread else {
            DispatchQueue.global().async { [weak self] in
                self?.cancelAllInflightRequests()
            }
            return
        }
        inflightRequests.forEach({ $0.value.request.cancel() })
        inflightRequests.removeAll()
    }
}

NikKovIos avatar Jun 09 '21 09:06 NikKovIos