Tuesday, December 8, 2020

Faster Apps Better Libraries

It

Has

Been

A

While

First, the nerd stuff

PYDroid

Remember in your past life, PYDroid took this sort of stance

"When PYDroid triggers your view on a render event, it does not take the care to avoid duplicate re-renders when no data has actually changed. We believe it is best to handle re-renders in the UiView layer itself."

Ya so, times have changed thanks to StateFlow and its amazing ability to only fire events when attributes of a state have changed. Yay!

The old onRender(state: S) has changed to an onRender(state: UiRender<S>). The UiRender<S> is an interface which can allows the calling view to subscribe to changes and either emit the full state object, or only emit when a particular attribute on a state changes.

interface UiRender<S> {

  fun render(scope: CoroutineScope, onRender: (S) -> Unit): Unit

  fun <T> distinctBy(distinct: (S) -> T): UiRender<T>

}

This can improve performance dramatically in apps because now only certain relevant parts need to render when their relevant state piece changes. You still have all of the strengths of knowing that your state object is a single source of truth, but also gain the performance benefits of only drawing when you need to.

The distinctBy() function is useful for cases like, you want to show or hide a view based on a loading boolean, but the boolean does not control what data is present in the view. Before you would have:

fun onRender(state: S) {

  handleComplexChange(state)

  handleLoadingChanged(state.isLoading)

  handleDataChanged(state.data)

}

and both of these methods would get called even when just the loading state changed, or just the data state changed. Now you can be more specific:

fun onRender(state: UiRender<S>) {

  state.render { full -> handleComplexChange(full)

  state.distinctBy { it.isLoading }.render { loading -> handleLoadingChanged(loading) }

  state.distinctBy { it.data }.render { data -> handleDataChanged(data) }

}

For views that are more complex or derive their state from multiple attributes, the old expensive way can still be had by simply calling state.render() without any distinct filtering.

Migration should be made less painful by the fact that existing onRender(state: S) functions will continue to work, but are deprecated. Note that if you use your own custom class extending UiView instead of BaseUiView or PrefUiView, you will have to make a small API change to respect the new API. To keep existing behavior, call a plain render similar to the example above's handleComplexChange function call.

For UiViewModels, the state handling has been separated out to make the ViewModel less complex. UiStateViewModel<S> is a ViewModel which only handles accessing and mutating view state, it does not have bind hooks or clear hooks, and does not publish events to a controller. UiStateViewModel is powered by a UiStateModel<S> which is the class that actually handles a view state with accessors and mutators. This can allow you to use the PYDroid state model without needing to extend the full-fat UiViewModel, or even re-use a UiViewState modeled via a UiStateModel but have it interact with completely different UiViewEvents and UiControllerEvents in multiple UiViewModels.

The UiStateModel withState() function has been deprecated. To get the current state from within a state model, simply access it via the state variable. The setState function has also received an extra optional parameter, called andThen: (S) -> Unit, which is called only if the setState call results in a state change. In a future major version these deprecated functions will be removed.

PYDroid has also removed its custom update checking code and review prompting code in favor of Google's Play Store solutions for in-app updates and in-app reviews. This is done to make it easier to maintain these features, as before we were using a home-grown solution and Google's is much more battle tested, despite it resulting in a reliance on the Play Store. But, pyamsoft applications have only ever been published on the Play Store, so I do not see this lock in as a huge issue.

All pyamsoft applications have received support for the full-bleed window style when using gesture navigation. Gone are the days of a colored sliver of navbar where the gesture bar sits on the bottom of the device. Now the gesture bar will be transparent and will give a more modern Android look and feel.

FridgeFriend has gained support for multiple Groups of items instead of just one large Group of items. The multiple groups can be broken up into anything you find logical, like locations, or different fridges, or even by meal or food group. There is no strict system around what you can, and cannot do.

With recent Google restrictions to accessing location, FridgeFriend has made some changes about when it suggests you stores that are nearby. We already did not use Geofencing because of these restrictions, and do not request BACKGROUND_LOCATION, but Google has decreed that even a foreground permission level request, but made by something like WorkManager counts as background work. As such, you will only be able to receive nearby notifications from now on when you open the application.

Plenty more is soon to come and I hope to have a release out soon, but for now enjoy the holidays and stay safe.

Stay tuned!

========================
Follow pyamsoft around the Web for updates and announcements about the newest applications!
Like what I do?

Send me an email at: pyam.soft@gmail.com
Or find me online at: https://pyamsoft.blogspot.com

Follow my Facebook Page
Check out my code on GitHub
=========================