Skip to content

Commit 9ff35f8

Browse files
[docs] for Blazor (#18)
Prepping for next release * Startup/app size comparisons * Update README.md * Blazor screenshots * Create settings.json
1 parent e91c8dd commit 9ff35f8

File tree

8 files changed

+183
-84
lines changed

8 files changed

+183
-84
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"dotnet.defaultSolution": "spice.sln"
3+
}

README.md

Lines changed: 104 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,60 @@ If you like this idea, star for approval! Read on for details!
66

77
![Spice running on iOS and Android](docs/spice.png)
88

9+
## Getting Started
10+
11+
Simply install the template:
12+
13+
```sh
14+
dotnet new install Spice.Templates
15+
```
16+
17+
Create either a plain Spice project, or a hybrid "Spice+Blazor" project:
18+
19+
```sh
20+
dotnet new spice
21+
# Or if you want hybrid/web support
22+
dotnet new spice-blazor
23+
```
24+
25+
Build it as you would for other .NET MAUI projects:
26+
27+
```sh
28+
dotnet build
29+
# To run on Android
30+
dotnet build -f net7.0-android -t:Run
31+
# To run on iOS
32+
dotnet build -f net7.0-ios -t:Run
33+
```
34+
35+
Of course, you can also just open the project in Visual Studio and hit F5.
36+
37+
## Startup Time & App Size
38+
39+
In comparison to a `dotnet new maui` project, I created a Spice
40+
project with the same layouts and optimized settings for both project
41+
types. (`AndroidLinkMode=r8`, etc.)
42+
43+
App size of a single-architecture `.apk`, built for `android-arm64`:
44+
45+
![Graph of an app size comparison](docs/appsize.png)
46+
47+
The average startup time of 10 runs on a Pixel 5:
48+
49+
![Graph of a startup comparison](docs/startup.png)
50+
51+
This gives you an idea of how much "stuff" is in .NET MAUI.
52+
53+
In some respects the above comparison isn't completely fair, as Spice
54+
🌶 has very few features. However, Spice 🌶 is [fully
55+
trimmable][trimming], and so a `Release` build of an app without
56+
`Spice.Button` will have the code for `Spice.Button` trimmed away. It
57+
will be quite difficult for .NET MAUI to become [fully
58+
trimmable][trimming] -- due to the nature of XAML, data-binding, and
59+
other System.Reflection usage in the framework.
60+
61+
[trimming]: https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming
62+
963
## Background & Motivation
1064

1165
In reviewing, many of the *cool* UI frameworks for mobile:
@@ -100,6 +154,52 @@ native views.
100154
[poco]: https://en.wikipedia.org/wiki/Plain_old_CLR_object
101155
[minimal-apis]: https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis
102156

157+
## *NEW* Blazor Support
158+
159+
Currently, Blazor/Hybrid apps are strongly tied to .NET MAUI. The
160+
implementation is basically working with the plumbing of the native
161+
"web view" on each platform. So we could have implemented
162+
`BlazorWebView` to be used in "plain" `dotnet new android` or
163+
`dotnet new ios` apps. For now, I've migrated some of the source code
164+
from `BlazorWebView` from .NET MAUI to Spice 🌶, making it available
165+
as a new control:
166+
167+
```csharp
168+
public class App : Application
169+
{
170+
public App()
171+
{
172+
Main = new BlazorWebView
173+
{
174+
HostPage = "wwwroot/index.html",
175+
RootComponents =
176+
{
177+
new RootComponent { Selector = "#app", ComponentType = typeof(Main) }
178+
},
179+
};
180+
}
181+
}
182+
```
183+
184+
From here, you can write `Index.razor` as the Blazor you know and love:
185+
186+
```razor
187+
@page "/"
188+
189+
<h1>Hello, world!</h1>
190+
191+
Welcome to your new app.
192+
```
193+
194+
To arrive at Blazor web content inside iOS/Android apps:
195+
196+
![Screenshot of Blazor app on iOS](docs/blazor.png)
197+
198+
This setup might be particularly useful if you want web content to
199+
take full control of the screen with minimal native controls. No need
200+
for the app size / startup overhead of .NET MAUI if you don't actually
201+
have native content?
202+
103203
## Scope
104204

