Daddoon / BlazorMobile

Create full C# driven hybrid-apps for iOS, Android, UWP & Desktop with Blazor!
MIT License
413 stars 55 forks source link

:warning: This project is discontinued as Microsoft now support Blazor Web through native directly with .NET MAUI Blazor :warning:

Links for .NET MAUI:

.NET MAUI Blazor - Build Hybrid Mobile, Desktop, and Web apps - By Daniel Roth: Youtube link Introduction to .NET MAUI Blazor | The Xamarin Show: Microsoft Docs

BlazorMobile[]()

Create full C# driven hybrid-apps for iOS, Android, UWP & Desktop with Blazor!

BlazorMobile - is a set of Nuget packages & project templates for embedding a Blazor web application as a standalone mobile application, hosted in Xamarin.

Framework requirement

Platform requirements

Additional platform notes

Android support

Universal Windows Platform

ElectronNET support

Summary

Troubleshoot

Updates and Migrations guides

See the Migration guide page

Difference between BlazorMobile & Progressive Web Apps (PWA)

Both creating an application as PWA or using BlazorMobile can be an option with Blazor

The main differences / advantages of BlazorMobile are:

Getting started from sample

First install the template model with the following command from a command prompt:

dotnet new -i BlazorMobile.Templates::3.2.8

Then go the folder where you want your project to be created, and from a command prompt type the following command, and of course replace MyProjectName to your desired project name:

dotnet new blazormobile -n MyProjectName

If you plan to also use the Desktop project using Electron.NET, you must first execute this command in order to install the Electron tool on your system:

dotnet tool install ElectronNET.CLI --version 5.30.1 -g

Then from your Desktop project directory, execute the following command:

electronize init

Open you newly created solution, and you are good to go!

Linking your Blazor app to your Xamarin project

Getting started from a fresh install

Beginning from a freshly installed BlazorMobile template, everything is already set by default.

The following informations only explains how your Xamarin.Forms project load your Blazor WebAssembly application.

How it works

This are informational bits about the project structure:

In order to ship your Blazor application within your Xamarin apps, you need to pack it and make it available to it.

Your Blazor app will be automatically packaged thanks to the BlazorMobile.Build NuGet package, that must be already installed on your Blazor web application project. The package location will be written in the build output after the Blazor build mecanism.

The filename should be YourBlazorProjectName.zip.

The steps to easily link it in Xamarin:

As seen on the BlazorMobile.Sample project, assuming a file linked in a virtual folder called Package, we would have a code like this:

namespace BlazorMobile.Sample
{
    public partial class App : BlazorApplication
    {
        public const string BlazorAppPackageName = "BlazorMobile.Sample.Blazor.zip";

        public App()
        {
            InitializeComponent();

            //Some code

            //Register Blazor application package resolver
            WebApplicationFactory.RegisterAppStreamResolver(() =>
            {
                //This app assembly
                var assembly = typeof(App).Assembly;

                //Name of our current Blazor package in this project, stored as an "Embedded Resource"
                //The file is resolved through AssemblyName.FolderAsNamespace.YourPackageNameFile

                //In this example, the result would be BlazorMobile.Sample.Package.BlazorMobile.Sample.Blazor.zip
                return assembly.GetManifestResourceStream($"{assembly.GetName().Name}.Package.{BlazorAppPackageName}");
            });

            //Some code

            MainPage = new MainPage();
        }
    }
}

Detecting Runtime Platform

Just call:

BlazorDevice.RuntimePlatform

In order to retrieve the current device runtime platform.

Note that the BlazorMobilService.Init() has an onFinish callback delegate. Every call to BlazorDevice.RuntimePlatform before the onFinish delegate call will return BlazorDevice.Unkown instead of the detected platform.

Communication between Blazor & Native

Using the ProxyInterface API

ProxyInterface API usages and limitations:

How-to use

