Skip to content

🐛 Deadlock with RC Paywalls Restore #5729

@denrase

Description

@denrase

Describe the bug

When pressing the restore button of a RC paywall, the app freezes.

Platform

iOS

SDK version

5.45.1

SDK integration method

Swift Package Manager

StoreKit version

StoreKit 2 (default on versions >=5.0.0)

OS version

iOS 26

Xcode version

Xcode 26

Device and/or simulator

Device

Environment

Sandbox

How widespread is the issue

100%

Debug logs

DEBUG: Restoring purchases
DEBUG: Will execute restore purchases logic provided by RevenueCat.
DEBUG: ℹ️ Found 0 unsynced attributes for App User ID: $RCAnonymousID:6b034102d4ff4a7fb4e41e0dd147605a
DEBUG: ℹ️ Skipping products request for these products because they were already cached: ["cc.cashcounter.apple.pro.sub.weekly.2"]
DEBUG: ℹ️ PostReceiptDataOperation: Started
DEBUG: ℹ️ PostReceiptDataOperation: Posting JWS token (source: 'restore'):
DEBUG: ℹ️ There are no requests currently running, starting request POST /v1/receipts
DEBUG: ℹ️ API request started: POST '/v1/receipts'
DEBUG: ℹ️ API request completed: POST '/v1/receipts' (200)

Steps to reproduce

  1. Present RC paywall on fresh app install
  2. Press restore button
  3. App freez
Image

Other information

