AppTransaction: how to use in ObjC apps (now that we are forced to use it after the exit(173) deprecation)

Hello

We are developers of a long-running game series and now reports have started to come in that users who install any of our previous games from the Mac App Store on OS X Sequoia are shown a popup claiming "The exit(173) API is no longer available". It's actually a lie, the mechanism is still there, the receipt generation still works and the game still runs afterwards. But the popup is confusing to users therefore we need to update the code.

Apparently the replacement for the old receipt generation mechanism is AppTransaction which does not exist for Objective C. We have attempted to use it using the Swift/ObjC interoperability and failed so far. The problem is that we need to call async methods in AppTransaction and all our attempts to make this work have failed so far. It seems as the actor/@MainActor concept is not supported by Swift/ObjC interoperability and without those concepts we don't know how to pass results from the async context to the callers from ObjC.

The lack of usable information and code online regarding this topic is highly frustrating. Apple really needs to provide better support for developers if they want us to continue to support the Mac platform with high quality games and applications on the Mac App Store.

We would appreciate if anyone can cook up a working sample code how to use AppTransaction in ObjC. Thanks in advance!

Answered by DTS Engineer in 810009022
All I need to do is execute one line of Swift:

Now that’s something I can help with. Here how I did this:

  1. I created a new project from an Objective-C template.

  2. I choose File > New > File from Template and then chose the macOS > Cocoa Class template, with Swift as the language.

  3. This asked whether I want to create a bridging header. I allowed that, but it’s not relevant to this discussion.

  4. I edited the file to look like this:

    import StoreKit
    
    @objc
    class MyAppTransaction: NSObject {
    
        @objc
        class func checkReceipt() async -> String {
            do {
                let verificationResult = try await AppTransaction.shared
                switch verificationResult {
                case .unverified(_, _):
                    return "NG"
                case .verified(_):
                    return "OK"
                }
            } catch {
                return "ERR"
            }
        }
    }
    

    I’ve chosen silly status values. You can tweak these values, or even the types, as you wish. However, the types must be Objective-C representable. In this case I’m using String, which bridges to NSString.

  5. On the Objective-C side, I added this:

    #import "Test764537-Swift.h"
    

    and then called the Swift code like this:

    [MyAppTransaction checkReceiptWithCompletionHandler:^(NSString * _Nonnull status) {
        NSLog(@"status: %@", status);
    }];
    

I’m working with Xcode 16.0, but I don’t think there’s anything Xcode 16 specific about this.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you so much for your help. I haven't decided what version to target, but this is easy. The fact that my project has a valid receipt that I got by calling exit(173) made it easier. In my opinion deprecating this is a bad idea that will make it harder for developers.

I am in the same boat and am now confused - I thought the solution, for ObjC, was to use SKReceiptRefreshRequest to refresh the receipt?!

My personal problem, though, is that I cannot test my code now. I seem to need a fresh system that has never seen the app before. I tried to create a VM with Fusion 11.5.3 as well as 13.5.2, but in all cases, I was only able to install macOS 10.14 or 11, but then upgrading to macOS 13 or 14 always results in kernel panics during install or shortly thereafter, with all settings still being defaults (other than increasing CPU cores and memory). Of course, this needs to be done on an Intel CPU Mac, because the Silicon Macs don't support the App Store in VMs. This is so fooked up.

I thought the solution, for ObjC, was to use SKReceiptRefreshRequest to refresh the receipt?

That still works but is deprecated. (IIRC).

AppTransaction: how to use in ObjC apps (now that we are forced to use it after the exit(173) deprecation)
 
 
Q