In the project shared between Blazor & Xamarin, formerly YourAppName.Common create an interface, and add the [ProxyInterface] attribute on top of it. Assuming using the sample IXamarinBridge interface, present by default on YourAppName.Common project, your interface may look like this:

using BlazorMobile.Common.Attributes;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BlazorMobile.Sample.Common.Interfaces
{
    [ProxyInterface]
    public interface IXamarinBridge
    {
        Task<List<string>> DisplayAlert(string title, string msg, string cancel);
    }
}

In your Xamarin shared application project, formerly YourAppName project:

NOTE: If you plan to ship on UWP, the last statement is important. Because if using the Xamarin attribute method instead, UWP native toolchain will strip your services registration when compiling in Release mode with .NET Native.

It's so highly recommended to keep the DependencyService.Register route.

Your implementation may look like this. Here a kind of simple example:

using BlazorMobile.Sample.Common.Interfaces;
using BlazorMobile.Sample.Services;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace BlazorMobile.Sample.Services
{
    public class XamarinBridge : IXamarinBridge
    {
        public async Task<List<string>> DisplayAlert(string title, string msg, string cancel)
        {
            await App.Current.MainPage.DisplayAlert(title, msg, cancel);

            List<string> result = new List<string>()
            {
                "Lorem",
                "Ipsum",
                "Dolorem",
            };

            return result;
        }
    }
}

In your Blazor project, you only have two things to do:

Starting from the template, as a convinience, adding BlazorMobile natives services from ServicesHelper.ConfigureCommonServices.

using Microsoft.Extensions.DependencyInjection;

namespace BlazorMobile.Sample.Blazor.Helpers
{
    public static class ServicesHelper
    {
        public static void ConfigureCommonServices(IServiceCollection services)
        {
            //Add services shared between multiples project here
            services.AddBlazorMobileNativeServices<Startup>();
        }
    }
}

Then if you want to use any of your Blazor to native interface, it's as simple as this:

@page  "/blazormobile"

@using BlazorMobile.Common
@using BlazorMobile.Sample.Common.Interfaces
@inject IXamarinBridge XamarinBridge

<h1>BlazorMobile</h1>

<button class="btn btn-primary" @onclick="@ShowPlatform">Show Runtime platform in a native dialog</button>

@code {

    async void ShowPlatform()
    {
        await XamarinBridge.DisplayAlert("Platform identity", $"Current platform is {BlazorDevice.RuntimePlatform}", "Great!");
    }
}

Using the Message API

Message API usages and limitations:

How-to use

CallJSInvokableMethod - Allow to call a Blazor static JSInvokable method

From the IBlazorWebView object retrieved when launching your BlazorMobile application, you should be able to call CallJSInvokableMethod. This is self explanatory about it's usage, as it look like signature you can find on the InvokeAsyncMethod in Javascript in a regular Blazor application.

/// <summary>
/// Call a static JSInvokable method from native side
/// </summary>
/// <param name="assembly">The assembly of the JSInvokable method to call</param>
/// <param name="method">The JSInvokable method name</param>
/// <param name="args">Parameters to forward to Blazor app. Check that your parameters are serializable/deserializable from both native and Blazor sides.</param>
/// <returns></returns>
void CallJSInvokableMethod(string assembly,string method, params object[] args);

An usage could look like this:

webview = BlazorWebViewFactory.Create();

//Assuming that we know that the Blazor application already started
webview.CallJSInvokableMethod("MyBlazorAppAssemblyName", "MyJSInvokableMethodName", "param1", "param2", "param3");

Your JSInvokable method on Blazor side will then be called.

PostMessage - Allow to post a message to a static or instanciated delegate method

This API is in two parts, one in the native side, the other one on the Blazor side.

Messages sent will be received:

Native side

From the IBlazorWebView object retrieved when launching your BlazorMobile application, you should be able to call PostMessage.

/// <summary>
/// Post a message to the Blazor app. Any objects that listen to a specific message name by calling BlazorMobileService.MessageSubscribe will trigger their associated handlers.
/// </summary>
/// <param name="messageName"></param>
/// <param name="args"></param>
void PostMessage<TArgs>(string messageName, TArgs args);

