Unfinished transactions not being emitted on start of app

I'm using the iOS simulator with a StoreKit configuration file. I can see that there have been transactions while the app has been closed, but my StoreKit 2 listener is never called with those updates to be able to finish them When I open my app from a cold start.

I've added a listener on application(_:didFinishLaunching:launchOptions:) like this:

  func startObservingTransactions() {
    task = Task(priority: .background) {
      for await result in Transaction.updates {
        if case .verified(let transaction) = result {
          await transaction.finish()
        }
      }
    }
  }

But the Transaction.updates loop never gets called (have added breakpoints to check). It's only ever called when a purchase is made, or subsequent transaction renewals when the app is open. Only then it will get the previously unfinished transactions.

Steps to reproduce:

  1. Create an app with a StoreKit config file (with sped up transactions) to purchase an item
  2. Make a purchase then quit the app
  3. Wait for a bit for more transactions to be made while the app is closed.
  4. Open the app from a cold start and none of the transactions will be finished by the listener in your app. Cancel the subscription via the transaction manager.
  5. Close and open the app from a cold start. The first transaction will be finished by the listener but none of the others will be.

In Apple's docs it says

If your app has unfinished transactions, the listener receives them immediately after the app launches

Why is this not the case?

Post not yet marked as solved Up vote post of itsyusuf Down vote post of itsyusuf
2.7k views
  • Update It turns out that if there are two Transaction.updates observers in an app it seems to only return the updates to one of these! When I get rid of the other observer it works fine... why?!

  • actually ignore above comment, still not working correctly. Any help?

Add a Comment

Replies

I have the same issue in an app using StoreKit 2.

Transaction.updates does not emit unfinished transactions after app launch like the documentation says. I have only one observer, and my code is based on the StoreKit 2 demo code from Apple. (And I have my Store class modeled on Apple's example as a @StateObject on my main SwiftUI view, just like Apple's sample.)

If I let a subscription renewal come through while the app is running, Transaction.updates does emit all of the unfinished transaction updates, but it's unrealistic to expect the app to be open in production at the time a subscription renewal occurs. (Transaction.currentEntitlements does show the correct information, but it's concerning that I potentially have so many unfinished transactions.)

Does anyone have any other additional info, workarounds/etc.? @itsyusuf did you happen to figure out any fixes for this issue?

  • I should also note, Transaction.unfinished doesn't appear to have anything in it on app launch when Transaction.updates doesn't emit anything in my case. (Even though I know there are multiple unfinished transactions waiting.)

Add a Comment

@khagan Yes your issue is exactly the same as mine. Unfortunately I couldn't figure this out and no help from Apple! I had to ditch StoreKit 2 in favour of StoreKit 1. I reproduced the bug in Apple's own sample app. Maybe you could file a bug report for this and link back here with the report number?

Thanks @itsyusuf

I was hoping you'd figured something out. I just filed a Feedback Assistant bug. #FB11984421

I attached Apple's own sample app code to go along with the reproduction steps.

I would think this should be a critical bug to fix.

  • @khagan thanks, if you receive any reply please report back in this thread. I’m hoping that in production it works fine and it’s just a sandbox bug but I’m not willing to risk it.

Add a Comment

I am experiencing this exact same issue as well. Subscription renewals that happen when the app is not running are not presented to my app on launch through Transaction.updates. They remain in an "unfinished" state. Attempting to Restore Purchases does nothing, and attempting to repurchase the subscription just presents the alert that the item has already been purchased. In both cases, current entitlements never reflect that the subscription is purchased and valid.

I have also tried using Product.SubscriptionInfo.Status.updates. Updates do not come in on app launch when a renewal happens when the app is not running. But I do see updates come in when calling AppStore.sync(). This is unlike Transaction.updates, where nothing happens when calling AppStore.sync(). This is not an acceptable workaround, as auto-renewals should be automatic and transparent to the user; not force them to "Restore Purchases".

Apple responded to my feedback and said they couldn't reproduce on the latest Xcode 14.3 and iOS 16.4 seeds and to send a sysdiagnose if there's still an issue. I think that might mean it's fixed now? I might wait for the release versions of Xcode 14.3/iOS 16.4 before testing it again and sending a sysdiagnose if it still occurs -- I do think it's quite possible that this only happens on the simulator (or is fixed in the betas).

Update: Now that Xcode 14.3 and the simulators for 16.4 are out, I tried again. I still see the same issue. I updated my Feedback Assistant report to Apple and attached a sysdiagnose. I think this is only happening in simulators, but it still makes me a little uneasy.

Is there an update on this issue? (I have tons of users which have non working renewals, and restoring purchase is not working for them.)

@Wizfinger Apple updated the feedback report I filed with a request to confirm the bug still exists on iOS 16.6. Since I think it may only happen on a simulator, I'm not sure if this is possible. I was going to wait until the release version of Xcode 15 to retest it. Not sure if anyone else has tested it on the betas/etc.

However, if you're having issues with users on real users with renewals, I think you may have something else going on. I actually went ahead and deployed my Storekit 2 code to production a while back, even though the simulator behavior was concerning. I think it actually works OK on a real device with real store purchases, since I haven't had any users complain (I've had subscription purchases, renewals, and in-app purchases through the code).

For what it's worth, I just added the code from the top of this thread into my new App in the Xcode 15 beta and it works great. I'm using the new SwiftUI hooks for subscriptions in StoreKit 2, and in a bunch of cases, it wasn't reliably finishing transactions. So I added the above code as a function invoked using the .onAppear modifier for the main view, and I literally only had to change one thing: Instead of "task = ...", I set it to "let t = ...", as it complained that "task" is a reserved word or some such.

So, thanks for the snippet! Good luck!!

FYI, renewals now work correctly for me also in Xcode 15.0/iOS 17 simulator (while open), so looks like the bug with the simulator is fixed to me. I closed my feedback.