Skip to content

Commit f275a1f

Browse files
committed
feat: add macos vuplex webview
1 parent 13fc6b5 commit f275a1f

File tree

3 files changed

+243
-5
lines changed

3 files changed

+243
-5
lines changed
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
#nullable enable
2+
using System;
3+
using System.Collections.Generic;
4+
using Cysharp.Threading.Tasks;
5+
using UnityEngine;
6+
using UnityEngine.UI;
7+
using Immutable.Passport.Core.Logging;
8+
9+
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
10+
using Vuplex.WebView;
11+
#endif
12+
13+
namespace Immutable.Passport
14+
{
15+
/// <summary>
16+
/// MacOS implementation of IPassportWebView using Vuplex WebView
17+
/// Provides embedded WebView functionality within the Unity app
18+
/// Similar to iOS implementation but optimized for MacOS desktop environment
19+
/// </summary>
20+
public class MacOSPassportWebView : IPassportWebView
21+
{
22+
private const string TAG = "[MacOSPassportWebView]";
23+
24+
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
25+
private CanvasWebViewPrefab? _webViewPrefab;
26+
#endif
27+
private readonly Dictionary<string, Action<string>> _jsHandlers = new Dictionary<string, Action<string>>();
28+
private readonly RawImage _canvasReference;
29+
private bool _isInitialized = false;
30+
31+
public event Action<string>? OnJavaScriptMessage;
32+
public event Action? OnLoadFinished;
33+
public event Action? OnLoadStarted;
34+
35+
// Safe access - check initialization
36+
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
37+
public bool IsVisible => _webViewPrefab?.Visible ?? false;
38+
public string CurrentUrl => _webViewPrefab?.WebView?.Url ?? "";
39+
#else
40+
public bool IsVisible => false;
41+
public string CurrentUrl => "";
42+
#endif
43+
44+
public MacOSPassportWebView(RawImage canvasReference)
45+
{
46+
_canvasReference = canvasReference ?? throw new ArgumentNullException(nameof(canvasReference));
47+
}
48+
49+
public void Initialize(PassportWebViewConfig config)
50+
{
51+
if (_isInitialized)
52+
{
53+
PassportLogger.Warn($"{TAG} Already initialized, skipping");
54+
return;
55+
}
56+
57+
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
58+
try
59+
{
60+
PassportLogger.Info($"{TAG} Initializing MacOS WebView...");
61+
62+
// Start async initialization but don't wait
63+
InitializeAsync(config).Forget();
64+
}
65+
catch (Exception ex)
66+
{
67+
PassportLogger.Error($"{TAG} Failed to initialize: {ex.Message}");
68+
throw;
69+
}
70+
#else
71+
PassportLogger.Warn($"{TAG} Vuplex WebView is only supported on MacOS builds, not in editor");
72+
_isInitialized = true;
73+
#endif
74+
}
75+
76+
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
77+
private async UniTaskVoid InitializeAsync(PassportWebViewConfig config)
78+
{
79+
try
80+
{
81+
// Create WebView prefab and parent to Canvas
82+
_webViewPrefab = CanvasWebViewPrefab.Instantiate();
83+
_webViewPrefab.Native2DModeEnabled = false; // Use standard mode for better desktop compatibility
84+
85+
// Set higher resolution for desktop - MacOS can handle larger textures
86+
_webViewPrefab.Resolution = 1.5f; // 1.5px per Unity unit for crisp rendering
87+
88+
// Must be child of Canvas for Vuplex to work
89+
_webViewPrefab.transform.SetParent(_canvasReference.canvas.transform, false);
90+
91+
// Set desktop-appropriate size - larger than mobile but not full-screen
92+
var rect = _webViewPrefab.GetComponent<RectTransform>();
93+
rect.anchorMin = new Vector2(0.5f, 0.5f); // Center anchor
94+
rect.anchorMax = new Vector2(0.5f, 0.5f);
95+
rect.sizeDelta = new Vector2(1200, 800); // Desktop size: 1200x800 for comfortable login UX
96+
rect.anchoredPosition = Vector2.zero; // Center position
97+
98+
// Wait for WebView initialization
99+
await _webViewPrefab.WaitUntilInitialized();
100+
101+
// Setup event handlers
102+
_webViewPrefab.WebView.LoadProgressChanged += (s, e) =>
103+
{
104+
if (e.Type == ProgressChangeType.Started)
105+
{
106+
OnLoadStarted?.Invoke();
107+
}
108+
else if (e.Type == ProgressChangeType.Finished)
109+
{
110+
OnLoadFinished?.Invoke();
111+
}
112+
};
113+
_webViewPrefab.WebView.MessageEmitted += (s, e) =>
114+
{
115+
foreach (var h in _jsHandlers)
116+
{
117+
if (e.Value.StartsWith($"{h.Key}:"))
118+
{
119+
h.Value?.Invoke(e.Value.Substring(h.Key.Length + 1));
120+
return;
121+
}
122+
}
123+
124+
OnJavaScriptMessage?.Invoke(e.Value);
125+
};
126+
_webViewPrefab.WebView.LoadFailed += (s, e) => PassportLogger.Warn($"{TAG} Load failed: {e.NativeErrorCode} for {e.Url}");
127+
128+
_isInitialized = true;
129+
PassportLogger.Info($"{TAG} MacOS WebView initialized successfully");
130+
}
131+
catch (Exception ex)
132+
{
133+
PassportLogger.Error($"{TAG} Failed to initialize MacOS WebView: {ex.Message}");
134+
throw;
135+
}
136+
}
137+
#endif
138+
139+
public void LoadUrl(string url)
140+
{
141+
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
142+
if (!_isInitialized || _webViewPrefab?.WebView == null)
143+
{
144+
PassportLogger.Error($"{TAG} Cannot load URL - MacOS WebView not initialized");
145+
return;
146+
}
147+
148+
PassportLogger.Info($"{TAG} Loading URL: {url}");
149+
_webViewPrefab.WebView.LoadUrl(url);
150+
#else
151+
PassportLogger.Warn($"{TAG} LoadUrl not supported in MacOS editor mode");
152+
#endif
153+
}
154+
155+
public void Show()
156+
{
157+
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
158+
if (_webViewPrefab != null)
159+
{
160+
_webViewPrefab.Visible = true;
161+
PassportLogger.Info($"{TAG} WebView shown");
162+
}
163+
#else
164+
PassportLogger.Info($"{TAG} Show() called (editor mode)");
165+
#endif
166+
}
167+
168+
public void Hide()
169+
{
170+
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
171+
if (_webViewPrefab != null)
172+
{
173+
_webViewPrefab.Visible = false;
174+
PassportLogger.Info($"{TAG} WebView hidden");
175+
}
176+
#else
177+
PassportLogger.Info($"{TAG} Hide() called (editor mode)");
178+
#endif
179+
}
180+
181+
public void ExecuteJavaScript(string js)
182+
{
183+
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
184+
if (!_isInitialized || _webViewPrefab?.WebView == null)
185+
{
186+
PassportLogger.Error($"{TAG} Cannot execute JavaScript - MacOS WebView not initialized");
187+
return;
188+
}
189+
190+
_webViewPrefab.WebView.ExecuteJavaScript(js);
191+
#else
192+
PassportLogger.Warn($"{TAG} ExecuteJavaScript not supported in MacOS editor mode");
193+
#endif
194+
}
195+
196+
public void RegisterJavaScriptMethod(string methodName, Action<string> handler)
197+
{
198+
_jsHandlers[methodName] = handler;
199+
PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered");
200+
201+
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
202+
if (_isInitialized && _webViewPrefab?.WebView != null)
203+
{
204+
// Register the method with Vuplex WebView using window.vuplex.postMessage
205+
string jsCode = $"window.{methodName}=d=>window.vuplex?.postMessage('{methodName}:'+(typeof d==='object'?JSON.stringify(d):d))";
206+
ExecuteJavaScript(jsCode);
207+
PassportLogger.Info($"{TAG} JavaScript method '{methodName}' registered with Vuplex");
208+
}
209+
#endif
210+
}
211+
212+
public void Dispose()
213+
{
214+
#if UNITY_STANDALONE_OSX && !UNITY_EDITOR
215+
if (_webViewPrefab != null)
216+
{
217+
PassportLogger.Info($"{TAG} Disposing MacOS WebView");
218+
_webViewPrefab.Destroy();
219+
_webViewPrefab = null;
220+
}
221+
#endif
222+
223+
_jsHandlers.Clear();
224+
_isInitialized = false;
225+
}
226+
}
227+
}