An usage could look like this:

webview = BlazorWebViewFactory.Create();

//Assuming that we know that the Blazor application already started
webview.PostMessage<string>("myNotification", "my notification value");

Your message will be sent to the Blazor app.

Blazor side

In order to receive message notifications on Blazor side, you should subscribe to the message to listen, with the expected argument type. Here are the three static methods usable from the BlazorMobileService static class in the Blazor app, for Message API:

/// <summary>
/// Subscribe to a specific message name sent from native side with PostMessage, and forward the event to the specified delegate if received
/// </summary>
/// <param name="messageName">The message name to subscribe to</param>
/// <param name="handler">The delegate action that must be executed at message reception</param>
static void MessageSubscribe<TArgs>(string messageName, Action<TArgs> handler);

/// <summary>
/// Unsubscribe to a specific message name sent from native side with PostMessage
/// </summary>
/// <param name="messageName">The message name to unsubscribe to</param>
/// <param name="handler">The delegate action that must be unsubscribed</param>
static void MessageUnsubscribe<TArgs>(string messageName, Action<TArgs> handler);

/// <summary>
/// Allow to post a message to any delegate action registered through MessageSubscribe.
/// This method behavior is similar to the IBlazorWebView.PostMessage method on the native side,
/// except that you send message from within your Blazor app instead sending it from native side.
/// </summary>
/// <typeparam name="TArgs">The paramter expected type</typeparam>
/// <param name="messageName">The message name to target</param>
/// <param name="value">The value to send in the message</param>
static void PostMessage<TArgs>(string messageName, TArgs value);

In order to receive the message sent from the native side in our previous example we could do this in a Blazor page:

public void OnMessageReceived(string payload)
{
   //Stuff here will be called when receiving the message
}

BlazorMobileService.MessageSubscribe<string>("myNotification", OnMessageReceived);

NOTE: If you are subscribing an instance method member like in this example, to the MessageSubscribe method, it's highly recommended to cleanly unregister it when you know that your object instance will be disposed.

In this example, from a Razor page you could do something like this:

@page "/myPage"
@implements IDisposable

//Some code

@code {
    //Some code

    public void Dispose()
    {
        BlazorMobileService.MessageUnsubscribe<string>("myNotification", OnMessageReceived);
    }
}

If there is no IDisposable mecanism on the C# component you are working on, you may also just unregister at Destructor level. See the following example:

public class MyClass()
{
    public void OnMessageReceived(string payload)
    {
       //Stuff here will be called when receiving the message
    }

    //Constructor
    MyClass()
    {
        BlazorMobileService.MessageSubscribe<string>("myNotification", OnMessageReceived);
    }

    //Destructor
    ~MyClass()
    {
        BlazorMobileService.MessageUnsubscribe<string>("myNotification", OnMessageReceived);
    }
}

Validating the Blazor application navigation

Rules have been enforced and/or implemented on each supported platforms, in order to allow:

This mean that if you register to the Navigating event handler of your IBlazorWebView object, you should be able to allow or cancel any document navigation made by:

As your Blazor application is mainly a single webview, you may want to prevent any unexpected action that would make your page navigate, and/or control more precisely some iframe behaviors shown in your Blazor application.

With this Navigating enforcement, you should be able to easily block any unwanted navigation, and instead opening the requested url in an external browser, as an example.

Here is a sample code you find with the default template of BlazorMobile.

In your MainPage.xaml.cs file:

//Blazor WebView agnostic contoller logic
webview = BlazorWebViewFactory.Create();

//Manage your native application behavior when an external resource is requested in your Blazor web application
//Customize your app behavior in BlazorMobile.Sample.Handler.OnBlazorWebViewNavigationHandler.cs file or create your own!
webview.Navigating += OnBlazorWebViewNavigationHandler.OnBlazorWebViewNavigating;

