convert iOS to MAC

Hello,
I'm using the following class for auth in xamarin-ios, but I'm unable to use it xamarin-macos because SFSafariViewController, SFSafariViewControllerDelegate and UIViewController cannot be resolved.
Can anyone help me and tell me what classes should I use to convert this for mac support

public class PlatformWebView : SFSafariViewControllerDelegate, IBrowser
    {
    private SafariServices.SFSafariViewController m_Safari;
    private readonly UIViewController r_Controller;

    public PlatformWebView(UIViewController i_Controller)
    {
        r_Controller = i_Controller;
    }

    public override void DidFinish(SFSafariViewController i_Controller)
    {
        ActivityMediator.Instance.Send("UserCancel");
    }

    public Task<BrowserResult> InvokeAsync(BrowserOptions i_Options)
    {
        if (string.IsNullOrWhiteSpace(i_Options.StartUrl))
        {
            throw new ArgumentException("Missing StartUrl", nameof(i_Options));
        }

        if (string.IsNullOrWhiteSpace(i_Options.EndUrl))
        {
            throw new ArgumentException("Missing EndUrl", nameof(i_Options));
        }

        // must be able to wait for the intent to be finished to continue
        // with setting the task result
        TaskCompletionSource<BrowserResult> tcs = new TaskCompletionSource<BrowserResult>();

        // create Safari controller
        m_Safari = new SafariServices.SFSafariViewController(new NSUrl(i_Options.StartUrl));
        m_Safari.Delegate = this;

        ActivityMediator.MessageReceivedEventHandler callback = null;
        callback = async (i_Response) =>
        {
            // remove handler
            ActivityMediator.Instance.ActivityMessageReceived -= callback;

            if (i_Response == "UserCancel")
            {
                tcs.SetResult(new BrowserResult
                {
                    ResultType = BrowserResultType.UserCancel
                });
            }
            else
            {
                // Close Safari
                await m_Safari.DismissViewControllerAsync(true);

                // set result
                tcs.SetResult(new BrowserResult
                {
                    Response = i_Response,
                    ResultType = BrowserResultType.Success
                });
            }
        };

        // attach handler
        ActivityMediator.Instance.ActivityMessageReceived += callback;

        // launch Safari
        r_Controller.PresentViewController(m_Safari, true, null);

        // need an intent to be triggered when browsing to the "io.identitymodel.native://callback"
        // scheme/URI => CallbackInterceptorActivity
        return tcs.Task;
    }
}

public class ActivityMediator
    {
    public delegate void MessageReceivedEventHandler(string i_Message);

    private static ActivityMediator s_Instance;

    public static ActivityMediator Instance
    {
        get
        {
            if (s_Instance == null)
            {
                s_Instance = new ActivityMediator();
            }

            return s_Instance;
        }
    }

    private ActivityMediator()
    {
    }

    public event MessageReceivedEventHandler ActivityMessageReceived;

    public void Send(string i_Response)
    {
        ActivityMessageReceived?.Invoke(i_Response);
    }
}