105205
* No XAML. No DI. No MVVM. No MVC. No data-binding. No System.Reflection.
@@ -136,35 +236,17 @@ It is an achievement in itself that I was able to invent my own UI
136236
framework and pick and choose the pieces of .NET MAUI that made sense
137237
for my framework.
138238

139-
## Getting Started
140-
141-
Simply install the template:
142-
143-
```bash
144-
dotnet new install Spice.Templates
145-
```
146-
147-
Create the project and build it as you would for other .NET MAUI
148-
projects:
149-
150-
```bash
151-
dotnet new spice
152-
dotnet build
153-
# To run on Android
154-
dotnet build -f net7.0-android -t:Run
155-
# To run on iOS
156-
dotnet build -f net7.0-ios -t:Run
157-
```
158-
159-
Of course, you can also just open the project in Visual Studio and hit F5.
160-
161239
## Implemented Controls
162240

163241
* `View`: maps to `Android.Views.View` and `UIKit.View`.
164242
* `Label`: maps to `Android.Widget.TextView` and `UIKit.UILabel`
165243
* `Button`: maps to `Android.Widget.Button` and `UIKit.UIButton`
166244
* `StackView`: maps to `Android.Widget.LinearLayout` and `UIKit.UIStackView`
167245
* `Image`: maps to `Android.Widget.ImageView` and `UIKit.UIImageView`
246+
* `Entry`: maps to `Android.Widget.EditText` and `UIKit.UITextField`
247+
* `WebView`: maps to `Android.Webkit.WebView` and `WebKit.WKWebView`
248+
* `BlazorWebView` extends `WebView` adding support for Blazor. Use the
249+
`spice-blazor` template to get started.
168250

169251
## Custom Controls
170252

@@ -315,64 +397,3 @@ or iOS view controllers if necessary.
315397
Hopefully, we can implement this for a future release of Visual Studio.
316398

317399
[muh]: https://learn.microsoft.com/dotnet/api/system.reflection.metadata.metadataupdatehandlerattribute
318-
319-
## Startup Time & App Size
320-
321-
In comparison to a `dotnet new maui` project, I created a Spice
322-
project with the same layouts and optimized settings for both project
323-
types. (`AndroidLinkMode=r8`, etc.)
324-
325-
Startup time for a `Release` build on a Pixel 5:
326-
327-
Spice 🌶:
328-
```log
329-
02-02 20:09:49.583 2174 2505 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +261ms
330-
02-02 20:09:51.060 2174 2505 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +265ms
331-
02-02 20:09:52.482 2174 2505 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +262ms
332-
02-02 20:09:53.902 2174 2505 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +266ms
333-
02-02 20:09:55.345 2174 2505 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +246ms
334-
02-02 20:09:56.755 2174 2505 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +243ms
335-
02-02 20:09:58.190 2174 2505 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +264ms
336-
02-02 20:09:59.592 2174 2505 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +246ms
337-
02-02 20:10:01.030 2174 2505 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +249ms
338-
02-02 20:10:02.452 2174 2505 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +248ms
339-
Average(ms): 255
340-
Std Err(ms): 2.94014360949333
341-
Std Dev(ms): 9.29755045398757
342-
```
343-
344-
.NET MAUI:
345-
```log
346-
02-02 20:07:52.357 2174 2505 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +541ms
347-
02-02 20:07:54.078 2174 2505 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +538ms
348-
02-02 20:07:55.799 2174 2505 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +538ms
349-
02-02 20:07:57.531 2174 2505 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +539ms
350-
02-02 20:07:59.262 2174 2505 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +537ms
351-
02-02 20:08:00.944 2174 2505 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +540ms
352-
02-02 20:08:02.666 2174 2505 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +544ms
353-
02-02 20:08:04.397 2174 2505 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +527ms
354-
02-02 20:08:06.111 2174 2505 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +532ms
355-
02-02 20:08:07.803 2174 2505 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +535ms
356-
Average(ms): 537.1
357-
Std Err(ms): 1.52351931760353
358-
Std Dev(ms): 4.8177911028926
359-
```
360-
361-
App size of a single-architecture `.apk`, built for `android-arm64`:
362-
363-
```
364-
7772202 com.companyname.HeadToHeadSpice-Signed.apk
365-
12808825 com.companyname.HeadToHeadMaui-Signed.apk
366-
```
367-
368-
This gives you an idea of how much "stuff" is in .NET MAUI.
369-
370-
In some respects the above comparison isn't completely fair, as Spice 🌶
371-
has like 0 features. However, Spice 🌶 is [fully trimmable][trimming],
372-
and so a `Release` build of an app without `Spice.Button` will have
373-
the code for `Spice.Button` trimmed away. It will be quite difficult
374-
for .NET MAUI to become [fully trimmable][trimming] -- due to the
375-
nature of XAML, data-binding, and other System.Reflection usage in the
376-
framework.
377-
378-
[trimming]: https://learn.microsoft.com/dotnet/core/deploying/trimming/prepare-libraries-for-trimming