This register the navigating event to the OnBlazorWebViewNavigating event handler, included in the sample.

Here is the control code in the sample:

using BlazorMobile.Common;
using BlazorMobile.Services;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using Xamarin.Forms;

namespace BlazorMobile.Sample.Handler
{
    public static class OnBlazorWebViewNavigationHandler
    {
        public static void OnBlazorWebViewNavigating(object sender, WebNavigatingEventArgs e)
        {
            var applicationBaseURL = WebApplicationFactory.GetBaseURL() + "/";

            if (e.Url.Equals(applicationBaseURL, StringComparison.OrdinalIgnoreCase))
            {
                //This is our application base URI. We should do nothing and continue navigating to the app
                e.Cancel = false;
            }
            else if (e.Url.StartsWith(WebApplicationFactory.GetBaseURL(), StringComparison.OrdinalIgnoreCase))
            {
                //Here, our application is loading an URI
                //You may add a custom logic, like opening a new view, changing the URI parameters then opening a view...

                e.Cancel = true;

                switch (BlazorDevice.RuntimePlatform)
                {
                    default:
                        Device.OpenUri(new Uri(WebUtility.UrlDecode(e.Url)));
                        break;
                }
            }
            else
            {
                //If here this is not our application loading an URI
                //You may add a custom logic, like opening a new view, changing the URI parameters then opening a view...

                e.Cancel = true;

                switch (BlazorDevice.RuntimePlatform)
                {
                    default:
                        Device.OpenUri(new Uri(WebUtility.UrlDecode(e.Url)));
                        break;
                }
            }
        }
    }
}

Device remote debugging & Debugging from NET Core 3.0

Even if there is now some debug functionalities in the Blazor WASM version in Chrome, it is pretty limited compared to the pure server-side debugging with NET Core 3.0.

A small server-side Blazor application sample has been added in order to test and debug your code from it. See your Blazor.Server project. You don't have to code anything in it, as it will use all the code logic you have done with the Blazor project (the WASM one).

This is very usefull if you need to debug your Blazor application logic, and also your device.

Credits to @Suchiman, for the BlazorDualMode project, taken as reference for server sharing client-side Blazor model.

"But wait ! I cannot ship a server-side version of my Blazor application as a mobile app !"

Of you course you can't. But you can do remote debugging on your device in order to mimic your mobile application environment, from your development environment.

You should be able:

You won't be able:

For this last critical point, you should remember that you may have some tools shipped for device browser debugging. On iOS, you should debug from Safari on OSX (see online documentation), and on Android, you should debug from the WebIDE tool available on Firefox 67 (SHIFT+F8 on Windows). New debugging tooling available from more recent Firefox version are not yet available for current GeckoView version, and WebIDE has been removed on more recent versions of Firefox.

Enable remote debugging

There is some, but little configuration to make in order to allow remote debugging.

Xamarin side

On the Xamarin side, you must allow debug features in order to allow external source to connect to your Device. On the BlazorMobile.Sample project, in App.cs constructor, we will allow debug features. see:

public App()
{
    ...

    #if DEBUG
    //This allow remote debugging features
    WebApplicationFactory.EnableDebugFeatures();
    #endif

    WebApplicationFactory.SetHttpPort(8888);
    ...
}

Also note the initialization and usage of the 8888 port. You may and want to use any other valid port. Just keep in mind the current used port in your application, for the remote debugging.

NOTE: The current #if DEBUG directive is present by default in source project, but it seem that it is removed when creating project template package.

Blazor side

On the Blazor project, both on WASM and Server projects if you want to test on both, you must call BlazorService.EnableClientToDeviceRemoteDebugging in your Statup.cs, Configure method. see:

using BlazorMobile.Common;
using BlazorMobile.Common.Services;
using Microsoft.AspNetCore.Components.Builder;
using Microsoft.Extensions.DependencyInjection;
using System;
using BlazorMobile.Sample.Blazor.Helpers;

