Engine Wrappers
Unity and Unreal Engine games use the official wrappers: stash-unity and stash-unreal. For another engine, add a small native glue file per platform that forwards calls to the SDK and events back to the engine.
sequenceDiagram
participant Game as Game thread
participant Glue as Native glue
participant SDK as Stash Native
Game->>Glue: Open a presentation
Glue->>SDK: Open call on the main thread
SDK-->>Glue: Callback on the main thread
Glue-->>Game: Queue the event
Rules
- Pass the engine’s current activity or root view controller to open calls.
- Set the receiver on the main thread before the first open, and clear it there on shutdown.
- Copy each callback payload and queue it for the game thread.
- Forward every callback, in order.
- Leave orientation to the SDK.
- On iOS, read
isCurrentlyPresentedandisPurchaseProcessingon the main thread. On Android, they work from any thread.
Android
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import com.stash.stashnative.StashNative;
public final class StashWrapper extends StashNative.StashNativeListenerAdapter {
private static final StashWrapper INSTANCE = new StashWrapper();
private static final Handler MAIN = new Handler(Looper.getMainLooper());
// Implemented in engine code; queues the event for the game thread.
private static native void nativeOnEvent(String name, String payload);
public static void initialize() {
MAIN.post(() -> StashNative.getInstance().setListener(INSTANCE));
}
public static void openCard(Activity activity, String url) {
MAIN.post(() -> StashNative.getInstance().openCard(activity, url, null));
}
@Override public void onPaymentSuccess(String order) { nativeOnEvent("paymentSuccess", order); }
@Override public void onCardDismissed() { nativeOnEvent("cardDismissed", null); }
// Forward the other events the same way.
}
| Engine | Activity | Event delivery |
|---|---|---|
| Unity | UnityPlayer.currentActivity | UnityPlayer.UnitySendMessage(gameObject, method, payload) |
| Unreal Engine | FJavaWrapper::GameActivityThis | JNI method that calls AsyncTask(ENamedThreads::GameThread, ...) |
Declare the AAR’s dependencies in an External Dependency Manager file (Unity) or in UPL <buildGradleAdditions> (Unreal).
iOS
#import <StashNative/StashNative.h>
typedef void (*StashEventCallback)(const char *name, const char *payload);
static StashEventCallback gCallback; // Copies the payload and queues it for the game thread.
extern UIViewController *StashEnginePresenter(void); // Supplied per engine.
@interface StashWrapperDelegate : NSObject <StashNativeDelegate>
@end
@implementation StashWrapperDelegate
- (void)stashNativeDidCompletePaymentWithOrder:(NSString *)order {
if (gCallback) gCallback("paymentSuccess", order.UTF8String);
}
- (void)stashNativeDidDismissCard {
if (gCallback) gCallback("cardDismissed", NULL);
}
// Forward the other events the same way.
@end
static StashWrapperDelegate *gDelegate; // Strong owner; the SDK does not retain the delegate.
extern "C" void StashWrapperInitialize(StashEventCallback callback) {
dispatch_async(dispatch_get_main_queue(), ^{
gCallback = callback;
if (!gDelegate) gDelegate = [[StashWrapperDelegate alloc] init];
[StashNative sharedInstance].delegate = gDelegate;
});
}
extern "C" void StashWrapperOpenCard(const char *url) {
NSString *pageURL = url ? [NSString stringWithUTF8String:url] : nil;
dispatch_async(dispatch_get_main_queue(), ^{
[[StashNative sharedInstance] openCardWithURL:pageURL
fromViewController:StashEnginePresenter()
config:nil];
});
}
| Engine | Presenter | Event delivery |
|---|---|---|
| Unity | UnityGetGLViewController() | UnitySendMessage(gameObject, method, payload) |
| Unreal Engine | [IOSAppDelegate GetDelegate].IOSController | Copy into an FString, then AsyncTask(ENamedThreads::GameThread, ...) |
StashNative.frameworkis dynamic. Embed and sign it.- The example uses ARC. Unreal compiles Objective-C without ARC unless the module sets
bEnableObjCAutomaticReferenceCounting = true; the SDK supports both.