Colin Dodd

One of the joys of working at Bakken & Bæck is that you regularly get the chance to click “New Project” in Android Studio. Last time I got to do that, it was decided that we’d do things using Googles Architecture Components. Rx was out; LiveData was in.

This isn’t going to be a piece comparing the two technologies. Instead I am going to talk about a small utility class NetworkBoundResource and how it can help you architect your apps.

So, what does it do?

Simply, it allows you to return data from database whilst simultaneously fetching the latest data from the network. When the network call is returned, the result can be stored to database and the new result can be broadcast.

To put that in more concrete terms; say you have an app that fetches the weather for the users current location. The first time the user opens the app there is obviously nothing in the database, so you’ll need to request the latest weather information from the network. Once you’ve fetched the weather information you can show it to the user.

It makes sense to cache that weather information to a database. The next time the user requests the weather information it can then be read from the database. This avoids a network call and works offline.

However, the weather obviously changes so if your user is always seeing the weather thats cached in the database your app isn’t going to be very useful. You’ll want to add some logic that decides how long the information in the database should be considered fresh. Once the information is no longer fresh a new network call can be started to get the latest weather information.

Network calls can fail, or be slow, so ideally you’ll also show the old weather information whilst the network call is happening. When the network call is complete the cached information can be replaced with the latest information and the UI can be refreshed.

All of this logic makes for an app that works offline, feels quick, and is kept up to date. NetworkBoundResource will give you all of this functionality and all it requires is the implementation of 4 methods:

loadFromDb is used to return whatever information is stored in the database.

shouldFetch decides whether the cached data is fresh or not. If not a new network request will be triggered.

createCall creates the network request. NetworkBoundResource will take responsibility for triggering the request.

saveCallResult saves the network response into the database. Any mapping of the response into a model object before storing can be done here.

For more concrete implementation details check the section on exposing network status in the official app architecture guide.

Under the hood

NetworkBoundResource works by making use of MediatorLiveData. In essence MediatorLiveData can observe multiple LiveData objects and react to their changes. In this instance there are two LiveData sources; one for the database and one for the network. Both of those LiveData are wrapped into one MediatorLiveData and it is that which is exposed by NetworkBoundResource. More information about MediatorLiveData can be found in the Arch documentation

>yieldtest_login()test_answers.py:11:________________________________________________________________________________________________________________________________________________________________env/lib/python3.6/site-packages/tornado/gen.py:1055:inrunvalue=future.result()env/lib/python3.6/site-packages/tornado/concurrent.py:238:inresultraise_exc_info(self._exc_info)<string>:4:inraise_exc_info???env/lib/python3.6/site-packages/tornado/gen.py:1143:inhandle_yieldself.future=convert_yielded(yielded)env/lib/python3.6/functools.py:803:inwrapperreturndispatch(args[0].__class__)(*args,**kw)________________________________________________________________________________________________________________________________________________________________yielded=<generatorobjecttest_loginat0x102420728>defconvert_yielded(yielded):"""Convert a yielded object into a `.Future`.
The default implementation accepts lists, dictionaries, and Futures.
If the `~functools.singledispatch` library is available, this function
may be extended to support additional types. For example::
@convert_yielded.register(asyncio.Future)
def _(asyncio_future):
return tornado.platform.asyncio.to_tornado_future(asyncio_future)
.. versionadded:: 4.1
"""# Lists and dicts containing YieldPoints were handled earlier.ifyieldedisNone:returnmomentelifisinstance(yielded,(list,dict)):returnmulti(yielded)elifis_future(yielded):returnyieldedelifisawaitable(yielded):return_wrap_awaitable(yielded)else:>raiseBadYieldError("yielded unknown object %r"%(yielded,))Etornado.gen.BadYieldError:yieldedunknownobject<generatorobjecttest_loginat0x102420728>env/lib/python3.6/site-packages/tornado/gen.py:1283:BadYieldError

WTF?

So there must be a stupid error, we check for typos, we go back and start copy/pasting code from the working test_schema to make sure we didn’t type @pytest.mark.test_gen or something. The failure remains.

After a while we reach the state where test_schema.py and test_answer.py is byte-for-byte identical, but answers fails and schema passes. We go home and rethink our lives.

Next day, we realise that when called on just one of those files, pytest will run TWO tests, it will find the test_login through the import as well as the test in the files we invoke on. And the order will be different, it will order the file alphabetically - so in case of test_answers it will first run that test, then test_login, but for test_schema the login test will run first.

WTF?

Renaming test_answers to test_manswers (sorted after login) confirms it, it then works.