namespace BlazorMobile.Sample.Blazor
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            ServicesHelper.ConfigureCommonServices(services);
        }

        public void Configure(IComponentsApplicationBuilder app)
        {
            #region DEBUG

            //Only if you want to test WebAssembly with remote debugging from a dev machine
            BlazorMobileService.EnableClientToDeviceRemoteDebugging("192.168.1.118", 8888);

            #endregion

            BlazorMobileService.Init(app, (bool success) =>
            {
                Console.WriteLine($"Initialization success: {success}");
                Console.WriteLine("Device is: " + Device.RuntimePlatform);
            });

            app.AddComponent<MobileApp>("app");
        }
    }
}

NOTE: You must call BlazorMobileService.EnableClientToDeviceRemoteDebugging before the BlazorMobileService.Init call !

Replace of course the first parameter by your own device IP address, and use the same port as configured in your Xamarin project.

Additional configuration for UWP

On UWP, because of the NetworkIsolation behavior, you cannot connect by default from your PC to the UWP app.

You must execute this command in background during development in order to allow incoming remote connection, and so remote debugging, in your UWP app:

CheckNetIsolation loopbackexempt -is -n=YourUWPPackageFamilyName

Of course, replace YourUWPPackageFamilyName by your package name on UWP. You can find it in the Packages tab of your Package.appxmanifest, at the end.

Deploy & Launch mobile application, debug from PC

Then, you just need to deploy your application to your phone, and launch it in order to allow external source to connect to it. You may just launch it on the device, and only debug Blazor from your PC, or you may also launch it with the Xamarin debugger, in order to test Xamarin code during Blazor session.

If you want to debug both Blazor side and Xamarin side you may:

The Blazor application will be launched from your PC, and it will try to connect to the remote application instance.

Values from Xamarin context will be returned, and your code will behave as launched within the device.

NOTE: You need to register server_index instead of index.html on your Blazor.Server project (even in Desktop project if needed), in Startup.cs in order to debug from the NET Core version.

The server_index.cshtml file is automatically generated by BlazorMobile.Build, in order to have a synced copy of your Blazor application index.html, replacing blazor.webassembly.js to blazor.server.js and also adding required component to load since Blazor preview9.

In Blazor server and Desktop projects, your Startup.cs must have this:

endpoints.MapFallbackToPage("/server_index");

Instead of this:

endpoints.MapFallbackToClientSideBlazor<BlazorMobile.Sample.Blazor.Startup>("index.html");

If you are starting from a fresh template, everything is right by default.

The server project should listen on http://localhost:5080/ by default.

When the server console will show up during your debugging session, you need to open a tab in your favorite browser and browse http://localhost:5080/ url, in order to connect and debug your Blazor .NET Core application.

Loading several or external BlazorMobile apps

You can load several apps during your application lifetime, shipped at build time or loaded through external source or saved on your filesystem. You can either manage by yourself how the packages are loaded and saved, or use some integrated helpers to store the packages if you want to, on your device, for all platforms.

The API is pretty straightforward, here is the availables methods through the WebApplicationFactory static class available from native side:

/// <summary>
/// Add the given Stream as a package in a data store on the device, with the given name
/// </summary>
/// <param name="name"></param>
/// <param name="content"></param>
WebApplicationFactory.AddPackage(string name, Stream content);

/// <summary>
/// Remove the package with the given name from the data store of the device
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
WebApplicationFactory.RemovePackage(string name);

/// <summary>
/// List available packages in the data store of the device
/// </summary>
/// <returns></returns>
WebApplicationFactory.ListPackages();

/// <summary>
/// Load an app package with the given name, stored on the device.
/// This is a shorthand on calling yourself <see cref="RegisterAppStreamResolver"/> and <see cref="ReloadApplication"/>
/// as the loading is managed by all the entries you get through <see cref="AddPackage(string, Stream)"/>, <see cref="RemovePackage(string)"/>, <see cref="ListPackages"/>.
/// </summary>
/// <param name="name">The package to load</param>
/// <returns></returns>
WebApplicationFactory.LoadPackage(string name);