Since this is happening 100%, this makes the Paywall feature unusable in the current form. Looking through the repo, there seems to be a [deadlocking issue](https://github.com/RevenueCat/purchases-ios/issues/4137) which this might relate to.


Thread 1 Queue : com.apple.main-thread (serial)
#0	0x000000023fcd40b4 in __psynch_mutexwait ()
#1	0x00000001f295bc94 in _pthread_mutex_firstfit_lock_wait ()
#2	0x00000001f295acac in _pthread_mutex_firstfit_lock_slow ()
#3	0x0000000108648960 in Lock.perform<Foundation.Data?>(_:) at /Users/denis/Library/Developer/Xcode/DerivedData/CashCounter-erjadhymqflniahhdlkthhtkfuuv/SourcePackages/checkouts/purchases-ios/Sources/Misc/Concurrency/Lock.swift:41
#4	0x00000001086476a8 in Atomic.withValue<RevenueCat.CustomerInfoManager.Data>(_:) at /Users/denis/Library/Developer/Xcode/DerivedData/CashCounter-erjadhymqflniahhdlkthhtkfuuv/SourcePackages/checkouts/purchases-ios/Sources/Misc/Concurrency/Atomic.swift:81
#5	0x0000000108593754 in CustomerInfoManager.withData<Foundation.Data?>(_:) at /Users/denis/Library/Developer/Xcode/DerivedData/CashCounter-erjadhymqflniahhdlkthhtkfuuv/SourcePackages/checkouts/purchases-ios/Sources/Identity/CustomerInfoManager.swift:674
#6	0x0000000108593c44 in CustomerInfoManager.cachedCustomerInfo(appUserID:) at /Users/denis/Library/Developer/Xcode/DerivedData/CashCounter-erjadhymqflniahhdlkthhtkfuuv/SourcePackages/checkouts/purchases-ios/Sources/Identity/CustomerInfoManager.swift:241
#7	0x00000001089d6e2c in Purchases.cachedCustomerInfo.getter at /Users/denis/Library/Developer/Xcode/DerivedData/CashCounter-erjadhymqflniahhdlkthhtkfuuv/SourcePackages/checkouts/purchases-ios/Sources/Purchasing/Purchases/Purchases.swift:1084
#8	0x0000000108e5a76c in static PaywallView.loadCachedCustomerInfoIfPossible() at /Users/denis/Library/Developer/Xcode/DerivedData/CashCounter-erjadhymqflniahhdlkthhtkfuuv/SourcePackages/checkouts/purchases-ios/RevenueCatUI/PaywallView.swift:405
#9	0x0000000108e59a40 in PaywallView.init(configuration:paywallViewOwnsPurchaseHandler:) at /Users/denis/Library/Developer/Xcode/DerivedData/CashCounter-erjadhymqflniahhdlkthhtkfuuv/SourcePackages/checkouts/purchases-ios/RevenueCatUI/PaywallView.swift:161
#10	0x0000000108e5a1b8 in PaywallView.init(offering:fonts:displayCloseButton:useDraftPaywall:introEligibility:performPurchase:performRestore:) at /Users/denis/Library/Developer/Xcode/DerivedData/CashCounter-erjadhymqflniahhdlkthhtkfuuv/SourcePackages/checkouts/purchases-ios/RevenueCatUI/PaywallView.swift:131
#11	0x0000000108e59eb8 in PaywallView.init(offering:fonts:displayCloseButton:performPurchase:performRestore:) at /Users/denis/Library/Developer/Xcode/DerivedData/CashCounter-erjadhymqflniahhdlkthhtkfuuv/SourcePackages/checkouts/purchases-ios/RevenueCatUI/PaywallView.swift:109
#12	0x000000010849b588 in closure #1 in ProScreen.body.getter at /Users/denis/Library/Developer/Xcode/DerivedData/CashCounter-erjadhymqflniahhdlkthhtkfuuv/SourcePackages/checkouts/base/Sources/BaseViews/Pro/ProScreen.swift:31
#13	0x00000001a19ad7c0 in closure #1 () -> τ_0_0 in SwiftUI.VStack.init(alignment: SwiftUI.HorizontalAlignment, spacing: Swift.Optional<CoreGraphics.CGFloat>, content: () -> τ_0_0) -> SwiftUI.VStack<τ_0_0> ()
#14	0x00000001a190e514 in __allocating_init ()
#15	0x00000001a19ad6f0 in __allocating_init ()
#16	0x000000010849a88c in ProScreen.body.getter at /Users/denis/Library/Developer/Xcode/DerivedData/CashCounter-erjadhymqflniahhdlkthhtkfuuv/SourcePackages/checkouts/base/Sources/BaseViews/Pro/ProScreen.swift:27
#17	0x000000010849ca1c in protocol witness for View.body.getter in conformance ProScreen ()
#18	0x00000001a18ebe40 in closure #1 @Swift.MainActor () -> () in SwiftUI.ViewBodyAccessor.updateBody(of: τ_0_0, changed: Swift.Bool) -> () ()
#19	0x00000001a18eb748 in updateBody ()
#20	0x00000001a18eb4e0 in protocol witness for SwiftUI.BodyAccessor.updateBody(of: τ_0_0.Container, changed: Swift.Bool) -> () in conformance SwiftUI.ViewBodyAccessor<τ_0_0> : SwiftUI.BodyAccessor in SwiftUI ()
#21	0x00000001a18de770 in closure #1 () -> () in SwiftUI.DynamicBody.updateValue() -> () ()
#22	0x00000001a18ddf6c in updateValue ()
#23	0x00000001a1e67504 in partial apply forwarder for implicit closure #1 (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 () -> (Swift.UnsafeMutableRawPointer, __C.AGAttribute) -> () in closure #1 (Swift.UnsafePointer<τ_1_0>) -> AttributeGraph.Attribute<τ_0_0> in AttributeGraph.Attribute.init<τ_0_0 where τ_0_0 == τ_1_0.Value, τ_1_0: AttributeGraph.StatefulRule>(τ_1_0) -> AttributeGraph.Attribute<τ_0_0> ()
#24	0x00000001c8abee48 in AG::Graph::UpdateStack::update ()
#25	0x00000001c8ac0cf0 in AG::Subgraph::update ()
#26	0x00000001a1946c88 in merged function signature specialization <Arg[3] = Owned To Guaranteed> of function signature specialization <Arg[1] = [Closure Propagated : implicit closure #2 () -> () in implicit closure #1 @Sendable (SwiftUI.(AsyncTransaction in _F9F204BD2F8DB167A76F17F3FB1B3335)) -> () -> () in SwiftUI.GraphHost.flushTransactions() -> (), Argument Types : [SwiftUI.AsyncTransaction]> of SwiftUI.GraphHost.runTransaction(_: Swift.Optional<SwiftUI.Transaction>, do: () -> (), id: Swift.Optional<Swift.UInt32>) -> () ()
#27	0x00000001a18d7218 in flushTransactions ()
#28	0x00000001a0659270 in closure #1 (SwiftUI.GraphHost) -> () in SwiftUI._UIHostingView._renderForTest(interval: Swift.Double) -> () ()
#29	0x00000001a18d14bc in partial apply forwarder for closure #1 (SwiftUI.ViewGraph) -> τ_1_0 in SwiftUI.ViewGraphRootValueUpdater.updateGraph<τ_0_0>(body: (SwiftUI.GraphHost) -> τ_1_0) -> τ_1_0 ()
#30	0x00000001a18d3e98 in _updateViewGraph ()
#31	0x00000001a18d1414 in updateGraph ()
#32	0x00000001a065923c in closure #1 () -> () in closure #1 () -> () in closure #1 () -> () in SwiftUI._UIHostingView.beginTransaction() -> () ()
#33	0x00000001a0659188 in partial apply forwarder for closure #1 () -> () in closure #1 () -> () in closure #1 () -> () in SwiftUI._UIHostingView.beginTransaction() -> () ()
#34	0x00000001a18c99e4 in closure #1 () throws -> τ_0_0 in static SwiftUI.Update.ensure<τ_0_0>(() throws -> τ_0_0) throws -> τ_0_0 ()
#35	0x00000001a18c9c28 in static SwiftUI.Update.ensure<τ_0_0>(() throws -> τ_0_0) throws -> τ_0_0 ()
#36	0x00000001a0659160 in partial apply forwarder for closure #1 () -> () in closure #1 () -> () in SwiftUI._UIHostingView.beginTransaction() -> () ()
#37	0x000000019d403b44 in ___lldb_unnamed_symbol293584 ()
#38	0x000000019d914e4c in ___lldb_unnamed_symbol308985 ()
#39	0x000000019d41f3c8 in ___lldb_unnamed_symbol293684 ()
#40	0x00000001a18c274c in static SwiftUI.Update.dispatchImmediately<τ_0_0>(reason: Swift.Optional<SwiftUI.CustomEventTrace.ActionEventType.Reason>, _: () -> τ_0_0) -> τ_0_0 ()
#41	0x00000001a1e35038 in static SwiftUI.ViewGraphHostUpdate.dispatchImmediately<τ_0_0>(() -> τ_0_0) -> τ_0_0 ()
#42	0x000000019d472e40 in ___lldb_unnamed_symbol294125 ()
#43	0x000000019d472d24 in ___lldb_unnamed_symbol294123 ()
#44	0x000000019d480ee8 in _UIUpdateSequenceRunNext ()
#45	0x000000019d480378 in schedulerStepScheduledMainSectionContinue ()
#46	0x00000002821295f8 in UC::DriverCore::continueProcessing ()
#47	0x0000000197b77230 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#48	0x0000000197b771a4 in __CFRunLoopDoSource0 ()
#49	0x0000000197b54c6c in __CFRunLoopDoSources0 ()
#50	0x0000000197b2a8b0 in __CFRunLoopRun ()
#51	0x0000000197b29c44 in _CFRunLoopRunSpecificWithOptions ()
#52	0x0000000236f1a498 in GSEventRunModal ()
#53	0x000000019d4a4ddc in -[UIApplication _run] ()
#54	0x000000019d449b0c in UIApplicationMain ()
#55	0x00000001a05fe6f0 in closure #1 (Swift.UnsafeMutablePointer<Swift.Optional<Swift.UnsafeMutablePointer<Swift.Int8>>>) -> Swift.Never in SwiftUI.KitRendererCommon(Swift.AnyObject.Type) -> Swift.Never ()
#56	0x00000001a05fb22c in runApp ()
#57	0x00000001a05fad18 in static SwiftUI.App.main() -> () ()
#58	0x00000001083ca69c in static CashCounterApp.$main() ()
#59	0x00000001083ca774 in main ()
#60	0x0000000194ba2e28 in start ()

Additional context

Also, before presenting the paywall view, I load the offering and the user info, as it's the only way to reliably circumvent the presentation of the RC internal loading animation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions