Custom URL redirect from SFSafariViewController to UE4 app? ...FOnOpenURL delegate?

Hi folks,

I am trying to do a redirect from an HTML/JS page that is loaded inside a SFSafariViewController back to my UE4 app using a custom uri scheme. I am using this How to use SafariViewcontroller as Callback on some URL in Objective C - Mobikul more or less. The redirect is supposed to close the SFSafariViewController w/ additional information passed back in query params.

I can not get the redirect to work and/or SFSafariViewController does not close. I have validated that the uri scheme is defined correctly (in project settings additional plist data) by using Safari dev tools on my computer to invoke Javascript in the SFSafariViewController in my deployed app on the iOS device. Trying to redirect to a url w/ *window.location.href = ā€˜testing://?success=1ā€™ *, which is different than defined uri scheme, gives a ā€œSafari cannot open the page because the address is invalidā€, which is expected, and when redirecting to the url w/ window.location.href = ā€˜freedomofkeima://?success=1ā€™, I do not see this error, but I also do not see the SFSafariViewController close.

Does anyone know how to handle custom uri schemes? I am unsure if the delegate FOnOpenURL in UnrealEngine/Engine/Source/Runtime/Core/Public/IOS/IOSAppDelegate.h at master Ā· raysjoshua/UnrealEngine Ā· GitHub would be available to me or can be used (new to c++), and I am also not sure if the code from the example link in my app is modified correctly.

My additional plist data (my bundle id is com.mypackage.mygame):



<key>LSApplicationQueriesSchemes</key><array><string>freedomofkeima</string></array><key>CFBundleURLTypes</key><array><dict><key>CFBundleURLSchemes</key><array><string>freedomofkeima</string></array><key>CFBundleURLName</key><string>com.mypackage.mygame</string></dict></array><key>UIRequiresPersistentWiFi</key><true/>

My blueprint, which is run on game start:

BrowserBlueprint.cpp:



#include "BrowserBlueprint.h"
#include "Runtime/Engine/Classes/Kismet/KismetSystemLibrary.h"
#include "Engine/GameEngine.h"
#include "BrowserSettings.h"
#include "EngineUtils.h"
#include "Engine/World.h"

#if PLATFORM_IOS
#include "MyBrowserController.h"
#endif

UBrowserBlueprint::UBrowserBlueprint(const FObjectInitializer &ObjectInitializer) : Super(ObjectInitializer) {}

void UBrowserBlueprint::loadBrowserWithUrl() {
#if PLATFORM_IOS
  [MyBrowserController loadBrowserWithUrl];
#endif
}


MyBrowserController.h:


#pragmaonce

#if PLATFORM_IOS
#import <Foundation/Foundation.h>
#import <SafariServices/SafariServices.h>

@interface MyBrowserController : UIViewController <SFSafariViewControllerDelegate>
+ (void)loadBrowserWithUrl;

@end
#endif

MyBrowserController.cpp



#include "MyBrowserController.h"
#include "BrowserSettings.h"

#if PLATFORM_IOS
#include "IOSAppDelegate.h"
#import <Foundation/Foundation.h>
#import <SafariServices/SafariServices.h>
#endif

#if PLATFORM_IOS
@interface MyBrowserController()
@end

@implementation MyBrowserController

+ (MyBrowserController*)GetDelegate {
  static MyBrowserController *Singleton = [MyBrowserController alloc] init];

  return Singleton;
}

+ (void)loadBrowserWithUrl {
  [MyBrowserController GetDelegate] performSelectorOnMainThread:@selector(loadBrowserURLInIOSThread) withObject:nil waitUntilDone:NO];
}

- (void)loadBrowserURLInIOSThread {
  const UBrowserSettings *browserSettings = GetDefault<UBrowserSettings>();

  NSString *oUrlString = browserSettings->url.GetNSString();
  NSString *urlString = [NSString alloc] initWithString:UrlString];
  NSURL *url = [NSURL URLWithString:urlString];

  SFSafariViewController *safariVC = [SFSafariViewController alloc] initWithURL:url entersReaderIfAvailable:NO];
  safariVC.delegate = self;

  UINavigationController *navigationController = [UINavigationController alloc]initWithRootViewController:safariVC];
  [navigationController setNavigationBarHidden:YES animated:NO];

  [IOSAppDelegate GetDelegate].IOSController presentViewController:navigationController animated:YES completion:nil];
}

#pragma mark - SFSafariViewController delegate methods

-(void)safariViewController:SFSafariViewController *)controller didCompleteInitialLoad:BOOL)didLoadSuccessfully {
  //Load finished
}

-(void)safariViewControllerDidFinish:SFSafariViewController *)controller {
  //Done button pressed
}

- (BOOL)application:UIApplication *)app openURL:NSURL *)url options:NSDictionary<NSString *, id> *)options {
  NSString *sourceApplication = [options objectForKey:@"UIApplicationOpenURLOptionsSourceApplicationKey"];
  NSString *urlScheme = [url scheme];
  NSString *urlQuery = [url query];

  if ([urlScheme isEqualToString:@"freedomofkeima"] && [sourceApplication isEqualToString:@"com.apple.SafariViewService"]) {

    //TODO check for success query param

    NSLog(@"Value: %@", urlQuery);
    NSDictionary *data = [NSDictionary dictionaryWithObject:urlQuery forKey:@"key"];

    [NSNotificationCenter defaultCenter] postNotificationName:@"SafariCallback" object:self userInfo:data];

    return YES;
  }

  return NO;
}

- (void)safariCallback:NSNotification *)notification {
  [IOSAppDelegate GetDelegate].IOSController dismissViewControllerAnimated:NO completion:nil];
}

@end
#endif


Hey Man, I know its been long but need to ask, I solved it in android, did you get any luck solving it for IOS ?

hello , i am stuck in getting the redirect url for android could you pleas help me ?

i to make login functionality using OAuth2 so i send the player to the login page and after he login the api will send him back to the game using redirect uri that contain AuthorizationCode wich i need it for login him

I am struggling with getting app uri in android as well, I want to get app redirect working could you please help?

Hey Just saw your questions,

Here is the solution of how to open the callback URL in android, so my call back url is of type,


#if PLATFORM_ANDROID
#include <jni.h>                            
#include "Android/AndroidApplication.h"
#include "Android/AndroidJavaEnv.h"
#include "Android/AndroidJava.h"
#include "Android/AndroidJNI.h"          // For FJavaWrapper
#endif

**static** jmethodID javaMethodId;

FString ABeyondLoginSystemLatest::GetAccessTokenFromIntent(){

JNIEnv* env = FAndroidApplication::GetJavaEnv();

javaMethodId = FJavaWrapper::FindMethod(env, FJavaWrapper::GameActivityClassID, "GetAccessTokenIntent", "()Ljava/lang/String;", **false**);

jstring result = (jstring)FJavaWrapper::CallObjectMethod(env, FJavaWrapper::GameActivityThis, javaMethodId);

**const** **char*** str = env->GetStringUTFChars(result, **NULL**);

FString FStringResult(str);

AccessToken = FStringResult;

Success = **true**;

**return** FStringResult;

}

the above code is you put in your actor class, Search for the following file in you UE project file.

Now create a UPL android class in your project, and add the following code.

    Uri data = this.getIntent().getData();
    if (data != null){
        if(data.isHierarchical()){
            access_token = data.getQueryParameter("access_token");
            refresh_token = data.getQueryParameter("refresh_token");
            PrintAccessToken();
        }
    }
   </insert>

the above code will fetch the data from the url scheme.

public static String refresh_token; public static String access_token;
    public String GetAccessTokenIntent(){
        return access_token;
    }

    public String GetRefreshTokenIntent(){
        return refresh_token;
    }
    
  	</insert>

when calling the methods shared above it calles the above funtion to get the data.

1 Like

Thank you.