Calling StoreKit Swift from C++

What is the most obvious method of calling StoreKit from C++. I'm getting blocked by the fact that most of the critical StoreKit calls are async and functions marked a sync don't show up in the swift header for me to call from C++ (at least as far as I can tell).

I'm trying to call

let result = try await Product.products(for:productIDs) or
let result = try await product.purchase()

And C++ can't even see any functions I wrap these in as far as I can tell because i have to make them async. What am I missing?

I tried a lot of alternates, like wrapping in

Task { let result = try await Product.products(for:productIDs) }

and it gives me 'Passing closure as a sending parameter' errors.

Also when I try to call the same above code it gives me 'initializtion of immutable value never used' errors and the variables never appear.

Code:

struct storeChooser {
public var productIDs: [String]
public function checkProduct1 {
Task { let result = try await Product.products(for: productIDs) }

The above gives the initialization of immutable value skipped, and when I create a

@State var products

Then I get the 'passing closure as a sending parameter' error when i try to run it in a task

it appears if I could make the function async and call it from C++ and have it return nothing it may work, does anyone know how to get C++ to see an async function in the -Swift.h file?

Answered by DTS Engineer in 838422022
Written by Roadmaster1975 in 783205021
What am I missing?

Well, you could argue that Swift is missing an interop feature for exporting async functions to C++ |-: If you want to raise that with the Swift folks then my advice is that you wade into the Swift Evolution process. OTOH, if you just want to solve your current problem, that’s something we can discuss here.

First, some background. When Swift imports Objective-C APIs, it looks for methods with a completion handler and imports each one twice: First as a synchronous method with a completion handler and again as an async method. For example, the -[NSSavePanel beginWithCompletionHandler:] method method gets imported as:

  • func begin(completionHandler handler: @escaping (NSApplication.ModalResponse) -> Void)

  • func begin() async -> NSApplication.ModalResponse

The compiler synthesises an implementation of the latter by combining the former with a checked continuation [1].

This synthesis also works the other way. In this post — it’s not an accident it’s also about StoreKit! — I show how Swift can export an async method to Objective-C as a method that takes a completion handler.

AFAICT this feature is simply missing for C++ interoperability [2]. Consider this type:

public class HackObjC: NSObject {
@objc
public class func addSync(_ a: Int, _ b: Int) -> Int {
return a + b
}
@objc
public class func addAsync(_ a: Int, _ b: Int) async -> Int {
return a + b
}
@objc
public class func addUsingCompletionHandler(_ a: Int, _ b: Int, _ completionHandler: @escaping (_ result: Int) -> Void) {
completionHandler(a + b)
}
@objc
public class func addUsingCompletionCallback(_ a: Int, _ b: Int, _ completionHandler: @convention(c) @escaping (_ result: Int) -> Void) {
completionHandler(a + b)
}
}

If you open the MyModlue-Swift.h file (I’m using Xcode 16.3) you see this:

@interface HackObjC : NSObject
+ (NSInteger)addSync:(NSInteger)a :(NSInteger)b SWIFT_WARN_UNUSED_RESULT;
+ (void)addAsync:(NSInteger)a :(NSInteger)b completionHandler:(void (^ _Nonnull)(NSInteger))completionHandler;
+ (void)addUsingCompletionHandler:(NSInteger)a :(NSInteger)b :(void (^ _Nonnull)(NSInteger))completionHandler;
+ (void)addUsingCompletionCallback:(NSInteger)a :(NSInteger)b :(void (* _Nonnull)(NSInteger))completionHandler;
@end

All four class methods are visible. [And marvel at those empty parameter labels, a little-known but still valid Objective-C construct (-: ]

Contrast with this type:

public class HackCPP {
public class func addSync(_ a: Int, _ b: Int) -> Int {
return a + b
}
public class func addAsync(_ a: Int, _ b: Int) async -> Int {
return a + b
}
public class func addUsingCompletionHandler(_ a: Int, _ b: Int, _ completionHandler: @escaping (_ result: Int) -> Void) {
completionHandler(a + b)
}
public class func addUsingCompletionCallback(_ a: Int, _ b: Int, _ completionHandler: @convention(c) @escaping (_ result: Int) -> Void) {
completionHandler(a + b)
}
}

I’m not gonna show the full MyModlue-Swift.h representation but, as you’ve noticed, only the addSync(…) method comes across.

Given the above, my immediate advice is to use Objective-C++ to do this bridging.

Share and Enjoy

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

[1] Actually, I think it uses an unchecked continuation, but that’s just an implementation detail.

[2] We have an internal bug about this (r. 119633247). I wasn’t able to find an equivalent open source issue. It wouldn’t hurt for you to file that yourself.

FYI in the meantime using original storekit 1, it works just fine from C++ as a class with the old SK functions and calls

Written by Roadmaster1975 in 783205021
What am I missing?

Well, you could argue that Swift is missing an interop feature for exporting async functions to C++ |-: If you want to raise that with the Swift folks then my advice is that you wade into the Swift Evolution process. OTOH, if you just want to solve your current problem, that’s something we can discuss here.

First, some background. When Swift imports Objective-C APIs, it looks for methods with a completion handler and imports each one twice: First as a synchronous method with a completion handler and again as an async method. For example, the -[NSSavePanel beginWithCompletionHandler:] method method gets imported as:

  • func begin(completionHandler handler: @escaping (NSApplication.ModalResponse) -> Void)

  • func begin() async -> NSApplication.ModalResponse

The compiler synthesises an implementation of the latter by combining the former with a checked continuation [1].

This synthesis also works the other way. In this post — it’s not an accident it’s also about StoreKit! — I show how Swift can export an async method to Objective-C as a method that takes a completion handler.

AFAICT this feature is simply missing for C++ interoperability [2]. Consider this type:

public class HackObjC: NSObject {
@objc
public class func addSync(_ a: Int, _ b: Int) -> Int {
return a + b
}
@objc
public class func addAsync(_ a: Int, _ b: Int) async -> Int {
return a + b
}
@objc
public class func addUsingCompletionHandler(_ a: Int, _ b: Int, _ completionHandler: @escaping (_ result: Int) -> Void) {
completionHandler(a + b)
}
@objc
public class func addUsingCompletionCallback(_ a: Int, _ b: Int, _ completionHandler: @convention(c) @escaping (_ result: Int) -> Void) {
completionHandler(a + b)
}
}

If you open the MyModlue-Swift.h file (I’m using Xcode 16.3) you see this:

@interface HackObjC : NSObject
+ (NSInteger)addSync:(NSInteger)a :(NSInteger)b SWIFT_WARN_UNUSED_RESULT;
+ (void)addAsync:(NSInteger)a :(NSInteger)b completionHandler:(void (^ _Nonnull)(NSInteger))completionHandler;
+ (void)addUsingCompletionHandler:(NSInteger)a :(NSInteger)b :(void (^ _Nonnull)(NSInteger))completionHandler;
+ (void)addUsingCompletionCallback:(NSInteger)a :(NSInteger)b :(void (* _Nonnull)(NSInteger))completionHandler;
@end

All four class methods are visible. [And marvel at those empty parameter labels, a little-known but still valid Objective-C construct (-: ]

Contrast with this type:

public class HackCPP {
public class func addSync(_ a: Int, _ b: Int) -> Int {
return a + b
}
public class func addAsync(_ a: Int, _ b: Int) async -> Int {
return a + b
}
public class func addUsingCompletionHandler(_ a: Int, _ b: Int, _ completionHandler: @escaping (_ result: Int) -> Void) {
completionHandler(a + b)
}
public class func addUsingCompletionCallback(_ a: Int, _ b: Int, _ completionHandler: @convention(c) @escaping (_ result: Int) -> Void) {
completionHandler(a + b)
}
}

I’m not gonna show the full MyModlue-Swift.h representation but, as you’ve noticed, only the addSync(…) method comes across.

Given the above, my immediate advice is to use Objective-C++ to do this bridging.

Share and Enjoy

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

[1] Actually, I think it uses an unchecked continuation, but that’s just an implementation detail.

[2] We have an internal bug about this (r. 119633247). I wasn’t able to find an equivalent open source issue. It wouldn’t hurt for you to file that yourself.

Calling StoreKit Swift from C++
 
 
Q