But why does the order matter? Digger a bit deeper, we see that the value returned from test_login is in both cases of type generator. But Tornado is happy with one of them, but not the other. In the convert_yielded function (which among other things lets tornado also work with await/async generators), Tornado uses inspect.isawaitable to check if the passed generator can actually be a future. This is False when the test fails.

This is the code for isawaitable:

defisawaitable(object):"""Return true if object can be passed to an ``await`` expression."""return(isinstance(object,types.CoroutineType)orisinstance(object,types.GeneratorType)andbool(object.gi_code.co_flags&CO_ITERABLE_COROUTINE)orisinstance(object,collections.abc.Awaitable))

It’s the co_flags line that causes out problem - in the working case, the flag for being an iterable coroutine is set. co_flags is pretty deep in the python internals, containing a number of flags for the interpreter (the inspect docs has the full list). Our CO_ITERABLE_COROUTINE flag was added in in PEP492, which says that:

And there the function __code__ object is modified in place, setting the flag! Setting a breakpoint there lets us see that pytest-tornado calls tornado.gen.coroutine on our function, which in turn calls types.coroutine:

# On Python 3.5, set the coroutine flag on our generator, to allow it# to be used with 'await'.wrapped=funcifhasattr(types,'coroutine'):func=types.coroutine(func)

And this is how the test_login function only works if once called first as a pytest.

¯\_(ツ)_/¯

In the end, that’s the explanation, but there is no real solution - we cannot rely on having the tests in alphabetical order, so we move the reusable code out to it’s own function:

Ezekiel Aquino

This might be caused by a variety of reasons: messy/unstructured layers and groups, there might be stray raster images, or just the exporter naturally embedding its own fingerprint on it. For cases like these you might be surprised by how much you can shave off an SVG, and how much cleaner the markup can be specially if you have to work with it. Who doesn’t want something cleaner and lighter?

This is a very long line that will still be quoted properly when it wraps. Oh boy let’s keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can putMarkdown into a blockquote.

When using SVG assets then I suggest passing it through Jake Archibald’s SVGO online compressor a nice online tool to optimise your SVGs. You can also add it to your build setup or do it via Sketch plugins.

There’s a bunch of settings you can fiddle around with and get the best settings for what you are trying to achieve; you don’t want to “collapse useless groups” if you’ve structured the groups for purposes of animation, for example.

Colin Dodd

If this is true, do this. If this is false, do that. But what about null?

If the user is logged in, show the “My Profile” button. If the user isn’t logged in, show the “Log In” button. It’s such a common thing to do that I’ve probably done it thousands, if not tens of thousands of times. It’s so common there are even jokes about it.

One of the major advantages of Kotlin is that it distinguishes between nullable objects and non-nullable objects. This includes the Boolean type:

Except, this doesn’t work because isLoggedIn can now be in three states, true, false, and null. Whereas the if statement only works with non-nullable booleans. We can rectify this by explicitly checking the state:

if(isLoggedIn==true)

This works, and is readable, but when code reviewing it can be hard to distinguish this from a new beginner mistake, causing it to be incorrectly flagged as an error. That said, this seems to be the recommended suggestion from the Kotlin style guide.

An alternative that may be preferable, is to use when: java when(isLoggedin) { true -> {} false -> {} null -> {} }

This works well if you want to branch based on the state of the boolean, but perhaps is a bit too verbose if you only want to do something in only one of the booleans states.

Colin Dodd

Now you see me, now you don’t. Improving Android visibility with Kotlin extension functions

Extension functions are one of the most powerful features of Kotlin. They allow for classes to be extended with your own logic no matter how locked down the class is.

This gave me the opportunity to fix one of my personal pet peeves in Android.

Views have three states of visibility. VISIBLE, the view can be seen. INVISIBLE, the view can not be seen but it takes up the same amount of space as it would have done were it visible. GONE, the view can not be seen and it takes up no space.

So many times I have ended up in a situation where I want to set the visibility of a view based on the state of a boolean.

Since views have three states there is no easy say to just map a boolean to visibility state. There is no setVisibility(true) – there has always had to be some wrapping logic. Well now that extension functions exist I can fix that once and for all:

Finally! Now I can just call isVisible on any view with a boolean and the visibility of the view will be set to either VISIBLE or GONE. Of course, if you actually want views to become INVISIBLE, then you’ll need a second extension function. Or you could give your extension function a default argument:

privatefunView.isVisible(bool:Boolean?,nonVisibleState:Int=View.GONE){visibility=if(bool==true)View.VISIBLEelsenonVisibleState}privatefunexample(){// this defaults to View.GONEbutton1.isVisible(isLoggedIn)// this uses View.INVISIBLEbutton2.isVisible(isLoggedIn,View.INVISIBLE)}