Posts

  • ChrisHamonsChrisHamons USXamarin Team Xamurai

    Look into https://developer.apple.com/documentation/webkit/wkwebview for displaying web content.

    In general many but not all UIFoo types have a NSFoo equivalent on macOS. This depends on Apple providing it, obviously. UIViewController => NSViewController in this case.

  • Hi Chris,
    Thanks for the answer, WKWebView need a CGRect in it's ctor I tried to pass it the NSWindow frame property from the AppDelegate.
    The WKWebView is created but I'm nut sure how to display it, I'm using xamarin forms and I want to show the browser before the xaml mainpage is loaded or using a popup somehow in order for the user to login with Auth0.
    How can I do it, I tried using AddSubView but I only see the contenet in the xaml page?

  • ChrisHamonsChrisHamons USXamarin Team Xamurai

    It sounds like you are new to Xamarin.Mac development. You can take a look at the resources linked here: https://forums.xamarin.com/discussion/comment/306182/#Comment_306182

    but the rough structure of adding a view in code is:

    NSView view; // Normally a custom or specific view
    public void Foo () // Where we want to add it. Often some proc after view is in visual tree
    {
        view = new NSView (new CGRect ()) // This is either a hard coded size or the superview's Bounds if you want it to cover the entire area
        View.AddSubView (view); // Add it to whatever view you have, to get it in the visual tree
    }
    
  • Hi,
    yes I'm new to xamarin.mac I only used xamarin.forms with android, uwp and ios.
    I tried to just show a button but still I don't see it.
    on AppDelegate I pass a reference to the mainwindow contentview

    public override NSWindow MainWindow
        {
            get { return m_Window; }
        }
    
        public override void DidFinishLaunching(NSNotification notification)
        {
            Forms.Init();
            Auth0MacClientOptions options = new Auth0MacClientOptions()
            {
                Domain = "",
                ClientId = "",
                Controller = MainWindow.ContentView,
                Scope = "openid profile offline_access"
            };
            Auth0Client client = new Auth0MacClient(options);
    
            LoadApplication(new App(client));
            base.DidFinishLaunching(notification);
        }
    

    on App.xaml.cs I call
    public App(Auth0Client i_Client)
    {
    InitializeComponent();

            MainPage = new Sirona.Management.MainPage();
            r_Client = i_Client;
        }
    
        protected async override void OnStart()
        {
            // Handle when your app starts
            var a = await r_Client.LoginAsync(new { audience = "" });
        }
    

    the login function calls the PlatformWebView class and it gets the NSView as a parameter
    you can see that to test it I tried to just show a button but i still only see the xaml content.

    the end result is to show the user the browser, complete the login process and return to xaml page

    public class PlatformWebView : WKNavigationDelegate, IBrowser
    {
        private WKWebView m_WebView;
        private readonly NSView r_View;
    
        public PlatformWebView(NSView i_View)
        {
            r_View = i_View;
        }
    
        public override void DidFinishNavigation(WKWebView i_WebView, WKNavigation i_Navigation)
        {
            //ActivityMediator.Instance.Send("UserCancel");
        }
    
        public Task<BrowserResult> InvokeAsync(BrowserOptions i_Options)
        {
            if (string.IsNullOrWhiteSpace(i_Options.StartUrl))
            {
                throw new ArgumentException("Missing StartUrl", nameof(i_Options));
            }
    
            if (string.IsNullOrWhiteSpace(i_Options.EndUrl))
            {
                throw new ArgumentException("Missing EndUrl", nameof(i_Options));
            }
    
            // must be able to wait for the intent to be finished to continue
            // with setting the task result
            TaskCompletionSource<BrowserResult> tcs = new TaskCompletionSource<BrowserResult>();
    
            // create Safari controller
            WKWebViewConfiguration configuration = new WKWebViewConfiguration();
            m_WebView = new WKWebView(new CGRect (0, 0, 100, 30), configuration);
            NSUrlRequest urlRequest = new NSUrlRequest(new NSUrl(i_Options.StartUrl));
            m_WebView.LoadRequest(urlRequest);
            m_WebView.NavigationDelegate = this;
    
            ActivityMediator.MessageReceivedEventHandler callback = null;
            callback = (i_Response) =>
            {
                // remove handler
                ActivityMediator.Instance.ActivityMessageReceived -= callback;
    
                if (i_Response == "UserCancel")
                {
                    tcs.SetResult(new BrowserResult
                    {
                        ResultType = BrowserResultType.UserCancel
                    });
                }
                else
                {
                    // set result
                    tcs.SetResult(new BrowserResult
                    {
                        Response = i_Response,
                        ResultType = BrowserResultType.Success
                    });
                }
            };
    
            // attach handler
            ActivityMediator.Instance.ActivityMessageReceived += callback;
    
            // launch Safari
    var ClickMeButton = new NSButton(new CGRect(10, 10, 100, 30))
            {
                AutoresizingMask = NSViewResizingMask.MinYMargin,
    
            };
            ClickMeButton.Title = "test";
    
            r_View.AddSubview(ClickMeButton);
    
    
            // need an intent to be triggered when browsing to the "io.identitymodel.native://callback"
            // scheme/URI => CallbackInterceptorActivity
            return tcs.Task;
        }
    }
    
  • Hi,
    yes I'm new to xamarin.mac I only used xamarin.forms with android, uwp and ios.
    I tried to just show a button but still I don't see it.
    on AppDelegate I pass a reference to the mainwindow contentview

    public override NSWindow MainWindow
        {
            get { return m_Window; }
        }
    
        public override void DidFinishLaunching(NSNotification notification)
        {
            Forms.Init();
            Auth0MacClientOptions options = new Auth0MacClientOptions()
            {
                Domain = "",
                ClientId = "",
                Controller = MainWindow.ContentView,
                Scope = "openid profile offline_access"
            };
            Auth0Client client = new Auth0MacClient(options);
    
            LoadApplication(new App(client));
            base.DidFinishLaunching(notification);
        }
    

    on App.xaml.cs I call
    public App(Auth0Client i_Client)
    {
    InitializeComponent();

            MainPage = new Sirona.Management.MainPage();
            r_Client = i_Client;
        }
    
        protected async override void OnStart()
        {
            // Handle when your app starts
            var a = await r_Client.LoginAsync(new { audience = "" });
        }
    

    the login function calls the PlatformWebView class and it gets the NSView as a parameter
    you can see that to test it I tried to just show a button but i still only see the xaml content.
    the end result is to show the user the browser, complete the login process and return to xaml page

    public class PlatformWebView : WKNavigationDelegate, IBrowser
    {
        private WKWebView m_WebView;
        private readonly NSView r_View;
    
        public PlatformWebView(NSView i_View)
        {
            r_View = i_View;
        }
    
        public override void DidFinishNavigation(WKWebView i_WebView, WKNavigation i_Navigation)
        {
            //ActivityMediator.Instance.Send("UserCancel");
        }
    
        public Task<BrowserResult> InvokeAsync(BrowserOptions i_Options)
        {
            if (string.IsNullOrWhiteSpace(i_Options.StartUrl))
            {
                throw new ArgumentException("Missing StartUrl", nameof(i_Options));
            }
    
            if (string.IsNullOrWhiteSpace(i_Options.EndUrl))
            {
                throw new ArgumentException("Missing EndUrl", nameof(i_Options));
            }
    
            // must be able to wait for the intent to be finished to continue
            // with setting the task result
            TaskCompletionSource<BrowserResult> tcs = new TaskCompletionSource<BrowserResult>();
    
            // create Safari controller
            WKWebViewConfiguration configuration = new WKWebViewConfiguration();
            m_WebView = new WKWebView(new CGRect (0, 0, 100, 30), configuration);
            NSUrlRequest urlRequest = new NSUrlRequest(new NSUrl(i_Options.StartUrl));
            m_WebView.LoadRequest(urlRequest);
            m_WebView.NavigationDelegate = this;
    
            ActivityMediator.MessageReceivedEventHandler callback = null;
            callback = (i_Response) =>
            {
                // remove handler
                ActivityMediator.Instance.ActivityMessageReceived -= callback;
    
                if (i_Response == "UserCancel")
                {
                    tcs.SetResult(new BrowserResult
                    {
                        ResultType = BrowserResultType.UserCancel
                    });
                }
                else
                {
                    // set result
                    tcs.SetResult(new BrowserResult
                    {
                        Response = i_Response,
                        ResultType = BrowserResultType.Success
                    });
                }
            };
    
            // attach handler
            ActivityMediator.Instance.ActivityMessageReceived += callback;
    
            // launch Safari
    var ClickMeButton = new NSButton(new CGRect(10, 10, 100, 30))
            {
                AutoresizingMask = NSViewResizingMask.MinYMargin,
    
            };
            ClickMeButton.Title = "test";
    
            r_View.AddSubview(ClickMeButton);
    
    
            // need an intent to be triggered when browsing to the "io.identitymodel.native://callback"
            // scheme/URI => CallbackInterceptorActivity
            return tcs.Task;
        }
    }
    
Sign In or Register to comment.