src/Packages/Passport/Runtime/Scripts/Private/UI/MacOSPassportWebView.cs.meta

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Packages/Passport/Runtime/Scripts/Public/PassportUI.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ public override string ToString()
3737
/// Cross-platform WebView UI component for Passport authentication.
3838
/// Automatically selects the appropriate WebView implementation based on the target platform:
3939
/// - Windows: Unity Web Browser (UWB) with Chromium CEF
40-
/// - iOS/Android: Gree unity-webview with external browser integration
41-
/// - macOS: Not yet implemented
40+
/// - iOS/Android: Vuplex WebView with embedded browser
41+
/// - macOS: Vuplex WebView with embedded browser
4242
///
4343
/// SETUP: When configuring the PassportUI prefab in the editor:
4444
/// - Set RawImage component's width and height to 0 in the RectTransform
@@ -207,9 +207,8 @@ private IPassportWebView CreatePlatformWebView()
207207
PassportLogger.Info($"{TAG} Creating Android WebView (Vuplex)");
208208
return new AndroidVuplexWebView(rawImage);
209209
#elif UNITY_STANDALONE_OSX
210-
PassportLogger.Info($"{TAG} Creating macOS WebView (Vuplex)");
211-
// TODO: Implement macOS WebView
212-
throw new NotImplementedException("macOS WebView not yet implemented");
210+
PassportLogger.Info($"{TAG} Creating MacOS WebView (Vuplex)");
211+
return new MacOSPassportWebView(rawImage);
213212
#else
214213
PassportLogger.Error($"{TAG} WebView not supported on this platform");
215214
return null;

0 commit comments

Comments
 (0)