Search code examples
c#.netxamarinxamarin.formssalesforce

How to develop salesforce chatbot with xamarin forms


I want to develop Salesforce chatbot with xamarin forms application. I am not able to find what sdk or nuget package, I should use. There is no information seem to available regarding this.

This salesforce article, I have found, where they do it for Android and iOS.

I have tried it by using .jar files of android libraries but that doesn't work, for that iOS development also required separately. I have found this article but not sure what package and which platform they are developing.

How to do Salesforce Einstein Chatbot integration with Xamarin forms. Can anyone please help me regarding this?


Solution

  • Jar files, nuget packages doesn't work for me. I have just used webview with custom renderer. This is how I managed to integrate Salesforce chatbot

    public class SfChatbotViewModel : BaseViewModel
    {
        public SfChatbotViewModel()
        {
    
        }
    }  
    <controls:HybridWebView Margin="0" x:Name="webView"
        VerticalOptions="FillAndExpand"
        HorizontalOptions="FillAndExpand" />
    

    This is HybridWebView Webview control in Shared(common) project

        public class HybridWebView : Xamarin.Forms.WebView
        {
            Action<string> action;
            public static readonly BindableProperty UriProperty = BindableProperty.Create(
                propertyName: "Uri",
                returnType: typeof(string),
                declaringType: typeof(HybridWebView),
                defaultValue: default(string));
            public string Uri
            {
                get { return (string)GetValue(UriProperty); }
                set { SetValue(UriProperty, value); }
            }
            public void RegisterAction(Action<string> callback)
            {
                action = callback;
            }
            public void Cleanup()
            {
                action = null;
            }
            public void InvokeAction(string data)
            {
                if (action == null || data == null)
                {
                    return;
                }
                action.Invoke(data);
            }
        }
    Below `Webview renderer` in Android project
    
        public class HybridWebViewRenderer : WebViewRenderer
        {
            const string JavascriptFunction = "function initESW(data){jsBridge.invokeAction(data);}";
            Context _context;
            public HybridWebViewRenderer(Context context) : base(context)
            {
                _context = context;
            }
            protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
            {
                base.OnElementChanged(e);
                global::Android.Webkit.WebView.SetWebContentsDebuggingEnabled(true);
                if (e.OldElement != null)
                {
                    Control.RemoveJavascriptInterface("jsBridge");
                    ((HybridWebView)Element).Cleanup();
                }
                if (e.NewElement != null)
                {
                    CookieManager.Instance.SetAcceptCookie(true);
                    CookieManager.Instance.SetAcceptThirdPartyCookies(Control, true);
                    Control.SetWebViewClient(new JavascriptWebViewClient(this, $"javascript: {JavascriptFunction}"));
                    Control.AddJavascriptInterface(new JSBridge(this), "jsBridge");
                    Control.Settings.AllowFileAccess = true;
                    Control.Settings.JavaScriptEnabled = true;
                    Control.Settings.AllowFileAccessFromFileURLs = true;
                    Control.Settings.AllowUniversalAccessFromFileURLs = true;
                    Control.Settings.AllowContentAccess = true;
                    Control.Settings.DomStorageEnabled = true;
                    Control.SetWebChromeClient(new WebChromeClient());
        
                    var content_UAT = LoadData("ChatUAT.html");
        
                    Control.LoadDataWithBaseURL("https://service.force.com/embeddedservice/5.0/esw.min.js",
                        content_UAT,
                        "text/html;",
                        null,
                        "https://service.force.com/embeddedservice/5.0/esw.min.js");
                }
            }
            public class JavascriptWebViewClient : FormsWebViewClient
            {
                string _javascript;
                public JavascriptWebViewClient(HybridWebViewRenderer renderer, string javascript) : base(renderer)
                {
                    _javascript = javascript;
                }
                public override void OnPageFinished(Android.Webkit.WebView view, string url)
                {
                    base.OnPageFinished(view, url);
                    view.EvaluateJavascript("document.cookie", null);
                }
                public override void OnReceivedSslError(Android.Webkit.WebView view, SslErrorHandler handler, SslError error)
                {
                    Console.WriteLine("Salesforce webview error: ", error);
                    base.OnReceivedSslError(view, handler, error);
                }
                public override void OnReceivedHttpError(Android.Webkit.WebView view, IWebResourceRequest request, WebResourceResponse errorResponse)
                {
                    base.OnReceivedHttpError(view, request, errorResponse);
                }
                public override void OnLoadResource(Android.Webkit.WebView view, string url)
                {
                    base.OnLoadResource(view, url);
                }
            }
            public class JSBridge : Java.Lang.Object
            {
                readonly WeakReference<HybridWebViewRenderer> hybridWebViewRenderer;
                public JSBridge(HybridWebViewRenderer hybridRenderer)
                {
                    hybridWebViewRenderer = new WeakReference<HybridWebViewRenderer>(hybridRenderer);
                }
                [JavascriptInterface]
                [Export("invokeAction")]
                public void InvokeAction(string data)
                {
                    HybridWebViewRenderer hybridRenderer;
                    if (hybridWebViewRenderer != null && hybridWebViewRenderer.TryGetTarget(out hybridRenderer))
                    {
                        ((HybridWebView)hybridRenderer.Element).InvokeAction(data);
                    }
                }
            }
        
            //this methods html file content from assets folder
            public string LoadData(string inFile)
            {
                Stream stream = Android.App.Application.Context.Assets.Open(inFile);
                StreamReader sr = new StreamReader(stream);
                string text = sr.ReadToEnd();
                sr.Close();
                return text;
            }
        }
    

    This is iOS renderer

    [assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
    namespace SfChatbot.iOS
    {
        public class HybridWebViewRenderer : WkWebViewRenderer, IWKScriptMessageHandler
        {
            const string JavascriptFunction = "function initESW(data){jsBridge.invokeAction(data);}";
            //const string JavaScriptFunction = "function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}";
            WKUserContentController userController;
    
            public HybridWebViewRenderer() : this(new WKWebViewConfiguration())
            {
            }
    
            public HybridWebViewRenderer(WKWebViewConfiguration config) : base(config)
            {
                userController = config.UserContentController;
                var script = new WKUserScript(new NSString(JavascriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
                userController.AddUserScript(script);
                userController.AddScriptMessageHandler(this, "invokeAction");
            }
    
            protected override void OnElementChanged(VisualElementChangedEventArgs e)
            {
                base.OnElementChanged(e);
    
                if (e.OldElement != null)
                {
                    userController.RemoveAllUserScripts();
                    userController.RemoveScriptMessageHandler("invokeAction");
                    HybridWebView hybridWebView = e.OldElement as HybridWebView;
                    hybridWebView.Cleanup();
                }
    
                if (e.NewElement != null)
                {
                    base.OnElementChanged(e);
                    var assembly = IntrospectionExtensions.GetTypeInfo(typeof(SfChatbot.iOS.HybridWebViewRenderer)).Assembly;
                    Stream stream = assembly.GetManifestResourceStream("SfChatbot.iOS.Resources.ChatUAT.html");
    
                    string content1 = "";
                    using (var reader = new System.IO.StreamReader(stream))
                    {
                        content1  = reader.ReadToEnd();
                    }
    
                    LoadSimulatedRequest(new NSUrlRequest( new NSUrl("https://service.force.com/embeddedservice/5.0/esw.min.js")), content1);
                    
                }
            }
    
            public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
            {
                ((HybridWebView)Element).InvokeAction(message.Body.ToString());
            }
    
            protected override void Dispose(bool disposing)
            {
                if (disposing)
                {
                    ((HybridWebView)Element).Cleanup();
                }
                base.Dispose(disposing);
            }
    
            //not being used
            public string LoadUATData()
            {
                var uri = new UriBuilder("https://run.mocky.io/v3/37c8-1f4501adeeff").Uri;
                ServicePointManager.ServerCertificateValidationCallback = new
                    RemoteCertificateValidationCallback
                    (
                       delegate { return true; }
                    );
                var client = new WebClient();
                var content = client.DownloadString(uri);
    
                //SMS UAT
                var content1 = content.Replace("t1", "https.my.salesforce.com").
                   Replace("t2", "https://.cs81.force.com").
                   Replace("t3", "00D260009").
                   Replace("t4", "5260000004C").
                   Replace("t5", "573200004").
                   Replace("t6", "EmbeddedServiceLiveAgent_Parent04I260000000AIbca0f71c").
                   Replace("t7", "https://salesforce.com/embeddedservice/5.0/esw.min.js").
                   Replace("path", "src");
    
                return content1;
            }
        }
    

    Salesforce providing javacript file, we need to convert that into html format a nd keep in Assets and Resource folder respectivly for android and iOS. If any page is not having CSS applied(after executing, in output), you need to add Lightings CSS styles in head tag of html file.

    If there is any issue with keyboard is is hiding screen while typing, take help from this. Get the format for Salesforce chat file, from this question.