@@ -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
1165In 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
136236framework and pick and choose the pieces of .NET MAUI that made sense
137237for 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.
315397Hopefully, 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
0 commit comments