docs/appsize.png

6.48 KB
Loading

docs/blazor.png

49.9 KB
Loading

docs/spice.pptx

62.9 KB
Binary file not shown.

docs/startup.png

5.85 KB
Loading
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
@page "/"
22

3-
<h1>Hello, world!</h1>
3+
<h1>Hello, Spice + Blazor 🌶!</h1>
44

55
Welcome to your new app.

sizes/startup.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Startup times
2+
3+
Just recording these over time. A `Release` build on a Pixel 5 device.
4+
5+
Spice 🌶:
6+
```log
7+
06-01 14:43:20.843 2084 2346 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +247ms
8+
06-01 14:43:22.248 2084 2346 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +249ms
9+
06-01 14:43:23.641 2084 2346 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +237ms
10+
06-01 14:43:25.040 2084 2346 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +255ms
11+
06-01 14:43:26.453 2084 2346 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +247ms
12+
06-01 14:43:27.848 2084 2346 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +239ms
13+
06-01 14:43:29.267 2084 2346 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +247ms
14+
06-01 14:43:30.654 2084 2346 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +231ms
15+
06-01 14:43:32.073 2084 2346 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +248ms
16+
06-01 14:43:33.497 2084 2346 I ActivityTaskManager: Displayed com.companyname.HeadToHeadSpice/crc6421a68941fd0c4613.MainActivity: +251ms
17+
Average(ms): 245.1
18+
Std Err(ms): 2.28254244210266
19+
Std Dev(ms): 7.21803297304743
20+
21+
Size: 7018603 com.companyname.HeadToHeadSpice-Signed.apk
22+
```
23+
24+
.NET MAUI:
25+
```log
26+
06-01 14:50:15.070 2084 2346 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +543ms
27+
06-01 14:50:16.763 2084 2346 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +539ms
28+
06-01 14:50:18.466 2084 2346 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +547ms
29+
06-01 14:50:20.183 2084 2346 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +562ms
30+
06-01 14:50:21.872 2084 2346 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +539ms
31+
06-01 14:50:23.559 2084 2346 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +534ms
32+
06-01 14:50:25.238 2084 2346 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +543ms
33+
06-01 14:50:26.934 2084 2346 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +542ms
34+
06-01 14:50:28.659 2084 2346 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +562ms
35+
06-01 14:50:30.361 2084 2346 I ActivityTaskManager: Displayed com.companyname.headtoheadmaui/crc649f845fb8d5de61df.MainActivity: +542ms
36+
Average(ms): 545.3
37+
Std Err(ms): 2.98161030317511
38+
Std Dev(ms): 9.42867965305853
39+
40+
Size: 12255930 com.companyname.headtoheadmaui-Signed.apk
41+
```
42+
43+
`dotnet new spice-blazor`:
44+
```log
45+
06-01 15:28:30.423 2084 2346 I ActivityTaskManager: Displayed com.companyname.spice.blazor/crc64ea1cd60bbe11594e.MainActivity: +396ms
46+
06-01 15:28:31.995 2084 2346 I ActivityTaskManager: Displayed com.companyname.spice.blazor/crc64ea1cd60bbe11594e.MainActivity: +414ms
47+
06-01 15:28:33.580 2084 2346 I ActivityTaskManager: Displayed com.companyname.spice.blazor/crc64ea1cd60bbe11594e.MainActivity: +417ms
48+
06-01 15:28:35.170 2084 2346 I ActivityTaskManager: Displayed com.companyname.spice.blazor/crc64ea1cd60bbe11594e.MainActivity: +431ms
49+
06-01 15:28:36.771 2084 2346 I ActivityTaskManager: Displayed com.companyname.spice.blazor/crc64ea1cd60bbe11594e.MainActivity: +426ms
50+
06-01 15:28:38.362 2084 2346 I ActivityTaskManager: Displayed com.companyname.spice.blazor/crc64ea1cd60bbe11594e.MainActivity: +408ms
51+
06-01 15:28:39.930 2084 2346 I ActivityTaskManager: Displayed com.companyname.spice.blazor/crc64ea1cd60bbe11594e.MainActivity: +420ms
52+
06-01 15:28:41.509 2084 2346 I ActivityTaskManager: Displayed com.companyname.spice.blazor/crc64ea1cd60bbe11594e.MainActivity: +432ms
53+
06-01 15:28:43.059 2084 2346 I ActivityTaskManager: Displayed com.companyname.spice.blazor/crc64ea1cd60bbe11594e.MainActivity: +394ms
54+
06-01 15:28:44.615 2084 2346 I ActivityTaskManager: Displayed com.companyname.spice.blazor/crc64ea1cd60bbe11594e.MainActivity: +412ms
55+
Average(ms): 415
56+
Std Err(ms): 4.15799096787005
57+
Std Dev(ms): 13.1487219488773
58+
```
59+
60+
`dotnet new maui-blazor`:
61+
```log
62+
06-01 15:51:14.648 2084 2346 I ActivityTaskManager: Displayed com.companyname.foo/crc64808a40cc7e533249.MainActivity: +653ms
63+
06-01 15:51:16.511 2084 2346 I ActivityTaskManager: Displayed com.companyname.foo/crc64808a40cc7e533249.MainActivity: +641ms
64+
06-01 15:51:18.327 2084 2346 I ActivityTaskManager: Displayed com.companyname.foo/crc64808a40cc7e533249.MainActivity: +640ms
65+
06-01 15:51:20.086 2084 2346 I ActivityTaskManager: Displayed com.companyname.foo/crc64808a40cc7e533249.MainActivity: +628ms
66+
06-01 15:51:21.885 2084 2346 I ActivityTaskManager: Displayed com.companyname.foo/crc64808a40cc7e533249.MainActivity: +631ms
67+
06-01 15:51:23.650 2084 2346 I ActivityTaskManager: Displayed com.companyname.foo/crc64808a40cc7e533249.MainActivity: +612ms
68+
06-01 15:51:25.438 2084 2346 I ActivityTaskManager: Displayed com.companyname.foo/crc64808a40cc7e533249.MainActivity: +635ms
69+
06-01 15:51:27.243 2084 2346 I ActivityTaskManager: Displayed com.companyname.foo/crc64808a40cc7e533249.MainActivity: +643ms
70+
06-01 15:51:29.037 2084 2346 I ActivityTaskManager: Displayed com.companyname.foo/crc64808a40cc7e533249.MainActivity: +633ms
71+
06-01 15:51:30.847 2084 2346 I ActivityTaskManager: Displayed com.companyname.foo/crc64808a40cc7e533249.MainActivity: +650ms
72+
Average(ms): 636.6
73+
Std Err(ms): 3.7214095298541
74+
Std Dev(ms): 11.7681302205953
75+
```

0 commit comments

Comments
 (0)