/// <summary>
/// Load an app package from the given Stream object. You are responsible for your Stream management.
/// This mean that things may behave incorrectly if you close or dispose the stream. The given stream will
/// be automatically disposed if you load or register another package instead.
/// </summary>
/// <param name="appStream">The package to load as a Stream</param>
/// <returns></returns>
WebApplicationFactory.LoadPackageFromStream(Stream appStream);

/// <summary>
/// Load an app package with the given package assembly path if you have stored it the assembly as a static resource.
/// This is the default mode used at start when shipping your base application from BlazorMobile template, but you can
/// extend this in order to load different packages at your native app startup.
/// </summary>
/// <param name="packageAssembly">The assembly where your Blazor package is stored.
/// TIPS: If you know a type stored in this assembly, you may resolve the assembly object with 'typeof(YourType).Assembly'</param>
/// <param name="packagePath">The relative path where the Blazor package is stored in the assembly</param>
/// <returns></returns>
WebApplicationFactory.LoadPackageFromAssembly(Assembly packageAssembly, string packagePath);

Android Build size optimization

The underlying Webview component used with BlazorMobile on Android is the excellent Mozilla GeckoView browser component, replacing the traditional Webview component shipped with the OS. This component allow us to:

This with the downside that we ship the GeckoView component in the APK. Without any optimzations, this component take roughly 150 MB because it ship all the CPU implementations by default.

The solution to this problem is to ship one APK per ABI, as this will split the multiple ABI implementation of the GeckoView component to each specific APK ABI. The GeckoView component for Android in your APK will then respectively shrink to approximatively 50MB per platform.

Recommended readings:

Multi-APK and 64-bit compliance

If you are using Google Play’s multiple-APK support to publish your app, note that compliance with the 64-bit requirement is evaluated at the release level. However, the 64-bit requirement does not apply to APKs or app bundles that are not distributed to devices running Android 9 Pie or later.
If one of your APKs is marked as not being compliant, but is older and it’s not possible to bring it into compliance, one strategy is to add a maxSdkVersion="27" attribute in the uses-sdk element in that APK’s manifest. This APK won’t be delivered to devices running Android 9 Pie or later, and it will no longer block compliance.

Electron.NET support with BlazorMobile

You can also deploy your application developped with Blazor & BlazorMobile as a desktop application with Electron.NET. The plugin has been updated in order to be aware of an Electron.NET executing context and behave correctly, with your same codebase and project structure.

Be aware that even if a Xamarin.Forms library is present on the Electron.NET desktop application, there is no deep support of the Xamarin.Forms API.

If you need to call anything from Xamarin on your shared Xamarin.Forms project that is not supported yet, you can check if we are running through Electron or Xamarin, by calling BlazorDevice.IsElectronNET().

To get started about the Electron.NET Desktop project, it's highly recommended to create it from BlazoreMobile.Templates. See Getting started from sample section.

Xamarin.Forms support on Electron.NET

NOTE: BlazorDevice.RuntimePlatform never returns "ElectronNET" but ElectronNET presence can be checked by BlazorDevice.IsElectronNET.

Troubleshoot

My Xamarin services are not found when interoping in UWP

As stated in the Communication between Blazor & Native section in Using the ProxtInterface API, check that your services are registered through the DependencyService.Register method call.

If you plan to ship on UWP [...] if using the Xamarin attribute method instead, UWP native toolchain will strip your services registration when compiling in Release mode with .NET Native.

Cannot connect to a remote webserver on UWP

There is some behaviors that are specifics to UWP:

Unable to connect to UWP remotely even with NetworkIsolation disabled

This behavior may happen if the certificate used in your Package.appxmanifest is not present on your computer. This may likely happen starting from the template. Please read this Microsoft documentation, Create a certificate for package signing, and don't forget to set your newly created certificate as your UWP certificate afterwards, from your Package.appxmanifest property window.

Cyclic restore issue at project template creation

