{"id":229224,"date":"2020-05-13T15:02:20","date_gmt":"2020-05-13T22:02:20","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/visualstudio\/?p=229224"},"modified":"2020-11-04T14:29:28","modified_gmt":"2020-11-04T22:29:28","slug":"building-a-progressive-web-app-with-blazor","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/visualstudio\/building-a-progressive-web-app-with-blazor\/","title":{"rendered":"Building a Progressive Web App with Blazor"},"content":{"rendered":"<p>A Progressive Web Application (PWA) is a Single Page Application (SPA) that uses modern browser APIs and capabilities to behave like a desktop app. Blazor WebAssembly (now in preview) includes support for Progressive Web Applications. Today, I want to show you how to build your first Blazor PWA. I am using the <a href=\"https:\/\/docs.microsoft.com\/visualstudio\/releasenotes\/vs2019-mac-preview-relnotes\">latest preview of Visual Studio for Mac<\/a>, you can also create them using the <a href=\"https:\/\/docs.microsoft.com\/visualstudio\/releases\/2019\/release-notes-preview\">latest Visual Studio 2019 Preview on Windows<\/a>.<\/p>\n<h2>Creating a new Blazor PWA<\/h2>\n<p>In this post, we\u2019ll walk through creating a simple \u201cTo Do\u201d application; in a future post we\u2019ll add some more advanced PWA features.<\/p>\n<p>Creating a new Blazor PWA is especially easy now in Visual Studio for Mac using the latest Blazor project template. You&#8217;ll need to have version 8.6 Preview 5 and later, and until the full release you&#8217;ll need to ensure you <a href=\"https:\/\/devblogs.microsoft.com\/aspnet\/blazor-webassembly-3-2-0-preview-5-release-now-available\/\">have the latest .NET Core SDK installed and to install the latest Blazor templates manually<\/a> to see them show up in the Visual Studio for Mac new project dialog. After launching Visual Studio for Mac you\u2019ll see the dialog below, click New to begin creating the project. If you already have Visual Studio open, you could also use the <a href=\"https:\/\/docs.microsoft.com\/visualstudio\/mac\/keyboard-shortcuts?view=vsmac-2019\">\u21e7\u2318N shortcut<\/a> to open the new project dialog.<\/p>\n<p><img decoding=\"async\" width=\"832\" height=\"553\" class=\"wp-image-229225 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati.png 832w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-300x199.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-768x510.png 768w\" sizes=\"(max-width: 832px) 100vw, 832px\" \/><\/p>\n<p>From here we will create a .NET Core Console project by selecting <em>Web and Console &gt; App &gt; Blazor WebAssembly App<\/em>.<\/p>\n<p><img decoding=\"async\" width=\"1013\" height=\"766\" class=\"wp-image-229226 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-1.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-1.png 1013w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-1-300x227.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-1-768x581.png 768w\" sizes=\"(max-width: 1013px) 100vw, 1013px\" \/><\/p>\n<p>Next, you\u2019ll need to configure the new Blazor WebAssembly (often abbreviated as WASM) application. We\u2019ll name our application \u201cBlazorPwaTodo\u201d. Select \u201c<em>No Authentication<\/em>\u201d, and check both the \u201c<em>ASP.NET Core Hosted<\/em>\u201d and \u201c<em>Progressive Web Application<\/em>\u201d options as shown below.<\/p>\n<p><img decoding=\"async\" width=\"1013\" height=\"766\" class=\"wp-image-229227 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-2.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-2.png 1013w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-2-300x227.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-2-768x581.png 768w\" sizes=\"(max-width: 1013px) 100vw, 1013px\" \/><\/p>\n<p>If you\u2019re using Visual Studio 2019 on Windows, the new project dialog is pretty similar:<\/p>\n<p><img decoding=\"async\" width=\"1023\" height=\"709\" class=\"wp-image-229228 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-3.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-3.png 1023w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-3-300x208.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-3-768x532.png 768w\" sizes=\"(max-width: 1023px) 100vw, 1023px\" \/><\/p>\n<p>If you\u2019re using Visual Studio Code, you can create a Blazor PWA from the command line using the \u2013pwa switch.<\/p>\n<pre class=\"prettyprint\">dotnet new blazorwasm -o BlazorPwaTodo --pwa --hosted<\/pre>\n<p>Note: We\u2019re selecting the \u201c<em>ASP.NET Core Hosted<\/em>\u201d option for two reasons. First, because we plan to integrate some backend services in the future. Second, using an ASP.NET Core Hosted site makes it easier to run a published version of the app in your local developer environment.<\/p>\n<h2>Adding a Todo Razor Component<\/h2>\n<p>We\u2019re going to build a simple To Do application, so we\u2019ll start by creating a new page to view our list. Right-click the Pages folder and select <em>Add<\/em> &gt; <em>New Item<\/em> &gt; <em><a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/blazor\/components?view=aspnetcore-3.1\">Razor Component<\/a><\/em>. Name the component\u2019s file <em>Todo.razor<\/em>.<\/p>\n<p>Use the following initial markup for the Todo component:<\/p>\n<pre class=\"prettyprint\">@page \"\/todo\"\r\n\r\n&lt;h3&gt;Todo&lt;\/h3&gt;<\/pre>\n<p>The first line of this template defines a <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/blazor\/components?view=aspnetcore-3.1&quot; \\l &quot;routing\">route template<\/a>, so browsing to \u201c\/todo\u201d will resolve to this Todo component.<\/p>\n<p>Next, we\u2019ll add the Todo component to the navigation bar. Open <em>Shared\/NavMenu.razor<\/em> and add a new list item with a <em>NavLink<\/em> element for the Todo component as shown below:<\/p>\n<pre class=\"prettyprint\">&lt;li class=\"nav-item px-3\"&gt;\r\n    &lt;NavLink class=\"nav-link\" href=\"todo\"&gt;\r\n        &lt;span class=\"oi oi-plus\" aria-hidden=\"true\"&gt;&lt;\/span&gt; Todo\r\n    &lt;\/NavLink&gt;\r\n&lt;\/li&gt;<\/pre>\n<p>Now run the application by selecting <em>Run<\/em> &gt; <em>Start Without Debugging<\/em> from the menu. Clicking the Todo link in the navigation shows our new page with the Todo heading, ready for us to start adding some code.<\/p>\n<p><img decoding=\"async\" width=\"1293\" height=\"952\" class=\"wp-image-229229 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-computer-screen-description-aut.png\" alt=\"A screenshot of a computer screen Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-computer-screen-description-aut.png 1293w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-computer-screen-description-aut-300x221.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-computer-screen-description-aut-1024x754.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-computer-screen-description-aut-768x565.png 768w\" sizes=\"(max-width: 1293px) 100vw, 1293px\" \/><\/p>\n<h2>Coding basic Todo functionality<\/h2>\n<p>Let\u2019s create a simple class to represent a todo item. Since we\u2019re using an ASP.NET Core hosted template and will probably include some back-end APIs later, we\u2019ll put that class in Shared project to make it available for use in our server application as well. Right-click the <em>Blazor.PwaTodo.Shared<\/em> project and select <em>Add<\/em> &gt; <em>New<\/em> <em>Class<\/em>. Name the new class file<em> TodoItem.cs<\/em> and enter the following code:<\/p>\n<pre class=\"prettyprint\">public class TodoItem\r\n{\r\n    public string Title { get; set; }\r\n    public bool IsDone { get; set; }\r\n}<\/pre>\n<p>Return to the Todo.razor component and add the following code:<\/p>\n<pre class=\"prettyprint\">@page \"\/todo\"\r\n\r\n&lt;h3&gt;Todo&lt;\/h3&gt;\r\n\r\n&lt;ul&gt;\r\n    @foreach (var todo in todos)\r\n    {\r\n        &lt;li&gt;@todo.Title&lt;\/li&gt;\r\n    }\r\n&lt;\/ul&gt;\r\n\r\n&lt;input placeholder=\"Something todo\" @bind=\"newTodo\" \/&gt;\r\n&lt;button @onclick=\"AddTodo\"&gt;Add todo&lt;\/button&gt;\r\n\r\n@code {\r\n    private IList&lt;TodoItem&gt; todos = new List&lt;TodoItem&gt;();\r\n    private string newTodo;\r\n\r\n    private void AddTodo()\r\n    {\r\n        if (!string.IsNullOrWhiteSpace(newTodo))\r\n        {\r\n            todos.Add(new TodoItem { Title = newTodo });\r\n            newTodo = string.Empty;\r\n        }\r\n    }\r\n}<\/pre>\n<p>Let\u2019s take a look at what this code is doing:<\/p>\n<ol>\n<li>In the code block at the bottom, we\u2019d declared a list to hold our TodoItem objects, named todo.<\/li>\n<li>We\u2019re iterating over the list of items using a <em>foreach<\/em> loop inside the <em>ul<\/em> element.<\/li>\n<li>We have an <em><a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/blazor\/data-binding?view=aspnetcore-3.1\">input<\/a><\/em><a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/blazor\/data-binding?view=aspnetcore-3.1\"> element that is bound<\/a> to a string property named <em>newTodo<\/em>.<\/li>\n<li>The input element has a a corresponding button to allow adding new items to the list.<\/li>\n<li>We\u2019re using the <em>button<\/em>\u2019s <em>@onclick<\/em> attribute to call the AddTodo method.<\/li>\n<li>The AddTodo method checks to make sure that text has been entered, then adds a new item to the list and clears the input to allow adding another item.<\/li>\n<\/ol>\n<p>Note: This explanation is condensed for brevity. If you\u2019d like a more thorough explanation, there\u2019s an <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/tutorials\/build-your-first-blazor-app?view=aspnetcore-3.1#build-a-todo-list\">in-depth walkthrough of this Todo example<\/a> in the Blazor documentation.<\/p>\n<p>Run the application (again, by using the <em>Run<\/em> &gt; <em>Start Without Debugging<\/em> command in the menu) and verify that you can add new items to the list.<\/p>\n<p><img decoding=\"async\" width=\"1253\" height=\"858\" class=\"wp-image-229230 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-4.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-4.png 1253w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-4-300x205.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-4-1024x701.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-4-768x526.png 768w\" sizes=\"(max-width: 1253px) 100vw, 1253px\" \/><\/p>\n<h2>Marking Todo items complete<\/h2>\n<p>So far, our Todo application only allows adding new items to the list with no support for marking them complete. Let\u2019s fix that by adding a checkbox for each item that\u2019s bound to the <em>IsDone<\/em> property.<\/p>\n<p>Update the list to include a checkbox input element as shown below.<\/p>\n<pre class=\"prettyprint\">&lt;ul&gt;\r\n    @foreach (var todo in todos)\r\n    {\r\n        &lt;li&gt;\r\n            &lt;input type=\"checkbox\" @bind=\"todo.IsDone\" \/&gt;\r\n            &lt;input @bind=\"todo.Title\" \/&gt;\r\n        &lt;\/li&gt;\r\n    }\r\n&lt;\/ul&gt;<\/pre>\n<p>And finally, to verify that these values are bound, we\u2019ll update the page header to show the number of items that are not yet complete. Update the <em>h3<\/em> element with this code:<\/p>\n<pre class=\"prettyprint\">&lt;h3&gt;Todo (@todos.Count(todo =&gt; !todo.IsDone))&lt;\/h3&gt;<\/pre>\n<p>Run the application to see it in action. Leave the application running when you\u2019re done testing, as we\u2019ll be using the running application in the next section.<\/p>\n<p><img decoding=\"async\" width=\"1425\" height=\"867\" class=\"wp-image-229231 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-computer-screen-description-aut-1.png\" alt=\"A screenshot of a computer screen Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-computer-screen-description-aut-1.png 1425w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-computer-screen-description-aut-1-300x183.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-computer-screen-description-aut-1-1024x623.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-computer-screen-description-aut-1-768x467.png 768w\" sizes=\"(max-width: 1425px) 100vw, 1425px\" \/><\/p>\n<h2>Installing the PWA<\/h2>\n<p>Now that we\u2019ve built a basic application, we can start taking advantage of the PWA features. Every time you\u2019ve been running this application, it\u2019s actually been running with PWA support due to using the PWA template.<\/p>\n<p>A Progressive Web Application (PWA) is a Single Page Application (SPA) that uses modern browser APIs and capabilities to behave like a desktop app. Blazor WebAssembly is a standards-based client-side web app platform, so it can use any browser API, including PWA APIs required for the following capabilities:<\/p>\n<ul>\n<li>Working offline and loading instantly, independent of network speed.<\/li>\n<li>Running in its own app window, not just a browser window.<\/li>\n<li>Being launched from the host&#8217;s operating system start menu, dock, or home screen.<\/li>\n<li>Receiving push notifications from a backend server, even while the user isn&#8217;t using the app.<\/li>\n<li>Automatically updating in the background.<\/li>\n<\/ul>\n<p>We\u2019ll look at two of these features in this post, then dig into some more advanced features in a future post.<\/p>\n<p>First, let\u2019s look at installation. When your application is running, you\u2019ll see an installation prompt in the browser. Here\u2019s how that looks using Edge on macOS:<\/p>\n<p><img decoding=\"async\" width=\"1425\" height=\"867\" class=\"wp-image-229232 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-5.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-5.png 1425w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-5-300x183.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-5-1024x623.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-5-768x467.png 768w\" sizes=\"(max-width: 1425px) 100vw, 1425px\" \/><\/p>\n<p>Installing the application relaunches the application in its own window without an address bar.<\/p>\n<p><img decoding=\"async\" width=\"1076\" height=\"822\" class=\"wp-image-229233 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-6.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-6.png 1076w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-6-300x229.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-6-1024x782.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-6-768x587.png 768w\" sizes=\"(max-width: 1076px) 100vw, 1076px\" \/><\/p>\n<p>Additionally, the application is shown with its own icon in the taskbar.<\/p>\n<p><img decoding=\"async\" width=\"1013\" height=\"82\" class=\"wp-image-229234 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/word-image.png\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/word-image.png 1013w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/word-image-300x24.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/word-image-768x62.png 768w\" sizes=\"(max-width: 1013px) 100vw, 1013px\" \/><\/p>\n<p>The window title, color scheme, icon, and other details are all customizable using the project\u2019s <em>manifest.json<\/em> file. We\u2019ll dig into that customization in the next post.<\/p>\n<h2>Testing mobile PWA installation and offline support<\/h2>\n<p>By default, apps created using the PWA template option have support for running offline. A user must first visit the app while they&#8217;re online. The browser automatically downloads and caches all the resources required to operate offline.<\/p>\n<p>However, offline support would interfere with local development since you might be viewing cached offline files when you make updates. For that reason, offline support is only available for published applications.<\/p>\n<p>To test this out locally, we\u2019ll need to publish the application. Right-click on the <em>BlazorPwaTodo.Server<\/em> application and select <em>Publish<\/em> &gt; <em>Publish to Folder<\/em>. You can leave the default folder name but be sure to note it. The default should be <em>bin\/Release\/netcoreapp3.1\/publish<\/em>.<\/p>\n<p><img decoding=\"async\" width=\"832\" height=\"408\" class=\"wp-image-229235 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-7.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-7.png 832w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-7-300x147.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-7-768x377.png 768w\" sizes=\"(max-width: 832px) 100vw, 832px\" \/><\/p>\n<p>Open a terminal and navigate to the publish directory &#8211; you can use Visual Studio for Mac\u2019s integrated terminal if you\u2019d like. Launch the server application using this command:<\/p>\n<p>dotnet BlazorPwaTodo.Server.dll<\/p>\n<p>This launches the published application running on localhost port 5001. Now that you\u2019re running the published application, we can test offline support. The easiest way to do this is by <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/blazor\/progressive-web-app?view=aspnetcore-3.1&amp;tabs=visual-studio&quot; \\l &quot;offline-support\">using the Network tab in browser tools to simulate offline mode<\/a>, as shown below.<\/p>\n<p><img decoding=\"async\" width=\"1089\" height=\"692\" class=\"wp-image-229236 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-8.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-8.png 1089w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-8-300x191.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-8-1024x651.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-8-768x488.png 768w\" sizes=\"(max-width: 1089px) 100vw, 1089px\" \/><\/p>\n<p>But let\u2019s make this a bit more of a challenge, and test this out in the mobile emulator! In order to do that, we\u2019ll need our mobile browser to be able to connect to our published site. My favorite way to handle that is by using the popular <a href=\"https:\/\/ngrok.com\/\">ngrok<\/a> tool.\u00a0After installing ngrok, launch it using the following command:<\/p>\n<pre class=\"prettyprint\">ngrok http https:\/\/localhost:5001 -host-header=\u201dlocalhost:5001\u201d<\/pre>\n<p>You should see ngrok spin up and provide a temporary public https endpoint we can use for testing. For more background on why we\u2019re setting the host header, see <a href=\"https:\/\/www.jerriepelser.com\/blog\/using-ngrok-with-aspnet-core\/\">this blog post by Jerrie Pelser<\/a>.<\/p>\n<p><img decoding=\"async\" width=\"1172\" height=\"687\" class=\"wp-image-229237 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-9.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-9.png 1172w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-9-300x176.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-9-1024x600.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-9-768x450.png 768w\" sizes=\"(max-width: 1172px) 100vw, 1172px\" \/><\/p>\n<p>Note: There are several different options for pointing a mobile emulator at your development website, but they all can carry some unexpected complications. The Android emulator does provide a local <a href=\"https:\/\/developer.android.com\/studio\/run\/emulator-networking#networkaddresses\">loopback on 10.0.0.2<\/a>, but it doesn\u2019t trust the ASP.NET Core HTTPS dev certs, which causes <a href=\"https:\/\/devblogs.microsoft.com\/xamarin\/cleartext-http-android-network-security\/\">some additional setup<\/a>. It\u2019s also possible to deploy to Azure and use the wildcard certificate support in the free tier, but you\u2019re no longer testing against localhost and need to publish each time you want to test an update. So, in my experience so far, ngrok is the quickest and simplest way to do a quick PWA \/ mobile test.<\/p>\n<p>Now that we\u2019ve got our site ready for testing on Using Visual Studio for Mac, we can launch an Android emulator using the <em>Tools<\/em> &gt; <em>Device Manager<\/em> menu (assuming you\u2019ve <a href=\"https:\/\/docs.microsoft.com\/visualstudio\/mac\/installation?view=vsmac-2019\">installed the Android workload<\/a>). If you don\u2019t already have an Android device configured, create one by clicking the New Device button. I\u2019m using the Pixel 2 Pie 9.0 image.<\/p>\n<p><img decoding=\"async\" width=\"548\" height=\"346\" class=\"wp-image-229238 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-10.png\" alt=\"A screenshot of a cell phone Description automatically generated\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-10.png 548w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-10-300x189.png 300w\" sizes=\"(max-width: 548px) 100vw, 548px\" \/><\/p>\n<p>Browse to the public HTTPS endpoint provided by ngrok. You should see the website, as well as a prompt to install the application locally. Install the application and close the browser, then test the PWA application by launching from the home screen.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-229239 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-11.png\" alt=\"A screenshot of a cell phone Description automatically generated\" width=\"530\" height=\"942\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-11.png 864w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-11-169x300.png 169w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-11-576x1024.png 576w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-11-768x1365.png 768w\" sizes=\"(max-width: 530px) 100vw, 530px\" \/><\/p>\n<p>Finally, let\u2019s test out offline support. Use the settings app and enable Airplane Mode, then launch the application. The application should continue to function with offline support.<\/p>\n<p><img decoding=\"async\" class=\"wp-image-229240 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-12.png\" alt=\"A screenshot of a cell phone Description automatically generated\" width=\"530\" height=\"942\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-12.png 811w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-12-169x300.png 169w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-12-576x1024.png 576w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2020\/05\/a-screenshot-of-a-cell-phone-description-automati-12-768x1366.png 768w\" sizes=\"(max-width: 530px) 100vw, 530px\" \/><\/p>\n<h2>Summary &amp; Wrap Up<\/h2>\n<p>In this post, we\u2019ve built a basic Todo application using the Blazor PWA template in Visual Studio for Mac. We then installed it locally on our Mac as well as on an Android emulator and tested basic offline support.<\/p>\n<p>Blazor PWA\u2019s offer a lot of <a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/blazor\/progressive-web-app?view=aspnetcore-3.1\">advanced features<\/a>, such as customizing the application appearance, adding push notifications, controlling caching and synchronization policies, leveraging the service worker for background work, and more. We\u2019ll take a look at some of these features in a future blog post.<\/p>\n<p>Documentation links:<\/p>\n<ul>\n<li><a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/tutorials\/build-your-first-blazor-app?view=aspnetcore-3.1\">Build your first Blazor app<\/a> (Todo sample)<\/li>\n<li><a href=\"https:\/\/docs.microsoft.com\/aspnet\/core\/blazor\/progressive-web-app?view=aspnetcore-3.1\">Build Progressive Web Applications with ASP.NET Core Blazor WebAssembly<\/a><\/li>\n<\/ul>\n<h2>Give it a try today!<\/h2>\n<p>The new Blazor WASM templates with PWA support are is now in Visual Studio 2019 for Mac 8.6 Preview. To start using them, make sure you\u2019ve\u00a0<a href=\"https:\/\/visualstudio.microsoft.com\/vs\/mac\/\">downloaded and installed Visual Studio 2019 for Mac<\/a>, then\u00a0<a href=\"https:\/\/aka.ms\/vs4mac-preview\">switch to the Preview channel<\/a>.<\/p>\n<p>As always, if you have any feedback on this, or any, version of Visual Studio for Mac, we invite you to leave them in the comments below this post or to reach out to us on Twitter at\u00a0<a href=\"https:\/\/twitter.com\/VisualStudioMac\" target=\"_blank\" rel=\"noopener noreferrer\">@VisualStudioMac<\/a>. If you run into issues while using Visual Studio for Mac, you can use\u00a0<a href=\"https:\/\/docs.microsoft.com\/visualstudio\/mac\/report-a-problem?view=vsmac-2019\" target=\"_blank\" rel=\"noopener noreferrer\">Report a Problem<\/a>\u00a0to notify the team. In addition to product issues, we also welcome your\u00a0<a href=\"https:\/\/aka.ms\/vsmac-suggestion\" target=\"_blank\" rel=\"noopener noreferrer\">feature suggestions<\/a>\u00a0on the\u00a0<a href=\"https:\/\/aka.ms\/feedback\/vsm-home\" target=\"_blank\" rel=\"noopener noreferrer\">Visual Studio Developer Community website<\/a>.<\/p>\n<p>We hope you enjoy using Visual Studio 2019 for Mac 8.6 as much as we enjoyed working on it!<\/p>\n<p><div  class=\"d-flex justify-content-center\"><a class=\"cta_button_link btn-primary mb-24\" href=\"https:\/\/aka.ms\/vs4mac-preview\" target=\"_blank\">Try Visual Studio for Mac Preview<\/a><\/div><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A Progressive Web Application (PWA) is a Single Page Application (SPA) that uses modern browser APIs and capabilities to behave like a desktop app. Blazor WebAssembly (now in preview) includes support for Progressive Web Applications. This blog post walks you through building a basic Blazor PWA application.<\/p>\n","protected":false},"author":470,"featured_media":229227,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[155],"tags":[6210,3743,452,4976],"class_list":["post-229224","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-visual-studio","tag-blazor","tag-visual-studio-2019-for-mac","tag-visual-studio-for-mac","tag-vsmac"],"acf":[],"blog_post_summary":"<p>A Progressive Web Application (PWA) is a Single Page Application (SPA) that uses modern browser APIs and capabilities to behave like a desktop app. Blazor WebAssembly (now in preview) includes support for Progressive Web Applications. This blog post walks you through building a basic Blazor PWA application.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/229224","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/users\/470"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/comments?post=229224"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/229224\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media\/229227"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media?parent=229224"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/categories?post=229224"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/tags?post=229224"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}