{"id":48261,"date":"2020-11-17T10:17:40","date_gmt":"2020-11-17T18:17:40","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/xamarin\/?p=48261"},"modified":"2020-11-17T10:17:40","modified_gmt":"2020-11-17T18:17:40","slug":"fabulous-going-beyond-hello-world","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/xamarin\/fabulous-going-beyond-hello-world\/","title":{"rendered":"Fabulous: Going Beyond Hello World"},"content":{"rendered":"<p><em>This is a guest blog by Timoth\u00e9 Larivi\u00e8re. Timoth\u00e9 is the maintainer of <a href=\"https:\/\/github.com\/fsprojects\/Fabulous\">Fabulous<\/a>: Functional App Development and a Microsoft MVP. You can find him on Twitter <a href=\"https:\/\/twitter.com\/Tim_Lariviere\">@Tim_Lariviere<\/a>.<\/em><\/p>\n<p>In <a href=\"https:\/\/devblogs.microsoft.com\/xamarin\/fabulous-functional-app-development\/\">Fabulous: Functional App Development<\/a>, we saw how to leverage functional programming and the Model-View-Update (MVU) architecture to build mobile and desktop apps with Fabulous. If you haven&#8217;t read it yet, I strongly encourage you to do so first as this blog post will build on it.<\/p>\n<p>A quick reminder on the MVU architecture as used in Fabulous:<br \/>\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/xamarin\/wp-content\/uploads\/sites\/44\/2020\/11\/MVU.png\" alt=\"Image of MVU architecture\" \/><br \/>\n<em>Credits: Beginning Elm (https:\/\/elmprogramming.com)<\/em><\/p>\n<p>MVU uses a single state object (aka the <code>Model<\/code>) for all the data needed by your app. This state is immutable, no one can change it.<br \/>\nThe state is created by the <code>init<\/code> function, and Fabulous passes it to the <code>view<\/code> function to know what to display on the UI.<\/p>\n<p>When the user interacts with the UI, it dispatches messages (aka <code>Msg<\/code>) back to Fabulous to let it know that the state needs to change.<br \/>\nAt this point, Fabulous call the <code>update<\/code> function with the current state and the received message to let you define a brand new state. Fabulous then proceeds to call the <code>view<\/code> function with the new state.<\/p>\n<p>For more information on MVU in Fabulous, please read <a href=\"https:\/\/devblogs.microsoft.com\/xamarin\/fabulous-functional-app-development\/\">Fabulous: Functional App Development<\/a>. By design, Fabulous guarantees that you won&#8217;t have any concurrency issue, even if multiple things go on simultaneously thanks to this <code>Msg<\/code> mechanism. If several messages are received at once, they will be queued and processed one by one.<\/p>\n<p>To ensure reactivity when a new message arrives, the <code>init<\/code>, <code>update<\/code> and <code>view<\/code> functions are synchronous. You don&#8217;t want your app to stop processing messages while calling an API over an EDGE connection.<\/p>\n<p>Querying an API, accessing the storage of the device, interacting with a local database, etc. All these things are necessary when writing real world applications. So how does one make async calls with Fabulous?<\/p>\n<p>Short answer: Side effects.<\/p>\n<h2>Side effects in Fabulous<\/h2>\n<p>In object-oriented programming, side effects are considered bad because they change things outside of their scope and lead to unpredictable behaviors. Functional programming has a broader definition for side effect:<\/p>\n<p>A function is considered pure of side effect when it always gives the same output for the same input without changing anything in the application. Everything else is considered a side effect, even just updating a property of its own class or logging something in the debug console; because the function would leave the app in a different state than it originally was.<\/p>\n<p>Here, Fabulous allows you to run side effects in a controlled manner while keeping the <code>init<\/code> and <code>update<\/code> functions pure and reactive.<br \/>\nThis is done in 2 ways: commands and subscriptions.<\/p>\n<h3>Commands<\/h3>\n<p>Command (shorten to <code>Cmd<\/code>) provides you with a way to tell Fabulous you&#8217;d like to run some side effects. Fabulous will run those for you outside of the main MVU loop, so it can continue to process messages.<\/p>\n<p>At any point in time, you&#8217;ll have the possibility to dispatch a <code>Msg<\/code> to update your app state and your UI. Fabulous will treat this message like any other message coming from the view. Let&#8217;s see how to use <code>Cmd<\/code>. If you remember from the last blog post, we saw the following declaration to bring together all our functions and start the app:<\/p>\n<pre><code class=\"fs\">let program = XamarinFormsProgram.mkSimple init update view\n<\/code><\/pre>\n<p>This declaration wants <code>init<\/code> and <code>update<\/code> to only return a <code>Model<\/code>.<\/p>\n<p>Let&#8217;s change it to <code>mkProgram<\/code>:<\/p>\n<pre><code class=\"fs\">let program = XamarinFormsProgram.mkProgram init update view\n<\/code><\/pre>\n<p>This time, <code>init<\/code> and <code>update<\/code> need to return a tuple of both a <code>Model<\/code> and a <code>Cmd&lt;Msg&gt;<\/code> (the side effect).<\/p>\n<p>We&#8217;ll need to update those functions to return the correct type<\/p>\n<pre><code class=\"fs\">let init () =\n    \/\/ The comma between the model and the Cmd means we're constructing a Tuple (commonly noted as `A * B`)\n    \/\/ Cmd.none means \"no side effect\"\n    { Count = 0 }, Cmd.none \n\nlet update msg model =\n    match msg with\n    | Increment -&gt; { model with Count = model.Count + 1 }, Cmd.none\n    | Decrement -&gt; { model with Count = model.Count - 1 }, Cmd.none\n<\/code><\/pre>\n<p>Let&#8217;s change this code to handle the sign up of a user. We&#8217;ll need a button to let the user choose whether he wants to sign up or not as well as a spinner control to let him know that his request is pending.<\/p>\n<p>The sign-up process can be complex and have multiple results like success and failure (especially when doing unreliable things like an API call). To represent that, we&#8217;ll need to declare 3 <code>Msg<\/code>s: <code>SignUp<\/code> (start the sign-up process), <code>SignUpSuccessful<\/code>, <code>SignUpFailed<\/code>.<\/p>\n<p>Since we&#8217;re calling an API, we&#8217;ll need the sign-up function to be async. To do that, we&#8217;ll use <code>Cmd.ofAsyncMsg<\/code> to tell Fabulous we want to execute an async function that will return a message at the end. <code>Cmd<\/code> has a handful of functions that accept several types of functions, feel free to explore them.<\/p>\n<pre><code class=\"fs\">type Msg =\n    | SignUp\n    | SignUpSuccessful\n    | SignUpFailed of errorMsg: string\n\n\/\/ let! and do! are the equivalent of using await in C#\nlet signUpAsync (email: string) =\n    async {\n        try\n            let! isEmailAlreadyUsed = Api.isEmailInUseAsync email\n            if isEmailAlreadyUsed then\n                return SignUpFailed \"Email already signed up\"\n            else\n                do! Api.signUpAsync email\n                return SignUpSuccessful    \n        with\n        | :? WebException as wex -&gt;\n            return SignUpFailed (\"A network error occurred: \" + wex.Message)\n        | ex -&gt;\n            return SignUpFailed \"An unknown error occurred\"\n    }\n\nlet update msg model =\n    match msg with\n    | SignUp -&gt;\n        \/\/ Sets the IsLoading flag to true (so we can display a spinner)\n        \/\/ and tell Fabulous we want to run the `signUpAsync` function as a side effect\n        { model with IsLoading = true }, Cmd.ofAsyncMsg (signUpAsync model.Email)\n\n    | SignUpSuccessful -&gt;\n        \/\/ We arrive here when the sign up is successful\n        { model with IsLoading = false; SignedUp = true }, Cmd.none\n\n    | SignUpFailed errorMessage -&gt;\n        \/\/ Or here when it failed\n        { model with IsLoading = false; ErrorMessage = errorMessage }, Cmd.none\n\nlet view model dispatch =\n    View.ContentPage(\n        (...)\n\n        if model.IsLoading then\n            View.ActivityIndicator()\n        else\n            View.Button(\n                text = \"Sign up\",\n                command = fun () -&gt; dispatch SignUp\n            )\n\n    )\n<\/code><\/pre>\n<p>The exact same process is used for any asynchronous calls. You can find a few other examples in FabulousContacts like <a href=\"https:\/\/github.com\/TimLariviere\/FabulousContacts\/blob\/f2fd703cc2d546a2355d275d6a3c6f8b32360316\/FabulousContacts\/DetailPage.fs#L32-L62\">using Xamarin.Essentials to start a phone call, author an email, etc.<\/a> or <a href=\"https:\/\/github.com\/TimLariviere\/FabulousContacts\/blob\/f2fd703cc2d546a2355d275d6a3c6f8b32360316\/FabulousContacts\/MainPage.fs#L36-L58\">using SQLite<\/a>.<\/p>\n<p><em>Tips:<\/em> Try to put all side effects in external functions and call them through <code>Cmd<\/code>, even when they are synchronous or could be inlined in the <code>update<\/code> function (like logging, sending metrics to AppCenter, etc.).<\/p>\n<p>This will keep the <code>init<\/code> and <code>update<\/code> functions pure which will let you unit test them easily. We&#8217;ll see together in a later blog post how to unit test all parts of a Fabulous app; UI included!<\/p>\n<h3>Subscriptions<\/h3>\n<p>Subscription (shorten to <code>Sub<\/code>) is a variant of <code>Cmd<\/code>. Declared at the start of the application, it can send messages at any point in time.<br \/>\nThis is typically used when listening to external events like push notifications or system events.<\/p>\n<p><em>Listening to RequestedThemeChanged:<\/em><\/p>\n<pre><code class=\"fs\">module App =\n    type Model = { ... }\n    type Msg = ThemeChanged of Xamarin.Forms.OSAppTheme\n\n    let init () = ...\n    let update msg model = ...\n    let view model dispatch = ...\n\n    let program = ...\n\ntype App() as app =\n    inherit Xamarin.Forms.Application\n\n    let themeChangedSub dispatch =\n        Application.Current.RequestedThemeChanged.AddHandler(\n            EventHandler&lt;AppThemeChangedEventArgs&gt;(fun _ args -&gt;\n                dispatch (App.Msg.ThemeChanged args.RequestedTheme)\n            )\n        )\n\n    let runner =\n        App.program\n        |&gt; Program.withSubscription themeChangedSub\n        |&gt; XamarinFormsProgram.run app\n<\/code><\/pre>\n<h2>Conclusion<\/h2>\n<p>Commands and Subscriptions are an important part of writing real world apps with Fabulous.<br \/>\nThey let you clearly separate the interface logic from the business logic, while ensuring you won&#8217;t encounter unpredictable behaviors when your app grows.<\/p>\n<p>If you want to learn more about commands and subscriptions, please read <a href=\"https:\/\/fsprojects.github.io\/Fabulous\/Fabulous.XamarinForms\/update.html\">the documentation of Fabulous<\/a>.<\/p>\n<p>Like last time, make sure to also visit our GitHub repository for all the latest bits: <a href=\"https:\/\/github.com\/fsprojects\/Fabulous\">https:\/\/github.com\/fsprojects\/Fabulous<\/a>.<br \/>\nWe welcome all contributions, either through issues, discussions or pull requests.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this guest blog from Fabulous maintainer Timoth\u00e9 Larivi\u00e8re shows us the next steps in leveraging F#, MVU, and Fabulous to build mobile apps with Xamarin.<\/p>\n","protected":false},"author":39433,"featured_media":47917,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2,1,291,367],"tags":[713,9160,9185],"class_list":["post-48261","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developers","category-xamarin","category-xamarin-platform","category-xamarin-forms","tag-f","tag-fabulous","tag-functional-app-development"],"acf":[],"blog_post_summary":"<p>In this guest blog from Fabulous maintainer Timoth\u00e9 Larivi\u00e8re shows us the next steps in leveraging F#, MVU, and Fabulous to build mobile apps with Xamarin.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/48261","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/users\/39433"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/comments?post=48261"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/posts\/48261\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media\/47917"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/media?parent=48261"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/categories?post=48261"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/xamarin\/wp-json\/wp\/v2\/tags?post=48261"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}