This may happen if you called your project BlazorMobile at template creation, as it seem to confuse the NuGet restore command with the Nuget packages with the same suffix name, like BlazorMobile and BlazorMobile.Common.

Just avoid theses reserved names when creating your project.

iOS/Safari 13: Unhandled Promise Rejection: TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context

This error is actually a regression in iOS 13, preventing Blazor to boot correctly. Microsoft already fixed the bug preventing to boot, but it's not yet officialy released. In the meantime, if you want to workaround this bug temporiraly, add this line...

<script>var Module;</script>

...before the blazor.webassembly.js script tag.

Credits to @kmiller68 in this issue

And also you have to opt-in to a patched blazor.webassembly.js runtime at iOS app startup. If so, in your AppDelegate.cs file, before the LoadApplication, call EnableDelayedStartPatch like in this example:

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    global::Xamarin.Forms.Forms.Init();
    BlazorWebViewService.Init();

    //Register our Blazor app package
    WebApplicationFactory.RegisterAppStreamResolver(AppPackageHelper.ResolveAppPackageStream);

    if (int.TryParse(UIDevice.CurrentDevice.SystemVersion.Split('.')[0], out int majorVersion) && majorVersion >= 13)
    {
        BlazorWebViewService.EnableDelayedStartPatch();
    }

    LoadApplication(new App());

    return base.FinishedLaunching(app, options);
}

ITMS-90809: Deprecated API Usage - Apple will stop accepting submissions of apps that use UIWebView APIs

When submiting an iOS app on the AppStore you may have this message: ITMS-90809: Deprecated API Usage - Apple will stop accepting submissions of apps that use UIWebView APIs . See https://developer.apple.com/documentation/uikit/uiwebview for more information.

Please follow this issue on Xamarin.Forms GitHub page, or this documentation

As stated, at the date of writing:

UIWebView Deprecation and App Store Rejection (ITMS-90809)

Starting in April 2020, Apple will reject apps that still use the deprecated UIWebView API. While Xamarin.Forms has switched to WKWebView as the default, there is still a reference to the older SDK in the Xamarin.Forms binaries. Current iOS linker behavior does not remove this, and as a result the deprecated UIWebView API will still appear to be referenced from your app when you submit to the App Store.

A preview version of the linker is available to fix this issue. To enable the preview, you will need to supply an additional argument --optimize=experimental-xforms-product-type to the linker.

The prerequisites for this to work are:

Xamarin.Forms 4.5 or higher – Pre-release versions of Xamarin.Forms 4.5 can be used. Xamarin.iOS 13.10.0.17 or higher – Check your Xamarin.iOS version in Visual Studio. This version of Xamarin.iOS is included with Visual Studio for Mac 8.4.1 and Visual Studio 16.4.3. Remove references to UIWebView – Your code should not have any references to UIWebView or any classes that make use of UIWebView.

Apple Rejection - Your app uses or references the following non-public APIs: LinkPresentation.framework, QuickLookThumbnailing.framework

You may receive this message from Apple:

Guideline 2.5.1 - Performance - Software Requirements

Your app uses or references the following non-public APIs:

- LinkPresentation.framework
- QuickLookThumbnailing.framework

The use of non-public APIs is not permitted on the App Store because it can lead to a poor user experience should these APIs change.

Continuing to use or conceal non-public APIs in future submissions of this app may result in the termination of your Apple Developer account, as well as removal of all associated apps from the App Store.

In this case:

--linksdkonly --linkskip=LinkPresentation --linkskip=QuickLookThumbnailing

Android crash at boot on API 28

This may be related if you are building your app as an Android App Bundles in release mode, API 28. Credits to @shawndeggans - See #137 for more info.

As stated at the top of the documentation, consider releasing your app as an APK. See also the Android Build size optimization section.

Application refresh and restart after going in background

There is actually two cases when this behavior may happen:

Community

Authors

License

This project is licensed under the MIT License - see the LICENSE file for details