<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/">
	<channel>
		<title>Implementing Customized Model-View-ViewModel with ViewStates | Infinum</title>
		<atom:link href="https://infinum.com/blog/mvvm-architecture/feed/" rel="self" type="application/rss+xml" />
		<link>https://infinum.com/blog/mvvm-architecture/</link>
		<description>Building digital products</description>
		<lastBuildDate>Wed, 29 Apr 2026 14:21:05 +0000</lastBuildDate>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>

					<item>
				<image>
					<url>7914https://infinum.com/uploads/2020/10/MVVM-architecture-0.webp</url>
				</image>
				<title>Implementing Custom Architecture based on Model-View-ViewModel with ViewStates</title>
				<link>https://infinum.com/blog/mvvm-architecture/</link>
				<pubDate>Wed, 21 Oct 2020 12:20:00 +0000</pubDate>
				<dc:creator>Marko Docevski</dc:creator>
				<guid isPermaLink="false">https://infinum.com/the-capsized-eight/mvvm-architecture/</guid>
				<description>
					<![CDATA[<p>Setting up a solid foundation for building our Android apps, with Model-View-ViewModel and ViewStates.</p>
<p>The post <a href="https://infinum.com/blog/mvvm-architecture/">Implementing Custom Architecture based on Model-View-ViewModel with ViewStates</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</description>
				<content:encoded>
					<![CDATA[<div
	class="wrapper"
	data-id="es-271"
	 data-animation-target='inner-items'>
		
			<div class="wrapper__inner">
			<div class="block-blog-content js-block-blog-content">
	
<div class="block-blog-content-sidebar" data-id="es-92">
	</div>

<div class="block-blog-content-main">
	
<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-95"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-93">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-94'
	>
	To shape the future of Android development, Google released a set of libraries, tools and guidelines under the name <a href="https://developer.android.com/jetpack">Android Jetpack</a>. An essential part of Jetpack are the <a href="https://developer.android.com/topic/libraries/architecture">Architecture Components</a>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-98"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-96">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-97'
	>
	Architecture Components are a set of libraries intended to facilitate designing and developing robust, testable and maintainable apps. We’ll take a look at the two most commonly used, <a href="https://developer.android.com/topic/libraries/architecture/viewmodel">ViewModel</a> and <a href="https://developer.android.com/topic/libraries/architecture/livedata">LiveData</a>, and how our team used them to implement our variant of the <a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel">Model-View-ViewModel (MVVM)</a> architecture pattern.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-101"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-99">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-100'
	>
	Trouble working with lifecycles</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-104"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-102">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-103'
	>
	Developers often run into difficulty when working with Activities and Fragments in determining the correct callback method to use to perform an action such as:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-107"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-105">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-106'
	>
	<li>Saving and restoring the state of the individual views.</li><li>Starting and canceling network requests, long-running jobs </li><li>Opening and closing database connections.</li><li>Releasing resources kept in memory (media, bitmaps, buffer streams).</li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-110"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-108">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-109'
	>
	Common issues include:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-113"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="lists" data-id="es-111">
	<ul	class='typography typography--size-16-text-roman js-typography lists__typography'
	data-id='es-112'
	>
	<li>Knowing when to release resources, since an Activity&#8217;s onDestroy callback is not guaranteed to be called. </li><li>Having to reinitialize presentation objects (Presenter or Controller) and their dependencies after each configuration change.</li><li>Properly saving and restoring the full state of the screen during configuration changes. While saved instance state bundles, retained configuration instance objects, headless fragments, repositories, caches, etc. were used for this, fully storing and restoring the state was never trivial. </li><li>Committing fragment transactions in asynchronous callbacks, while an activity is Stared, but not Resumed. </li></ul></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-118"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="blockquote block-blockquote__blockquote" data-id="es-114">
	
	<div class="blockquote__content">
		<i
	class="icon blockquote__icon icon--size-16 icon--scale-100"
	 aria-hidden='true' data-name='blockquote-24' data-id='es-115'>
	<svg fill='none' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path clip-rule='evenodd' d='m12 24c6.6274 0 12-5.3726 12-12 0-2.79685-.9568-5.37021-2.561-7.41062-.581.22951-1.0832.60583-1.5069 1.12898-.5132.60844-.7698 1.41969-.7698 2.43375v.07605h2.5789v5.59004h-5.6197v-5.01962c0-1.11547.154-2.06616.4619-2.85205.3336-.81125.757-1.48307 1.2702-2.01545.528-.52161 1.1175-.92155 1.7687-1.1998-2.0728-1.70651-4.7279-2.73128-7.6223-2.73128-6.62742 0-12 5.37258-12 12 0 6.6274 5.37258 12 12 12zm-3.53811-18.05347c-.30793.78589-.46189 1.73658-.46189 2.85205v5.01962h5.6197v-5.59004h-2.5789v-.07605c0-1.01406.2566-1.82531.7698-2.43375.5389-.63379 1.1804-1.05209 1.9245-1.2549v-2.28164c-.7441.07605-1.4626.25351-2.1555.53238-.6928.27887-1.3086.68449-1.84752 1.21688-.51321.53238-.9366 1.2042-1.27019 2.01545z' fill='currentColor' fill-rule='evenodd'/></svg></i><p	class='typography typography--size-36-text js-typography blockquote__quote'
	data-id='es-116'
	>
	The ViewModel and LiveData components are lifecycle-aware, and help us get around issues like these.</p>
		<div class="blockquote__caption-wrap">
					</div>
	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-121"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-119">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-120'
	>
	Model View View-Model</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-124"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-122">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-123'
	>
	The <strong>ViewModel</strong> is an essential component of the <strong>MVVM architectural pattern</strong>, commonly used for designing mobile and desktop applications.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-127"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-125">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-126'
	>
	Architecture Components only provides us a ViewModel implementation, and we’re free to implement the View and Model in any way we see fit. This <a href="https://developer.android.com/reference/androidx/lifecycle/ViewModel?hl=en">ViewModel class</a> is responsible for the presentation data and logic for its scope. A <strong>ViewModel’s scope</strong> can be either an <strong>Activity</strong> or a <strong>Fragment</strong>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-130"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-128">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-129'
	>
	We instantiate a ViewModel in its scope with a <a href="https://developer.android.com/reference/androidx/lifecycle/ViewModelProvider">ViewModelProvider</a>, during the scope’s creation.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-133"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-131">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-132'
	>
	When an Activity or Fragment is recreated due to a configuration change, it can immediately access the data stored in the ViewModel. We no longer have to save and restore the state by serializing it to a bundle, which might work well for small sets of primitive data, but is not that practical for big collections, images, audio streams, etc.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-136"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-134"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-135">
	<picture class="image__picture block-media__image-picture">
								
			<source
				srcset=https://infinum.com/uploads/2020/10/MVVM-architecture-1-1400x387.webp				media='(max-width: 699px)'
				type=image/webp								height="387"
												width="1400"
				 />
												<img
					src="https://infinum.com/uploads/2020/10/MVVM-architecture-1.webp"
					class="image__img block-media__image-img"
					alt=""
										height="462"
															width="1672"
										loading="lazy"
					 />
					</picture>

	</figure></div></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-139"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-137">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-138'
	>
	Another important component of the MVVM architecture pattern is exposing the data from the ViewModel to the View (Activities, Fragments, custom Views) by the use of the <strong>Observer pattern</strong>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-142"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-140">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-141'
	>
	<a href="https://developer.android.com/reference/androidx/lifecycle/LiveData?hl=en">LiveData</a> is an implementation of the pattern, and it is an observable data stream that is Lifecycle aware. To those familiar with <strong>RxJava</strong>, LiveData has some limited set of the functionality of an RxJava <strong>Observable</strong>. With the inclusion of some additional libraries, like <a href="https://github.com/adibfara/Lives">Lives</a>, we can get most of the functionality we’re used to in RxJava, like filtering, combining and transforming operators.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-145"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-143">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-144'
	>
	A View will start observing an exposed LiveData and modify its UI according to the values it observes. While we can set a LiveData’s value at any time, the LiveData will only notify the observing View, when it is in at least its Started state. </p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-148"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-146">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-147'
	>
	Improving on the basic MVVM pattern</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-151"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-149">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-150'
	>
	In basic MVVM implementations, each piece of data that needs to be bound to a UI element should be exposed through its own LiveData, matched with an Observer instance in the View. For simple screens, this wouldn’t be much of an issue, but as complexity grows so would the number of LiveDatas and Observers active at the same time, which could potentially impact performance.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-154"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-152">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-153'
	>
	At Infinum, to go around this limitation we have borrowed the concept of a <strong>ViewState</strong>, used in <a href="http://hannesdorfmann.com/android/mosby3-mvi-1">MVI (Model View Intent)</a>, another popular architecture pattern for mobile and web applications.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-157"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-155">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-156'
	>
	A <strong>ViewState</strong> object contains all the data required to describe the state of every subcomponent of the View at once. Implementing a ViewState in Kotlin is trivial, it can be done with a <strong>data class</strong>, containing only primitives and other data classes, where all the data classes would have the same restriction, all the way down in the structure.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-160"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-158">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-159'
	>
	Data classes provide equality comparison and toString out-of-the-box. Now, we only need to expose a single LiveData that holds the current ViewState.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-163"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-161">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-162'
	>
	Let’s say we are to implement a screen with a list of TV shows that the user is currently watching. The ViewModel contains a private <strong>ShowsState MutableLiveData</strong> so it can change its value, but it is exposed as a <strong>regular LiveData</strong> so it is protected and cannot be changed from outside.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-165"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token">val </span><span class="token">showsState </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token">MutableLiveData</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">&gt;</span><span class="token">()
</span></span><span class="line"><span class="token">val </span><span class="token">state</span><span class="token">: </span><span class="token" style="color: #6f42c1;">LiveData</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">&gt; </span><span class="token" style="color: #d73a49;">=</span><span class="token"> showsState
</span></span><span class="line"><span class="token">
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-168"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-166">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-167'
	>
	And the states could be:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-170"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">class ShowsState
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">object</span><span class="token"> </span><span class="token" style="color: #6f42c1;">NoShowsFound</span><span class="token"> </span><span class="token">: </span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">()
</span></span><span class="line"><span class="token" style="color: #d73a49;">data</span><span class="token"> </span><span class="token">class ShowListFetched</span><span class="token">(</span><span class="token">val </span><span class="token">shows</span><span class="token">: </span><span class="token" style="color: #6f42c1;">List</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">Show</span><span class="token">&gt;</span><span class="token">) </span><span class="token">: </span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">()
</span></span><span class="line"><span class="token" style="color: #d73a49;">data</span><span class="token"> </span><span class="token">class ErrorFetchingShows</span><span class="token">(</span><span class="token">val </span><span class="token">errorMessage</span><span class="token">: </span><span class="token" style="color: #6f42c1;">String</span><span class="token">) </span><span class="token">: </span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">()
</span></span><span class="line"><span class="token"> 
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-173"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-171">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-172'
	>
	Another Kotlin language feature that comes in handy with ViewStates is <strong>sealed classes</strong>. If we make our screen’s base state class sealed, then we are forced to enumerate all the possible states the screen can be in, in one file. The enumeration helps us when we’re using the state object as a parameter of a when statement, a <strong>compile-time check</strong> tells us if we’ve covered all the states.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-176"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-174">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-175'
	>
	Additionally, if we nest the extending state classes in the base class, we can use the <strong>IDE autocomplete</strong> feature, we only need to type in the name of the base state class, and we get a full list of all possible states.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-179"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-177">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-178'
	>
	If we applied the nesting we’d have these states:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-181"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">sealed</span><span class="token"> </span><span class="token">class ShowsState </span><span class="token">{
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">object</span><span class="token"> </span><span class="token" style="color: #6f42c1;">NoneFound</span><span class="token">: </span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">()
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">data</span><span class="token"> </span><span class="token">class Fetched</span><span class="token">(</span><span class="token">val </span><span class="token">shows</span><span class="token">: </span><span class="token" style="color: #6f42c1;">List</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">Show</span><span class="token">&gt;</span><span class="token">)</span><span class="token">: </span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">()
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">data</span><span class="token"> </span><span class="token">class Error</span><span class="token">(</span><span class="token">val </span><span class="token">message</span><span class="token">: </span><span class="token" style="color: #6f42c1;">String</span><span class="token">)</span><span class="token">: </span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">()
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token"> 
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-184"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-182">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-183'
	>
	Which we would observe:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-186"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">fun observeState</span><span class="token">(state</span><span class="token">: </span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">) {
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">when</span><span class="token">(state) {
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #d73a49;">is</span><span class="token"> ShowsState.NoneFound </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> </span><span class="token">displayNoneFound</span><span class="token">()
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #d73a49;">is</span><span class="token"> ShowsState.Fetched </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> </span><span class="token">displayShows</span><span class="token">(state.shows)
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #d73a49;">is</span><span class="token"> ShowsState.Error </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> </span><span class="token">displayErrorState</span><span class="token">(state.message)
</span></span><span class="line"><span class="token">   }
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token"> 
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-189"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-187">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-188'
	>
	A very common functionality we have in almost every feature is to display the progress of some action, or to notify the user of an error occurring in some specific way. To cut down on writing almost identical variants of the same concept, and for the sake of consistency throughout all features in the app, we define a set of <strong>common states</strong>, which coexist with the set of states specific to each feature.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-192"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-190">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-191'
	>
	A common ViewState object contains the <strong>current error state and loading state</strong>:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-194"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">data</span><span class="token"> </span><span class="token">class CommonState</span><span class="token">(
</span></span><span class="line"><span class="token">   </span><span class="token">val </span><span class="token">loadingState</span><span class="token">: </span><span class="token" style="color: #6f42c1;">LoadingState</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> LoadingState.Idle,
</span></span><span class="line"><span class="token">   </span><span class="token">val </span><span class="token">errorState</span><span class="token">: </span><span class="token" style="color: #6f42c1;">ErrorState</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> ErrorState.NoError
</span></span><span class="line"><span class="token">)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">sealed</span><span class="token"> </span><span class="token">class LoadingState </span><span class="token">{
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">object</span><span class="token"> </span><span class="token" style="color: #6f42c1;">Idle</span><span class="token"> </span><span class="token">: </span><span class="token" style="color: #6f42c1;">LoadingState</span><span class="token">()
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">object</span><span class="token"> </span><span class="token" style="color: #6f42c1;">Loading</span><span class="token"> </span><span class="token">: </span><span class="token" style="color: #6f42c1;">LoadingState</span><span class="token">()
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">sealed</span><span class="token"> </span><span class="token">class ErrorState </span><span class="token">{
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">object</span><span class="token"> </span><span class="token" style="color: #6f42c1;">NoError</span><span class="token"> </span><span class="token">: </span><span class="token" style="color: #6f42c1;">ErrorState</span><span class="token">()
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">object</span><span class="token"> </span><span class="token" style="color: #6f42c1;">UnknownError</span><span class="token">: </span><span class="token" style="color: #6f42c1;">ErrorState</span><span class="token">()
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">data</span><span class="token"> </span><span class="token">class Error</span><span class="token">(</span><span class="token">val </span><span class="token">message</span><span class="token">: </span><span class="token" style="color: #6f42c1;">String</span><span class="token">) </span><span class="token">: </span><span class="token" style="color: #6f42c1;">ErrorState</span><span class="token">()
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token"> 
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-197"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-195">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-196'
	>
	With a combination of the feature-specific and common ViewState-s, we can instruct the View from the ViewModel to completely form the UI. The View itself can be destroyed and recreated an arbitrary amount of times, due to whatever reason, but we’re still able to recreate it consistently, and without having to save and restore instances, and manage states on several different levels like in the good old days.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-200"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-198">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-199'
	>
	On the other hand, some information we do not want to embed in the ViewState. Instead, we want to show it only once.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-203"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-201">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-202'
	>
	For that purpose we have <strong>Events</strong>, and use them mostly for <strong>displaying alerts, snackbars or other kinds of on-screen notifications</strong>. Events are also exposed to the View through a <strong>SingleLiveEvent</strong>, an extension of LiveData based on <a href="https://proandroiddev.com/livedata-with-single-events-2395dea972a8">SingleLiveEvent</a>. This live data emits any event only once to its subscribers, and will not re-emit in case the observer’s lifecycle owner is recreated due to a configuration change.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-206"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-204">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-205'
	>
	An extensible base for our Views and ViewModels</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-209"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-207">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-208'
	>
	A common practice we have at Infinum is creating base implementations for the most commonly used components. For the MVVM implementation, we create a <strong>BaseFragment</strong> and <strong>BaseViewModel</strong>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-212"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-210">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-211'
	>
	The BaseViewModel class exposes three data streams as LiveData-s, specifically:<br>– <strong>Feature state</strong><br>– <strong>Feature events</strong><br>– <strong>Common state</strong></p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-214"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">open</span><span class="token"> </span><span class="token">class BaseViewModel</span><span class="token" style="color: #6f42c1;">&lt;State: Any, Event: Any&gt;</span><span class="token"> </span><span class="token">: </span><span class="token" style="color: #6f42c1;">ViewModel</span><span class="token">() {
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token">val </span><span class="token">stateLiveData</span><span class="token">: </span><span class="token" style="color: #6f42c1;">MutableLiveData</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">State</span><span class="token">&gt; </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token">MutableLiveData</span><span class="token">()
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token">val </span><span class="token">commonStateLiveData</span><span class="token">: </span><span class="token" style="color: #6f42c1;">MutableLiveData</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">CommonState</span><span class="token">&gt; </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token">MutableLiveData</span><span class="token">()
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">private</span><span class="token"> </span><span class="token">val </span><span class="token">eventLiveData</span><span class="token">: </span><span class="token" style="color: #6f42c1;">LiveEvent</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">Event</span><span class="token">&gt; </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token">LiveEvent</span><span class="token">()
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token">fun stateHolder</span><span class="token">()</span><span class="token">: </span><span class="token" style="color: #6f42c1;">LiveData</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">State</span><span class="token">&gt; </span><span class="token" style="color: #d73a49;">=</span><span class="token"> stateLiveData
</span></span><span class="line"><span class="token">   </span><span class="token">fun commonStateHolder</span><span class="token">()</span><span class="token">: </span><span class="token" style="color: #6f42c1;">LiveData</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">CommonState</span><span class="token">&gt; </span><span class="token" style="color: #d73a49;">=</span><span class="token"> commonStateLiveData
</span></span><span class="line"><span class="token">   </span><span class="token">fun eventHolder</span><span class="token">()</span><span class="token">: </span><span class="token" style="color: #6f42c1;">LiveData</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">Event</span><span class="token">&gt; </span><span class="token" style="color: #d73a49;">=</span><span class="token"> eventLiveData
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token"> 
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-217"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-215">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-216'
	>
	It also contains <strong>convenience methods</strong> for changing the common ViewState.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-219"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">protected</span><span class="token"> </span><span class="token">fun showLoading</span><span class="token">() {
</span></span><span class="line"><span class="token">   commonState </span><span class="token" style="color: #d73a49;">=</span><span class="token"> commonState</span><span class="token">.copy</span><span class="token">(loadingState </span><span class="token" style="color: #d73a49;">=</span><span class="token"> LoadingState.Loading)
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">protected</span><span class="token"> </span><span class="token">fun hideLoading</span><span class="token">() {
</span></span><span class="line"><span class="token">   commonState </span><span class="token" style="color: #d73a49;">=</span><span class="token"> commonState</span><span class="token">.copy</span><span class="token">(loadingState </span><span class="token" style="color: #d73a49;">=</span><span class="token"> LoadingState.Idle)
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token" style="color: #d73a49;">protected</span><span class="token"> </span><span class="token">fun showError</span><span class="token">(message</span><span class="token">: </span><span class="token" style="color: #6f42c1;">String</span><span class="token"> </span><span class="token" style="color: #d73a49;">=</span><span class="token"> </span><span class="token" style="color: #032f62;">&quot;&quot;</span><span class="token">) {
</span></span><span class="line"><span class="token">   commonState </span><span class="token" style="color: #d73a49;">=</span><span class="token"> commonState</span><span class="token">.copy</span><span class="token">(errorState </span><span class="token" style="color: #d73a49;">=</span><span class="token"> ErrorState</span><span class="token">.Error</span><span class="token">(message))
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">fun dismissError</span><span class="token">() {
</span></span><span class="line"><span class="token">   commonState </span><span class="token" style="color: #d73a49;">=</span><span class="token"> commonState</span><span class="token">.copy</span><span class="token">(errorState </span><span class="token" style="color: #d73a49;">=</span><span class="token"> ErrorState.NoError)
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token"> 
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-222"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-220">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-221'
	>
	The BaseFragment class that contains code that is shared by all the Fragments in the application, including basic UI setup, dependency injection, shared resources and utilities, and most importantly the wiring between the View and the ViewModel.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-225"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-223">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-224'
	>
	It is provided with an instance of its ViewModel, and it starts observing the streams from the ViewModel, as soon as it is ready.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-227"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">abstract</span><span class="token"> </span><span class="token">class BaseFragment</span><span class="token" style="color: #6f42c1;">&lt;State : Any, Event : Any&gt;</span><span class="token">() </span><span class="token">: </span><span class="token" style="color: #6f42c1;">Fragment</span><span class="token">() {
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">abstract</span><span class="token"> </span><span class="token">fun provideBaseViewModel</span><span class="token">()</span><span class="token">: </span><span class="token" style="color: #6f42c1;">BaseViewModel</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">State</span><span class="token">, </span><span class="token" style="color: #6f42c1;">Event</span><span class="token">&gt;?
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">override</span><span class="token"> </span><span class="token">fun onViewCreated</span><span class="token">(view</span><span class="token">: </span><span class="token" style="color: #6f42c1;">View</span><span class="token">, savedInstanceState</span><span class="token">: </span><span class="token" style="color: #6f42c1;">Bundle</span><span class="token">?</span><span class="token">) {
</span></span><span class="line"><span class="token">        </span><span class="token" style="color: #005cc5;">super</span><span class="token">.onViewCreated</span><span class="token">(view, savedInstanceState)
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">        </span><span class="token">provideBaseViewModel</span><span class="token">()</span><span class="token">?.apply </span><span class="token">{
</span></span><span class="line"><span class="token">            </span><span class="token">stateHolder</span><span class="token">()</span><span class="token">?.observe</span><span class="token">(viewLifecycleOwner, </span><span class="token">Observer </span><span class="token">{ state </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> </span><span class="token">handleState</span><span class="token">(state) })
</span></span><span class="line"><span class="token">            </span><span class="token">commonStateHolder</span><span class="token">()</span><span class="token">?.observe</span><span class="token">(viewLifecycleOwner, </span><span class="token">Observer </span><span class="token">{ state </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> </span><span class="token">handleCommonState</span><span class="token">(state) })
</span></span><span class="line"><span class="token">            </span><span class="token">eventHolder</span><span class="token">()</span><span class="token">?.observe</span><span class="token">(viewLifecycleOwner, </span><span class="token">Observer </span><span class="token">{ event </span><span class="token" style="color: #d73a49;">-&gt;</span><span class="token"> </span><span class="token">handleEvent</span><span class="token">(event) })
</span></span><span class="line"><span class="token">        }
</span></span><span class="line"><span class="token">    }
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">abstract</span><span class="token"> </span><span class="token">fun handleState</span><span class="token">(state</span><span class="token">: </span><span class="token" style="color: #6f42c1;">State</span><span class="token">)
</span></span><span class="line"><span class="token">    </span><span class="token" style="color: #d73a49;">abstract</span><span class="token"> </span><span class="token">fun handleEvent</span><span class="token">(event</span><span class="token">: </span><span class="token" style="color: #6f42c1;">Event</span><span class="token">)
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token"> 
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-230"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-228">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-229'
	>
	Implementing a feature’s View and ViewModel</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-233"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-231">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-232'
	>
	The BaseFragment contains an implementation for handleCommonState, but the extending feature fragments must implement the handleState and handleEvent methods. User interactions are signaled from the View to the ViewModel through methods that the ViewModel exposes.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-235"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token">class ShowsFragment </span><span class="token">: </span><span class="token" style="color: #6f42c1;">BaseFragment</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">, </span><span class="token" style="color: #6f42c1;">ShowsEvents</span><span class="token">&gt;</span><span class="token">() {
</span></span><span class="line"><span class="token">    addNewShowButton</span><span class="token">.setOnClickListener </span><span class="token">{
</span></span><span class="line"><span class="token">        viewModel</span><span class="token">.addNewShow</span><span class="token">(showTitle)
</span></span><span class="line"><span class="token">    }
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">    showsSwipeRefreshView</span><span class="token">.setOnRefreshListener </span><span class="token">{
</span></span><span class="line"><span class="token">        viewModel</span><span class="token">.refreshShowsData</span><span class="token">()
</span></span><span class="line"><span class="token">    }
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token"> 
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-238"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-236">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-237'
	>
	It is very important to be <strong>consistent</strong> and <strong>thorough</strong> when <strong>defining the feature-specific ViewState-s</strong>.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-241"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-239">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-240'
	>
	Let’s examine the case where we add a search functionality to the Shows screen. We could leave the states as they are, and simply pass the search query from the View to the ViewModel, after which it will process the query, and return a new state containing a list of shows passing the search criteria, and we’d get a working search screen. But it will only work up until the point the user rotates their device.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-244"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-242">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-243'
	>
	After the fragment is recreated, the search query will be lost, because it’s not stored in the state. If we add it to the state, the complete version of the state class would look like this:</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-246"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-code">
	<pre class="phiki language-kotlin github-light" data-language="kotlin" style="background-color: #fff;color: #24292e;"><code><span class="line"><span class="token" style="color: #d73a49;">sealed</span><span class="token"> </span><span class="token">class ShowsState </span><span class="token">{ 
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">abstract</span><span class="token"> </span><span class="token">val </span><span class="token">searchQuery</span><span class="token">: </span><span class="token" style="color: #6f42c1;">String</span><span class="token">
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">data</span><span class="token"> </span><span class="token">class NoneFound</span><span class="token">(</span><span class="token" style="color: #d73a49;">override</span><span class="token"> </span><span class="token">val </span><span class="token">searchQuery</span><span class="token">: </span><span class="token" style="color: #6f42c1;">String</span><span class="token">)</span><span class="token">: </span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">()
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">data</span><span class="token"> </span><span class="token">class Fetched</span><span class="token">(
</span></span><span class="line"><span class="token">       </span><span class="token">val </span><span class="token">shows</span><span class="token">: </span><span class="token" style="color: #6f42c1;">List</span><span class="token">&lt;</span><span class="token" style="color: #6f42c1;">Show</span><span class="token">&gt;</span><span class="token">,
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #d73a49;">override</span><span class="token"> </span><span class="token">val </span><span class="token">searchQuery</span><span class="token">: </span><span class="token" style="color: #6f42c1;">String</span><span class="token">
</span></span><span class="line"><span class="token">   )</span><span class="token">: </span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">()
</span></span><span class="line"><span class="token">
</span></span><span class="line"><span class="token">   </span><span class="token" style="color: #d73a49;">data</span><span class="token"> </span><span class="token">class Error</span><span class="token">(
</span></span><span class="line"><span class="token">       </span><span class="token">val </span><span class="token">message</span><span class="token">: </span><span class="token" style="color: #6f42c1;">String</span><span class="token">,
</span></span><span class="line"><span class="token">       </span><span class="token" style="color: #d73a49;">override</span><span class="token"> </span><span class="token">val </span><span class="token">searchQuery</span><span class="token">: </span><span class="token" style="color: #6f42c1;">String</span><span class="token">
</span></span><span class="line"><span class="token">   )</span><span class="token">: </span><span class="token" style="color: #6f42c1;">ShowsState</span><span class="token">()
</span></span><span class="line"><span class="token">}
</span></span><span class="line"><span class="token"> 
</span></span></code></pre></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-249"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-media">
	<div	class="media block-media__media media__border--none media__align--center-center"
	data-id="es-247"
	 data-media-type='image'>

	<figure class="image block-media__image-figure image--size-stretch" data-id="es-248">
	<picture class="image__picture block-media__image-picture">
								
			<source
				srcset=https://infinum.com/uploads/2020/10/MVVM-architecture-2-1400x528.webp				media='(max-width: 699px)'
				type=image/webp								height="528"
												width="1400"
				 />
												<img
					src="https://infinum.com/uploads/2020/10/MVVM-architecture-2.webp"
					class="image__img block-media__image-img"
					alt=""
										height="796"
															width="2112"
										loading="lazy"
					 />
					</picture>

	</figure></div></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-252"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-250">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-251'
	>
	The ViewState-s now <strong>completely describe the screen</strong>, and the screen will be completely restored upon recreation.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-255"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-heading" data-id="es-253">
	<h2	class='typography typography--size-52-default js-typography block-heading__heading'
	data-id='es-254'
	>
	A future-proof architecture</h2></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-258"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-256">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-257'
	>
	It is important to note that it is <strong>not a silver bullet</strong>. If we consider the search screen, the search query field is modified by the fragment due to state changes, and is modified by the user, often simultaneously, which could lead to inconsistencies in the text content, cursor position, selection, etc.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-263"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="blockquote block-blockquote__blockquote" data-id="es-259">
	
	<div class="blockquote__content">
		<i
	class="icon blockquote__icon icon--size-16 icon--scale-100"
	 aria-hidden='true' data-name='blockquote-24' data-id='es-260'>
	<svg fill='none' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path clip-rule='evenodd' d='m12 24c6.6274 0 12-5.3726 12-12 0-2.79685-.9568-5.37021-2.561-7.41062-.581.22951-1.0832.60583-1.5069 1.12898-.5132.60844-.7698 1.41969-.7698 2.43375v.07605h2.5789v5.59004h-5.6197v-5.01962c0-1.11547.154-2.06616.4619-2.85205.3336-.81125.757-1.48307 1.2702-2.01545.528-.52161 1.1175-.92155 1.7687-1.1998-2.0728-1.70651-4.7279-2.73128-7.6223-2.73128-6.62742 0-12 5.37258-12 12 0 6.6274 5.37258 12 12 12zm-3.53811-18.05347c-.30793.78589-.46189 1.73658-.46189 2.85205v5.01962h5.6197v-5.59004h-2.5789v-.07605c0-1.01406.2566-1.82531.7698-2.43375.5389-.63379 1.1804-1.05209 1.9245-1.2549v-2.28164c-.7441.07605-1.4626.25351-2.1555.53238-.6928.27887-1.3086.68449-1.84752 1.21688-.51321.53238-.9366 1.2042-1.27019 2.01545z' fill='currentColor' fill-rule='evenodd'/></svg></i><p	class='typography typography--size-36-text js-typography blockquote__quote'
	data-id='es-261'
	>
	No one-size-fits-all solution exists, and each situation should be addressed individually to maintain consistent UX.</p>
		<div class="blockquote__caption-wrap">
					</div>
	</div>
</div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-266"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-264">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-265'
	>
	This was just an overview of Infinum’s implementation of the MVVM architecture pattern, and insight into some of the motivations behind the design decisions that we’ve made. The more we use the architecture pattern, the more we’ll see where it shines, and where it lacks, and we’ll continue to improve it.</p></div>	</div>

<div
	class="wrapper wrapper__use-simple--true"
	data-id="es-269"
	 data-animation='slideFade' data-animation-target='inner-items'>
		
			<div class="block-paragraph" data-id="es-267">
	<p	class='typography typography--size-16-text-roman js-typography block-paragraph__paragraph'
	data-id='es-268'
	>
	Stay tuned!</p></div>	</div>
</div>
</div>		</div>
	</div><p>The post <a href="https://infinum.com/blog/mvvm-architecture/">Implementing Custom Architecture based on Model-View-ViewModel with ViewStates</a> appeared first on <a href="https://infinum.com">Infinum</a>.</p>
]]>
				</content:encoded>
			</item>
		
	</channel>
</rss>