Unity 하이브리드 앱 연동 가이드

개요


Unity 하이브리드 앱 연동 가이드에 대한 내용입니다. Unity 에서 WebView 를 사용하기 위해 다양한 플러그인을 사용 할 수 있습니다. 각각의 플러그인 마다 하이브리드 앱 연동 코드 형태가 다르므로 Javascript Interface 를 활용하기 위한 메서드를 제공합니다.

interface

AirbridgeWebInterface webInterface;
webInterface = AirbridgeUnity.CreateWebInterface(
	"YOUR_WEB_TOKEN",     // web token
	(msg) => $@"..."      // post command function
);

📘

YOUR_WEB_TOKEN 은 대시보드의 Settings > Tokens > 웹 SDK 토큰 에서 확인할 수 있습니다.

public interface AirbridgeWebInterface
{
	string Script { get; }

	void Handle(string message);
}

이용가이드

아래는 많이 사용되는 plugin 3가지에 대한 연동에 대한 내용입니다.

  • gree/unity-webview
  • UniWebView
  • GPM (Game Package Manger)

gree/unity-webview 바로가기

WebViewObject webViewObject;
AirbridgeWebInterface webInterface;

public void Display()
{
	webInterface = AirbridgeUnity.CreateWebInterface(
		"YOUR_WEB_TOKEN",
		// postCommandFunction에 WebView에서 Unity로 메세지를 보내는 Javascript를 작성한다.
		(msg) => $@"
if (window && window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.unityControl) {{
	window.webkit.messageHandlers.unityControl.postMessage({msg});
}} else {{
	var iframe = document.createElement('IFRAME');
	iframe.setAttribute('src', 'unity:' + {msg});
	document.documentElement.appendChild(iframe);
	iframe.parentNode.removeChild(iframe);
	iframe = null;
}}");
  
  
	webViewObject.Init(
		cb: (msg) =>
		{
			// do something
    
			// WebView에서 Unity로 전달된 메세지를 Airbridge Unity SDK에서 처리할 수 있도록 설정한다.
			webInterface.Handle(msg);
		},
		err: (msg) => { /* do something */ },
		httpErr: (msg) => { /* do something */ },
		started: (msg) =>
		{
			// do something
    
			// JavaScript must be injected before the website loads.
#if UNITY_IOS
			webViewObject.EvaluateJS(webInterface.Script);
#endif
		},
		hooked: (msg) => { /* do something */ },
		ld: (msg) =>
		{
			// do something
    
			webViewObject.EvaluateJS(webInterface.Script);
		},
		...
	);
  ...
}

UniWebView 바로가기

<script>
(function(a_,i_,r_,_b,_r,_i,_d,_g,_e){if(!a_[_b]){var d={queue:[],get isSDKEnabled(){return!1}};_r.concat(_i).forEach(function(a){var h=a.split("."),n=h.pop();h.reduce(function(k,l){return k[l]=k[l]||{}},d)[n]=function(){d.queue.push([a,arguments])}});a_[_b]=d;a_=i_.getElementsByTagName(r_)[0];i_=i_.createElement(r_);i_.onerror=function(){d.queue.filter(function(a){return 0<=_i.indexOf(a[0])}).forEach(function(a){a=a[1];a=a[a.length-1];"function"===typeof a&&a("error occur when load airbridge")})};i_.async=1;i_.src="//static.airbridge.io/sdk/latest/airbridge.min.js";a_.parentNode.insertBefore(i_,a_)}})(window,document,"script","airbridge","init startTracking fetchResource setBanner setDownload setDownloads openDeeplink setDeeplinks sendWeb setUserAgent setUserAlias addUserAlias setMobileAppData setUserId setUserEmail setUserPhone setUserAttributes clearUser setDeviceAlias removeDeviceAlias clearDeviceAlias setDeviceIFV setDeviceIFA setDeviceGAID events.send events.signIn events.signUp events.signOut events.purchased events.addedToCart events.productDetailsViewEvent events.homeViewEvent events.productListViewEvent events.searchResultViewEvent".split(" "),["events.wait","createTouchpoint"]); 

function initAirbridge() {
	if (typeof(AirbridgeNative) !== "undefined") {
		airbridge.init({
			app: '앱 이름',
			webToken: '웹 SDK 토큰'
		});
	}
}
initAirbridge();
</script>

🚧

iOS 의 경우 자바스크립트 인젝트 시점이 Web SDK init 이후가 될 수 있습니다. 따라서 웹 페이지 코드를 위와 같이 변경하여 사용해주세요.


UniWebView webView;
AirbridgeWebInterface webInterface;

// 로드되는 웹 페이지의 Airbridge SDK init을 수행하는 Javascript를 작성한다.
string InitAirbridgeScript
{
	get
	{
		return "initAirbridge();";
	}
}

public void Display()
{
	webInterface = AirbridgeUnity.CreateWebInterface(
		"YOUR_WEB_TOKEN",
		// postCommandFunction에 WebView에서 Unity로 메세지를 보내는 Javascript를 작성한다.
		(msg) => $@"window.location.href = 'uniwebview://airbridge?msg=' + {msg}"
	);

	webView.OnMessageReceived += (view, message) =>
	{
		// WebView에서 Unity로 전달된 메세지를 Airbridge Unity SDK에서 처리할 수 있도록 설정한다.
		if (message.Path.Equals("airbridge")) 
		{
			string msg = message.Args["msg"];
			webInterface.Handle(msg);
		}
	};

	webView.OnPageFinished += (view, statusCode, url) =>
	{
		InjectAirbridgeJavascript();
	};

	...
	webView.Show();
}

private void InjectAirbridgeJavascript()
{
	webView.EvaluateJavaScript(CreateSupportedJavascript(webInterface.Script), (payload) => 
	{
		if (payload.resultCode.Equals("0")) 
		{
			Debug.Log("Airbridge javascript injection succeeded");
#if UNITY_IOS
			webView.EvaluateJavaScript(CreateSupportedJavascript(InitAirbridgeScript), (payload) =>
			{
				if (payload.resultCode.Equals("0")) 
				{
					Debug.Log("Airbridge initialization succeeded");
				} 
				else 
				{
					Debug.Log("Airbridge initialization failed: " + payload.data);
				}
			});
#endif
		} 
		else 
		{
			Debug.Log("Airbridge javascript injection failed: " + payload.data);
		}
	});
}

private string CreateSupportedJavascript(string script)
{
#if UNITY_IOS
	script += "'ios';";   
#endif
	return script;
}

GPM (Game Package Manger) 바로가기

<script>
(function(a_,i_,r_,_b,_r,_i,_d,_g,_e){if(!a_[_b]){var d={queue:[],get isSDKEnabled(){return!1}};_r.concat(_i).forEach(function(a){var h=a.split("."),n=h.pop();h.reduce(function(k,l){return k[l]=k[l]||{}},d)[n]=function(){d.queue.push([a,arguments])}});a_[_b]=d;a_=i_.getElementsByTagName(r_)[0];i_=i_.createElement(r_);i_.onerror=function(){d.queue.filter(function(a){return 0<=_i.indexOf(a[0])}).forEach(function(a){a=a[1];a=a[a.length-1];"function"===typeof a&&a("error occur when load airbridge")})};i_.async=1;i_.src="//static.airbridge.io/sdk/latest/airbridge.min.js";a_.parentNode.insertBefore(i_,a_)}})(window,document,"script","airbridge","init startTracking fetchResource setBanner setDownload setDownloads openDeeplink setDeeplinks sendWeb setUserAgent setUserAlias addUserAlias setMobileAppData setUserId setUserEmail setUserPhone setUserAttributes clearUser setDeviceAlias removeDeviceAlias clearDeviceAlias setDeviceIFV setDeviceIFA setDeviceGAID events.send events.signIn events.signUp events.signOut events.purchased events.addedToCart events.productDetailsViewEvent events.homeViewEvent events.productListViewEvent events.searchResultViewEvent".split(" "),["events.wait","createTouchpoint"]); 

function initAirbridge() {
	if (typeof(AirbridgeNative) !== "undefined") {
		airbridge.init({
			app: '앱 이름',
			webToken: '웹 SDK 토큰'
		});
	}
}
initAirbridge();
</script>

🚧

iOS 의 경우 자바스크립트 인젝트 시점이 Web SDK init 이후가 될 수 있습니다. 따라서 웹 페이지 코드를 위와 같이 변경하여 사용해주세요.


AirbridgeWebInterface webInterface;

// 로드되는 웹 페이지의 Airbridge SDK init을 수행하는 Javascript를 작성한다.
string InitAirbridgeScript
{
	get
	{
		return "initAirbridge();";
	}
}

public void Display()
{
	webInterface = AirbridgeUnity.CreateWebInterface(
		"YOUR_WEB_TOKEN",
		// postCommandFunction에 WebView에서 Unity로 메세지를 보내는 Javascript를 작성한다.
		(msg) => $@"window.location.href = 'airbridge://?msg=' + {msg}"
	);

	GpmWebView.ShowUrl(
		"WEB_URL",
		new GpmWebViewRequest.Configuration() { ... },
		OnCallback,
		new List<string>()
		{
			"airbridge"    // PUT AIRBRIDGE SCHEME HERE
		}
	);
}


private void OnCallback(
	GpmWebViewCallback.CallbackType callbackType,
	string data,
	GpmWebViewError error
) 
{
	switch (callbackType)
	{
		case GpmWebViewCallback.CallbackType.PageLoad:
			if (string.IsNullOrEmpty(data) == false)
			{
				// do something
				
				GpmWebView.ExecuteJavaScript(webInterface.Script);
#if UNITY_IOS
				GpmWebView.ExecuteJavaScript(InitAirbridgeScript);
#endif
			}
			break;
		case GpmWebViewCallback.CallbackType.Scheme:
			if (error == null)
			{
				Uri uri = new Uri(data);
				// WebView에서 Unity로 전달된 메세지를 Airbridge Unity SDK에서 처리할 수 있도록 설정한다.
				if (uri.Scheme.Equals("airbridge"))
				{
					try
					{
						string message = HttpUtility.ParseQueryString(uri.Query)["msg"];
						string decodedMessage = HttpUtility.UrlDecode(message);
						webInterface.Handle(decodedMessage);
					}
					catch (Exception e)
					{
						Console.WriteLine("Fail to receive message. Error: {0}", e.Message);
					}
				}
			}
			else
			{
				Debug.Log(string.Format("Fail to custom scheme. Error: {0}", error));
			}
			break;
		...
	}
}