Last month, I shared some Power Automate tips and tricks, of which the majority was focused on improving the runtime of a cloud flow, for multiple reasons. Just like cloud flows benefit from improving the runtime, so do Canvas Apps. In this challenge, you will learn about improving the performance of your canvas app.
🎯 Improve your app load time
🎯 Use delegation warnings effectively
🎯 Assess your own work with the Power Apps Review tool
Improve Loading time
Microsoft has a dedicated docs page that discusses considerations for improving your app performance. There are a few things that I would like to point out, and elaborate on.
The first one is the Concurrent() function. Remember the parallel branches and the concurrency control we've discussed in Challenge 016? The Concurrent() Power Fx function does exactly the same within canvas apps. It will run all the nested commands in parallel, rather that in sequence. Especially if you are initiating many collections when the app starts, this will improve your loading time significantly.
Just like with parallel branching and concurrency control in Power Automate, you have to be mindful of the fact that you don't have control over the order in which the nested functions start, or finish. If there is some dependency between functions, you should run those in sequence, which means taking the depending function out if the Concurrent() function. You could chain Concurrent() functions, so all the f
The snippets and images below are just copied from the docs page, as I think it clearly shows the potential improvement.
Without Concurrent() function
ClearCollect( Product, '[SalesLT].[Product]' );
ClearCollect( Customer, '[SalesLT].[Customer]' );
ClearCollect( SalesOrderDetail, '[SalesLT].[SalesOrderDetail]' );
ClearCollect( SalesOrderHeader, '[SalesLT].[SalesOrderHeader]' )
With Concurrent() function
ClearCollect( Product, '[SalesLT].[Product]' ),
ClearCollect( Customer, '[SalesLT].[Customer]' ),
ClearCollect( SalesOrderDetail, '[SalesLT].[SalesOrderDetail]' ),
ClearCollect( SalesOrderHeader, '[SalesLT].[SalesOrderHeader]' )
Number of Controls
The docs state that your app should not hold more than 500 controls. Initially, you will probably not reach this threshold. But after some iterations, you might have added some functionality, which require controls. As a rule of thumb, I just like to use as little controls as possible, obviously without obsessively reaching this goal. There are two fairly easy ways to do this.
The first tip is using the Creator Kit. One of the many reasons why I am such a big fan of this kit, is that you can build a fully functional screen, with under 10 controls. It eases the branding, reduces development time even further, and thus improves the app's performance. A total no-brainer if you'd ask me.
The second tip is to partition the app, once your app is becoming a bit too big. This way you app stays nimble. The CoE Starter Kit is a great example of this approach. The different modules are partitioned into different solutions, and within the solutions there are multiple applications. At first, I would section it into apps, and once it really becomes a massive solution (just like the CoE Starter Kit), you can think about solution partitioning.
Another way the CoE Starter Kit leads by example, is the way the applications are partitioned. In most cases, they split it by the type of user. Many admin apps are model-driven apps, which can be developed rapidly. Apps for end-users are canvas apps, or custom pages, to make them a bit better looking. The reason why this is recommended, is for testing. added functionality can be bundled for a type of persona, and added in an iteration or sprint. after adding the functionality, you only have to test this user's perspective. You can mix the personas into two apps, but for development reasons, this is not ideal. The CoE team is actually transitioning the model driven apps to use a lot of custom pages for this reason. Each custom page can be developed separately, which is a great way to partition model-driven apps even further.
You have probably seen some delegation warnings within your application while developing. But what is this? And how could you use it to your benefit?
You have data stored somewhere, and you need to load that data temporarily in your application to visualize it, make some adjustments, and send it back to where the data is stored. The location where the data is stored is called the server and your application is the client.
Let's say we store the data in SharePoint. You might have thousands of rows with hundreds of columns. It is impossible to visualize all this detailed data at once, so you would want to transfer this data in your app. For example, you wat to aggregate some values, have the rows filtered on a particular column value, and only show a few of the hundred columns.
This can be done in two ways. Option one is copying all this data from the server (SharePoint) to the client (your canvas apps), store it in your memory, and do the data transformation on the client side, which means on your device. With bigger data sets and devices with limited memory (cheaper/older devices), this can tremendously reduce the performance, and affect the experience in a negative way.
The other option is clearly asking the server what data you are after. The server understands your query, will do the data transformation for you, and will return exactly the data you are after. The client (the canvas app running on your device) will only need to keep this smaller dataset in memory. This second option is called delegation, as you will delegate the hard work to the server.
In our SharePoint example, the calculation is done on Microsoft servers, which have way more calculation power than your beloved mobile device. That's why this is the preferred option. Not all functions are delegable, and this varies between connectors. Which functions on which data type are delegable are documented for the SharePoint, SQL server, Dataverse, and Salesforce connectors.
The delegation error indicates that your Power Fx expression contains a non-delegable function. Next time you will see this, don't just raise the limit from 500 records to 2000, as this will only move the issue slightly, but will not resolve it. Filling a collecting iteratively is also not the way to go, as your collection is also within your memory. Just be thankful for this indication, so we can resolve it and improve the performance of the canvas app.
In some cases, you can just query other columns that contain other data types, that can deal with this function. In some cases, you can just formulate your query in a delegable way, with the same result. Below is an example given of this.
//NON DELEGABLE QUERY
Filter(SHAREPOINTLIST, COLUMN = Blank())
In other cases, you might want to just use a different data type. For example, the Person type in SharePoint is a complex data type, which has less delegable capabilities. It has nice functionality in SharePoint, but if this really causes problems for you, you could change it to a text field containing the email. Or create an additional column just for this purpose. This does requires you being mindful of the consequences.
The last thing I want to mention about delegation, is the order in which you put the functions. As you can read from the documentation, a complex datatype cannot deal with sorting. Below you can see how just the order in which you put the functions can remove any issues. Do you understand why this is not giving any issues?
//ORDER WITH ISSUES
COLUMN = Blank()
//ORDER WORKING FINE
COLUMN = Blank()
Use the Power Apps Review Tool
All the things mentioned above, are best practices while developing a canvas app. You could keep a list of all these things (that is far from complete), or you could use the Power Apps Review tool. It's a tool that you can use to assess your own, or someone else their work. You can download it from this GitHub page.
After downloading and installing the solution, create a review yourself. If you want to reproduce the steps I took, use the Power Apps Training for Office template app.
It will unpack your solution/app and will do some predefined checks automatically. Based on these checks, your app will get a score. This is a quick indication of the quality of the work done, and where there is some room for improvement. This overview can be found on the first page, the Code Review Checklist.
Code Review Checklist
If you expand the image, you can see some checks are OK, some are not. The check that failed in this example is the Use of Concurrent Function. We have discussed this, so we know what the issue is, and how to resolve it. The nice thing about the unpassed checks is that it will also tell you why it would be good to make this check go to an OK state. The View Details section also shows some useful docs links.
It also shows the limitation of this tool. It did not found any Concurrent function. That also means that it will be set to OK, whenever there is a concurrent function somewhere used in the app. Using one concurrent function does not directly means that it is used in an optimal way. An OK thus does not mean that it is perfect.
However, I do think that this is a really useful check. Especially when assessing someone else's work, this is a good indicator of their knowledge level. If this is not used, you can easily link them this challenge, so they will know. If this check is on OK, you can suppose the developer put some thought into optimizing the loading time. Any further investigation can be done after some performance issues are raised. It also means that it might be interesting to also look into the OK stated checks. The Error Handling check is a good example of this (not discussed in this challenge).
A little further down the list, you can also see the delegation check. How lovely that all this is in this tool, right? Two out of three of this challenge are already found in this tool...
App Checker Results
Let's move to the next page, the App Checker Results. Here you can see the output of the last run of the app checker. This is the tool that is executed when pressing the stethoscope.
All things found of interest, are listed on this page. In my case, I get four accessibility recommendations. As we focus on performance in this challenge, we will skip the accessibility for now. I might do a dedicated challenge on this topic, as you want everyone to be able to enjoy what you have created. In this example, two performance improvements are suggested.
The first is removing an unused image. The image is downloaded, without being used. This obviously takes some time, which is a fairly easy improvement.
The second is about unused variables, in the example there are two of them. A variable requires less memory compared to an image, but this is still really easy to improve. Cleaning up just should be part of any deployment.
For the performance category, the following checks are available:
As shown in the picture above, the Power Apps Review tool will tell you how to fix it, and where to look. Besides the performance category, there are the categories Accessibility and Formula. You could look into the admin app if you want to dig a little deeper.
The app overview page will show you a few things. It shows some app settings that can improve the performance. Note that it is not required for every setting to be turned on. However, I would suggest a rule of thumb is to have it on, and turning a setting of, should be argumented.
Below the settings, you see an overview of the screens, the amount of controls on them, and what types are used. As you've read above, there is a cap in the controls. Just scanning through this will tell you quite a bit on the app, and the developer.
On the right there are some other pieces of information that might be of interest. The left side is the more interesting side, where some performance improvements can be found.
There is also a Code Viewer page. Here you can basically find every Power Fx function written in your Canvas App. There is a search function to just browse through the whole app. This function recently also was added to Power Apps Studio, which is a very helpful feature. For that reason, it might not bring any additional value when you are assessing your own work. I do find this feature vey helpful when assessing someone else their work. The fact that you can quickly scan through all of the Power Fx function will help you quickly get an understanding of the developed app, and determine if the check are really OK (e.g. searching for First(Filter combinations).
You can see that it shows the screen, control, and the property of the control where this function is used. By clicking on the plus icon on the right of the gray bar, you can manually add code issues to this control. This way you could implement some predefined check for an application. In the example, the screens are named in PascalCase.
According to the Power Apps canvas apps coding standards and guidelines, you should include a space, as screen readers are used to read aloud the screens. Again an accessibility check, but a good example of a manual check. This way, you can dig further and check it to the provided guidelines, or check it on some internal guidelines.
There is also the possibility to send the report to yourself. This can be used to improve your app in a future iteration, or forward it to the developer whose work you've assessed.
I hope you've enjoyed these tips, and are eager to try to improve your app's performance.
In this challenge, some Microsoft docs pages are linked. Below you will find more pages around performance.
👉🏻 Do multiple things at the same time
👉🏻 Delegate hard work
👉🏻 Keep apps as simple and nimble as possible
👉🏻 Use the Power Apps Review Tool for (self) assessment