Android – How To Manage Core App Quality In Android ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Manage Core App Quality In Android ?).

In this article, we will learn about how to manage Core App Quality in Android application. Android users expect high-quality apps. App quality directly influences the long-term success of our app – in terms of installs, user rating and reviews, engagement, and user retention. This article reviews about the core aspects of quality (Core App Quality Guidelines) in our android application, through a compact set of quality criteria and associated tests. All Android apps should meet these criteria.

A famous quote about learning is :

“One learns from books and example only that certain things can be done. Actual learning requires that you do those things.”

So let’s begin.

Overview

Android users expect high-quality apps that should meet the core aspects of quality, through a compact set of quality criteria and associated tests criteria which provides in Core App Quality Guidelines.

Before publishing our apps, we need to test them against these criteria to ensure that application function works well on many devices and meets Android standards for navigation and design, and are prepared for promotional opportunities in the Google Play store.

Core App Quality Guidelines

Handle Visual Design and User Interaction

These criteria ensure that our application provides standard Android visual design and interaction patterns from core app quality guidelines where appropriate, for a consistent and intuitive user experience.

Standard Design

The application follows Android Design guidelines and uses common UI patterns and icons:

  • This does not redefine the expected function of a system icon (such as the Back button).
  • The application does not replace a system icon with a completely different icon if it triggers the standard UI behavior.
  • If the application provides a customized version of a standard system icon, the icon strongly resembles the system icon and triggers the standard system behavior.
  • The application does not redefine or misuse Android UI patterns, such that icons or behaviors could be misleading or confusing to users.

Navigation

  • The application supports standard system Back button navigation and does not make use of any custom, on-screen “Back button” prompts.
  • All dialogs are dismissible using the Back button.
  • Pressing the Home button at any point navigates to the Home screen of the device.

Notifications

  • Notifications follow Android Design guidelines. In particular:
    • Multiple notifications are stacked into a single notification object, where possible.
    • Notifications are persistent only if related to ongoing events (such as music playback or a phone call).
    • Notifications do not contain advertising or content unrelated to the core function of the app, unless the user has opted in.
  • The application uses notifications only to:
    • Indicate a change in context relating to the user personally (such as an incoming message), or
    • Expose information/controls relating to an ongoing event (such as music playback or a phone call).

Manage Functionality

These criteria ensure that our application provides the expected functional behavior, with the appropriate level of permissions.

Permissions

  • The application requests only the absolute minimum permissions that it needs to support core functionality.
  • The application does not request permissions to access sensitive data (such as Contacts or the System Log) or services that can cost the user money (such as the Dialer or SMS), unless related to a core capability of the application.

Install Location

The application functions normally when installed on SD card (if supported by application). Supporting installation to SD card is recommended for most large apps (10MB+).

Audio

  • Audio does not play when the screen is off, unless this is a core feature (for example, the app is a music player).
  • It does not play behind the lock screen, unless this is a core feature.
  • Audio does not play on the home screen or over another application, unless this is a core feature.
  • Audio resumes when the application returns to the foreground, or indicates to the user that playback is in a paused state.

UI and Graphics

  • The application supports both landscape and portrait orientations (if possible). Orientations expose largely the same features and actions and preserve functional parity. Minor changes in content or views are acceptable.
  • The application uses the whole screen in both orientations and does not letterbox to account for orientation changes. Minor letterboxing to compensate for small variations in screen geometry is acceptable.
  • The application correctly handles rapid transitions between display orientations without rendering problems.

User/App State

  • The application should not leave any services running when the app is in the background, unless related to a core capability of the application. For example, the application should not leave services running to maintain a network connection for notifications, to maintain a Bluetooth connection, or to keep the GPS powered-on.
  • The application correctly preserves and restores user or application state. The application preserves user or application state when leaving the foreground and prevents accidental data loss due to back-navigation and other state changes. This must restore the preserved state and any significant stateful transaction that was pending, such as changes to editable fields, game progress, menus, videos, and other sections of the application or game, when returning to the foreground.
    • The application returns the user to the exact state in which it was last used, when the application is resumed from the Recents application switcher.
    • When this is resumed after the device wakes from sleep (locked) state, the application returns the user to the exact state in which it was last used.
    • When the application is relaunched from Home or All Applications, the application restores the application state as closely as possible to the previous state.
    • On Back keypresses, the application gives the user the option of saving any application or user state that would otherwise be lost on back-navigation.

Manage Compatibility, Performance and Stability

These criteria ensure that applications provide the compatibility, performance, stability, and responsiveness expected by users.

Stability

The application does not crash, force close, freeze, or otherwise function abnormally on any targeted device.

Performance

  • The application loads quickly or provides onscreen feedback to the user (a progress indicator or similar cue) if the application takes longer than two seconds to load.
  • With StrictMode enabled, no red flashes (performance warnings from StrictMode) are visible when exercising the application, including during game play, animations and UI transitions, and any other part of the application.

Software Development Kit (SDK)

  • The application runs on the latest public version of the Android platform without crashing or loss of core function.
  • This targets the latest SDK by setting the targetSdk value to minimize the use of any platform-provided compatibility fallbacks.
  • The application is built with the latest SDK by setting the compileSdk value.

Battery

The Android application supports power management features in Android 6.0+ (Doze and App Standby) properly. In the case where core functionality is disrupted by power management, only qualified apps may request an exemption.

Media

Music and video playback is smooth, without crackle, stutter, or other artifacts, during normal app use and load.

Visual Quality

  • The application displays graphics, text, images, and other UI elements without noticeable distortion, blurring, or pixelation.
    • The application provides high-quality graphics for all targeted screen sizes and form factors.
    • No aliasing at the edges of menus, buttons, and other UI elements is visible.
  • The application displays text and text blocks in an acceptable manner.
    • Composition is acceptable in all supported form factors.
    • No cut-off letters or words are visible.
    • No improper word wraps within buttons or icons are visible.
    • Sufficient spacing between text and surrounding elements.

Handle Application Security

These criteria ensure that apps handle user data and personal information safely. In addition to this checklist, applications published on the Google Play Store must also follow the User Data policies to protect users’ privacy.

Data

  • All private data is stored in the application’s internal storage.
  • External storage data is verified before being accessed.
  • All intents and broadcasts follow secure best practices:
    • Intents are explicit if the destination application is known.
    • These intents enforce and use appropriate permissions.
    • Intents that contain data and payload are verified before use.
  • No personal or sensitive user data is logged to the system or app-specific log.

App Components

  • Only application components that share data with other applications, or components that should be invoked by other applications, are exported. This includes activities, services, broadcast receivers, and especially content providers. Always set the android:exported attribute explicitly, regardless of whether or not we export any of your application’s components.
  • All application components that share content with other applications define (and enforce) appropriate permissions. This includes activities, services, broadcast receivers, and especially content providers.
  • All content providers that share content between our applications use android:protectionLevel="signature".

Networking

  • All network traffic is sent over SSL.
  • Application declares a network security configuration.
  • If the application uses Google Play services, the security provider is initialized at application startup.

Libraries

All libraries, SDKs, and dependencies are up to date.

WebViews

  • JavaScript is disabled in all WebViews (unless required).
  • WebViews only load allow listed content if possible.
  • WebViews do not use addJavaScriptInterface() with untrusted content. On Android M and above, HTML message channels can be used instead.

Execution

The app does not dynamically load code from outside the app’s APK.

Cryptography

  • The application uses strong, platform-provided cryptographic algorithms and does not implement custom algorithms.
  • The application uses a properly secure random number generator, in particular to initialize cryptographic keys.

Google Play

These criteria ensure that our applications are ready to publish on Google Play.

Policies

  • The application strictly adheres to the terms of the Google Play Developer Content Policy and does not offer inappropriate content, does not use the intellectual property or brand of others, and so on.
  • The maturity level of the application is set appropriately, based on the Content Rating Guidelines.
  • The application supports power management features in Android 6.0+ (Doze and App Standby) properly. In the case where core functionality is disrupted by power management, only qualified applications may request an exemption.

App Details Page

  • The application’s feature graphic follows the guidelines outlined. Make sure that:
    • The application listing includes a high-quality feature graphic.
    • The feature graphic does not contain device images, screenshots, or small text that will be illegible when scaled down and displayed on the smallest screen size that our application is targeting.
    • The feature graphic does not resemble an advertisement.
  • The application’s screenshots and videos do not show or reference non-Android devices.
  • The application’s screenshots or videos do not represent the content and experience of our application in a misleading way.

User Support

Common user-reported bugs in the Reviews tab of the Google Play page are addressed if they are reproducible and occur on many different devices. If a bug occurs on only a few devices, we should still address it if those devices are particularly popular or new.

Setting Up A Test Environment

To assess the quality of our application, we need to set up a suitable hardware or emulator environment for testing. The ideal test environment would include a small number of actual hardware devices that represent key form factors and hardware/software combinations currently available to consumers. It’s not necessary to test on every device that’s on the market — rather, we should focus on a small number of representative devices, even using one or two devices per form factor.

If we are not able to obtain actual hardware devices for testing, we should set up emulated devices (AVDs) to represent the most common form factors and hardware/software combinations.

To go beyond basic testing, we can add more devices, more form factors, or new hardware/software combinations to our test environment. We can also increase the number or complexity of tests and quality criteria.

Test Procedures

These test procedures help us discover various types of quality issues in our app. We can combine the tests or integrate groups of tests together in our own test plans.

Core Suite

  • Navigate to all parts of the application — all screens, dialogs, settings, and all user flows.
    • If the application allows for editing or content creation, game play, or media playback, make sure to enter those flows to create or modify content.
    • While exercising the application, we introduce transient changes in network connectivity, battery function, GPS availability, system load, and so on.
  • From each application screen, we press the device’s Home key, then re-launch the application from the All Applications screen.
  • We switch to another running application and then return to the application under test using the Recents application switcher from each application screen.
  • From each application screen (and dialogs), we press the Back button.
  • From each application screen, we rotate the device between landscape and portrait orientation at least three times.
  • Switch to another application to send the test application into the background. Go to Settings and check whether the test application has any services running while in the background. In Android 4.0 and higher, we go to the Applications screen and find the application in the “Running” tab.
  • We press the power button to put the device to sleep, then press the power button again to wake the screen.
  • We set the device to lock when the power button is pressed. Press the power button to put the device to sleep, then press the power button again to wake the screen, then unlock the device.
  • For devices that have slide-out keyboards, we slide the keyboard in and out at least once. For devices that have keyboard docks, we attach the device to the keyboard dock.
  • We plug-in the external display for those devices that have an external display port.
  • Trigger and observe in the notifications drawer all types of notifications that the application can display. Expand notifications where applicable (Android 4.1 and higher), and tap all actions offered.

Install On SD Card

Repeat Core Suite with the application installed to a device’s SD card (if supported by application). To move the application to SD card, we can use Settings > App Info > Move to SD Card.

Hardware Acceleration

Repeat Core Suite with hardware acceleration enabled. To force-enable hardware acceleration (where supported by device), add hardware-accelerated="true"to the <application> in the application manifest and recompile.

Performance and Stability

Review the Android manifest file and build configuration to ensure that the application is built against the latest available SDK (targetSdk and compileSdk).

Performance Monitoring

Repeat Core Suite with StrictMode profiling enabled. Pay close attention to garbage collection and its impact on the user experience.

Battery

Repeat Core Suite across Doze and App Standby cycles. Pay close attention to alarms, timers, notifications, syncs, and so on.

Security

  • Review all data stored in external storage.
  • We review how data loaded from external storage is handled and processed.
  • Review all content providers defined in the Android manifest file for appropriate protectionLevel.
  • All permissions review that our application requires, in the manifest file, at runtime and in the application settings (Settings > App Info) on the device.
  • Review all application components defined in the Android manifest file for the appropriate export state. The export property must be set explicitly for all components.
  • Review the application’s Network Security configuration, ensuring that no lint checks on the configuration fail.
  • For each WebView, navigate to a page that requires JavaScript.
  • For each WebView, attempt to navigate to sites and content that are outside of our control.
  • Declare a Network Security Configuration that disables cleartext traffic then execute the application.
  • Run the application and exercise all core functionality, while observing the device log. No private user information should be logged.

Google Play

  • Sign into the Google Play Developer Console to review developer profile, app description, screenshots, feature graphic, content rating and user feedback.
  • Download feature graphic and screenshots and scale them down to match the display sizes on the devices and form factors we are targeting.
  • Review all graphical assets, media, text, code libraries, and other content packaged in the application or expansion file download.
  • Review Support for other use cases in Doze and App Standby.

Payments

Navigate to all screens of our app and enter all in-app purchase flows.

Testing with StrictMode

For performance testing, we recommend enabling StrictMode in our app and using it to catch operations on the main thread and other threads that could affect performance, network accesses, file reads/writes, and so on.

We can set up a monitoring policy per thread using StrictMode.ThreadPolicy.Builder and enable all supported monitoring in the ThreadPolicy using detectAll().

Make sure to enable visual notification of policy violations for the ThreadPolicy using penaltyFlashScreen().

That’s all about in this article.

Conclusion

In this article, we understood about how to manage Core App Quality in Android application. This article reviewed about the core aspects of quality (Core App Quality) in our android application, through a compact set of quality criteria and associated tests.

Thanks for reading! I hope you enjoyed and learned about Core App Quality Guidelines concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolMonkTechie as below link :

You can also follow official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Android – An Overview Of Property Animation In Android

Hello Readers, CoolMonkTechie heartily welcomes you in this article (An Overview Of Property Animation In Android).

In this article, we will learn about Property Animation Overview in Android. The property animation system is a robust framework that allows us to animate almost anything. We can define an animation to change any object property over time, regardless of whether it draws to the screen. A property animation changes a property’s (a field in an object) value over a specified length of time. To animate something, we specify the object property that we want to animate, such as an object’s position on the screen, how long we want to animate it for, and what values we want to animate between. This article shows the Property Animation related concepts in Android.

A famous quote about learning is :

” Develop a passion for learning. If you do, you will never cease to grow. “

So let’s begin.

Characteristics Of Property Animation

The property animation system lets us define the following characteristics of an animation:

  • Duration: We can specify the duration of an animation. The default length is 300 ms.
  • Time interpolation: This specify how the values for the property calculate as a function of the animation’s current elapsed time.
  • Repeat count and behavior: This specify whether to have an animation repeat when it reaches the end of a duration and how many times to repeat the animation. We can also specify whether we want the animation to play back in reverse. Setting it to reverse plays the animation forwards then, backwards repeatedly, until it reaches the number of repeats.
  • Animator sets: We can group animations into logical sets that play together or sequentially or after specified delays.
  • Frame refresh delay: This specify how often to refresh frames of our animation. The default is set to refresh every 10 ms, but the speed in which our application can refresh frames is ultimately dependent on how busy the system is overall and how fast the system can service the underlying timer.

The Work Flow of Property Animation

Linear Animation

First, we see how an animation works with a simple example.

Source: Android Developer – The Example Of Linear Animation

In this above linear animation figure, it depicts a hypothetical object animates with its x property, which represents its horizontal location on a screen. It sets the duration of the animation to 40 ms and the distance to travel is 40 pixels. Every 10 ms, which is the default frame refresh rate, the object moves horizontally by 10 pixels. At the end of 40ms, the animation stops, and the object ends at horizontal position 40. This is an example of an animation with linear interpolation, meaning the object moves at a constant speed.

Non-Linear Animation

We can also specify animations to have a non-linear interpolation.

Source: Android Developer – The Example of a non-linear animation

In this above non-linear animation figure, it illustrates a hypothetical object that speeds up at the beginning of the animation and decelerates at the end of the animation. The object still moves 40 pixels in 40 ms, but non-linearly. In the beginning, this animation accelerates up to the halfway point, then decelerates from the halfway point until the end of the animation. As this non-linear animation figure shows the distance traveled at the beginning and end of the animation is less than in the middle.

Animations Calculation Using Property Animation

We understood how the important components of the property animation system would calculate animations like the ones. Now we will discuss how the main classes work with one another.

Source: Android Developer – How animations are calculated

In this figure, The ValueAnimator object keeps track of our animation’s timing, such as how long the animation has been running, and the current value of the property that it is animating.

The ValueAnimator encapsulates

  • TimeInterpolator, which defines animation interpolation, and
  • TypeEvaluator, which defines how to calculate values for the property being animated.

For example, in non-linear animation figure, the TimeInterpolator used would be AccelerateDecelerateInterpolator and the TypeEvaluator would be IntEvaluator.

To start an animation, we create a ValueAnimator and give it the starting and ending values for the property that we want to animate, along with the duration of the animation. When we call start(), the animation begins. During the whole animation, the ValueAnimator calculates an elapsed fraction between 0 and 1, based on the duration of the animation and how much time has elapsed. The elapsed fraction represents the percentage of time that the animation has completed, 0 meaning 0% and 1 meaning 100%. For example, in linear animation figure , the elapsed fraction at t = 10 ms would be .25 because the total duration is t = 40 ms.

When the ValueAnimator calculates an elapsed fraction, it calls the TimeInterpolator that is currently set to calculate an interpolated fraction. An interpolated fraction maps the elapsed fraction to a new fraction that takes into account the time interpolation that is set. For example, in non-linear animation figure, because the animation slowly accelerates, the interpolated fraction, about .15, is less than the elapsed fraction, .25, at t = 10 ms. In linear animation figure, the interpolated fraction is always the same as the elapsed fraction.

When the interpolated fraction calculates, the ValueAnimator calls the TypeEvaluator to calculate the value of the property that you are animating, based on the interpolated fraction, the starting value, and the ending value of the animation. For example, in non-linear figure, the interpolated fraction was .15 at t = 10 ms, so the value for the property would be .15 × (40 – 0), or 6.

Property Animation Vs View Animation

View Animation

” The View Animation system provides the capability to only animate View objects.”

So if we wanted to animate non-View objects, we have to implement our own code to do so.

“The View Animation system constrains because it only exposes a few aspects of a View object to animate, such as the scaling and rotation of a View but not the background color, for instance.”

“Another disadvantage of the View Animation system is that it only modified where the View drew, and not the actual View itself.”

For instance, if we animated a button to move across the screen, the button draws correctly, but the actual location where we can click the button does not change, so we have to implement our own logic to handle this.

Property Animation

“With the property animation system, these constraints removed completely, and we can animate any property of any object (Views and non-Views) and the object they modify it.”

The property animation system is also more robust in the way it carries out animation. At a high level, we assign animators to the properties that we want to animate, such as color, position, or size and can define aspects of the animation such as interpolation and synchronization of multiple animators.

The View Animation system, however, takes less time to set up and requires less code to write. If View Animation accomplishes everything that we need to do, or if our existing code already works the way we want, there is no need to use the Property Animation system. It also might make sense to use both animation systems for different situations if the use case arises.

The Main Components of Property Animation System

We can find most of the property animation system’s APIs in android.animation. Because the View Animation system already defines many interpolators in android.view.animation, we can use those interpolators in the property animation system.

The Main Components of Property Animation System are :

  • Animators
  • Evaluators
  • Interpolators

Animators

The Animator class provides the basic structure for creating animations. We normally do not use this class directly, as it only provides minimal functionality that must be extended to fully support animating values. The following subclasses extend Animator:

  • ValueAnimator
  • ObjectAnimator
  • AnimatorSet

ValueAnimator

The main timing engine for property animation that also computes the values for the property to be animated. It has all the core functionality that calculates animation values and contains the timing details of each animation, information about whether an animation repeats, listeners that receive update events, and the ability to set custom types to evaluate.

There are two pieces to animating properties: 

  • calculating the animated values and
  • setting those values on the object and property that is being animated.

 ValueAnimator does not carry out the second piece, so we must listen for updates to values calculated by the ValueAnimator and modify the objects that want to animate with our own logic.

ObjectAnimator

A subclass of ValueAnimator that allows us to set a target object and object property to animate. This class updates the property accordingly when it computes a new value for the animation.

We want to use ObjectAnimator most of the time, because it makes animating values on target objects much easier. However, we sometimes want to use ValueAnimator directly because ObjectAnimator has a few more restrictions, such as requiring specific accessor methods to be present on the target object.

AnimatorSet

This provides a mechanism to group animations together so they run in relation to one another. We can set animations to play together, sequentially, or after a specified delay. 

Evaluators

Evaluators tell the property animation system how to calculate values for a property. They take the timing data that an Animator class provides, the animation’s start and end value, and calculate the animated values of the property based on this data.

The property animation system provides the following evaluators:

  • IntEvaluator: The default evaluator to calculate values for int properties.
  • FloatEvaluator: The default evaluator to calculate values for float properties.
  • ArgbEvaluator: The default evaluator to calculate values for color properties that represents as hexadecimal values.
  • TypeEvaluator: An interface that allows us to create our own evaluator. If we are animating an object property that is not an intfloat, or color, we must implement the TypeEvaluator interface to specify how to compute the object property’s animated values. We can also specify a custom TypeEvaluator for intfloat, and color values, if we want to process those types differently than the default behavior.

Interpolators

A time interpolator defines how specific values in an animation are calculated as a function of time.

For example, we can specify animations to happen linearly across the whole animation, meaning the animation moves evenly the entire time, or we can specify animations to use non-linear time, for example, accelerating at the beginning and decelerating at the end of the animation.

The property animation system provides the following interpolators:

  • AccelerateDecelerateInterpolator: An interpolator whose rate of change starts and ends slowly but accelerates through the middle.
  • AccelerateInterpolator: An interpolator whose rate of change starts out slowly and then accelerates.
  • AnticipateInterpolator: An interpolator whose change starts backward, then flings forward.
  • AnticipateOvershootInterpolator: An interpolator whose change starts backward, flings forward and overshoots the target value, then finally goes back to the final value.
  • BounceInterpolator: An interpolator whose change bounces at the end.
  • CycleInterpolator: An interpolator whose animation repeats for a specified number of cycles.
  • DecelerateInterpolator: An interpolator whose rate of change starts out quickly and then decelerates.
  • LinearInterpolator: An interpolator whose rate of change is constant.
  • OvershootInterpolator: An interpolator whose change flings forward and overshoots the last value then comes back.
  • TimeInterpolator: An interface that allows you to implement your own interpolator.

Animate Using ValueAnimator

The ValueAnimator class lets us animate values of some type for the duration of an animation by specifying a set of intfloat, or color values to animate through. We get a ValueAnimator by calling one of its factory methods: ofInt()ofFloat(), or ofObject(). For example:

ValueAnimator.ofFloat(0f, 100f).apply {
    duration = 1000
    start()
}

In this code, the ValueAnimator calculates the values of the animation, between 0 and 100, for a duration of 1000 ms, when the start() method runs.

We can also specify a custom type to animate by doing the following code:

ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply {
    duration = 1000
    start()
}

In this code, the ValueAnimator calculates the values of the animation, between startPropertyValue and endPropertyValue using the logic supplied by MyTypeEvaluator for a duration of 1000 ms, when the start() method runs.

We can use the values of the animation by adding an AnimatorUpdateListener to the ValueAnimator object, as shown in the following code:

ValueAnimator.ofObject(...).apply {
    ...
    addUpdateListener { updatedAnimation ->
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        textView.translationX = updatedAnimation.animatedValue as Float
    }
    ...
}

In the onAnimationUpdate() method, we can access the updated animation value and use it in a property of one of our views.

Animate Using ObjectAnimator

The ObjectAnimator is a subclass of the ValueAnimator and combines the timing engine and value computation of ValueAnimator with the ability to animate a named property of a target object. This makes animating any object much easier, as we no longer need to implement the ValueAnimator.AnimatorUpdateListener, because the animated property updates automatically.

Instantiating an ObjectAnimator is like a ValueAnimator, but we also specify the object and the name of that object’s property (as a String) along with the values to animate between:

ObjectAnimator.ofFloat(textView, "translationX", 100f).apply {
    duration = 1000
    start()
}

To have the ObjectAnimator update properties correctly, we must do the following steps:

  • The object property that we are animating must have a setter function (in camel case) in the form ofset<PropertyName>(). Because the ObjectAnimator automatically updates the property during animation, it must be able to access the property with this setter method. For example, if the property name is foo, we need to have a setFoo() method. If this setter method does not exist, we have three options:
    • Add the setter method to the class if we have the rights to do so.
    • Use a wrapper class that we have rights to change and have that wrapper receive the value with a valid setter method and forward it to the original object.
    • Use ValueAnimator instead.
  • If we specify only one value for the values... parameter in one of the ObjectAnimator factory methods, we assume it to be the ending value of the animation. Therefore, the object property that we are animating must have a getter function that is used to get the starting value of the animation. The getter function must be in the form of get<PropertyName>(). For example, if the property name is foo, we need to have a getFoo() method.
  • The getter and setter methods of the property that we are animating must operate on the same type as the starting and ending values that specify to ObjectAnimator. For example, we must have targetObject.setPropName(float) and targetObject.getPropName() if we construct the following ObjectAnimator:
ObjectAnimator.ofFloat(targetObject, "propName", 1f)
  • Depending on what property or object we are animating, we might need to call the invalidate() method on a View to force the screen to redraw itself with the updated animated values. We do this in the onAnimationUpdate() callback.

Animate Using AnimatorSet

In many cases, we want to play an animation that depends on when another animation starts or finishes. The Android system lets us bundle animations together into an AnimatorSet, so that we can specify whether to start animations simultaneously, sequentially, or after a specified delay. We can also nest AnimatorSet objects within each other.

val bouncer = AnimatorSet().apply {
    play(bounceAnim).before(squashAnim1)
    play(squashAnim1).with(squashAnim2)
    play(squashAnim1).with(stretchAnim1)
    play(squashAnim1).with(stretchAnim2)
    play(bounceBackAnim).after(stretchAnim2)
}
val fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
}
AnimatorSet().apply {
    play(bouncer).before(fadeAnim)
    start()
}

In this code snippet, the following Animator objects in the following manner :

  • Plays bounceAnim.
  • Plays squashAnim1squashAnim2stretchAnim1, and stretchAnim2 at the same time.
  • Plays bounceBackAnim.
  • Plays fadeAnim.

Animation Listeners

We can listen for important events during an animation’s duration with the listeners described below.

  • Animator.AnimatorListener
  • ValueAnimator.AnimatorUpdateListener

Animator.AnimatorListener

  • onAnimationStart() : This method called when the animation starts.
  • onAnimationEnd(): This called when the animation ends.
  • onAnimationRepeat(): This method called when the animation repeats itself.
  • onAnimationCancel(): Called when the animation is canceled. A cancelled animation also calls onAnimationEnd(), regardless of how they were ended.

ValueAnimator.AnimatorUpdateListener

onAnimationUpdate(): This method called on every frame of the animation. Listen to this event to use the calculated values generated by ValueAnimator during an animation.

Animate Layout Changes to ViewGroup Objects

The property animation system provides the capability to animate changes to ViewGroup objects as well as provide an easy way to animate View objects themselves.

We can animate layout changes within a ViewGroup with the LayoutTransition class. Views inside a ViewGroup can go through an appearing and disappearing animation when we add them to or remove them from a ViewGroup or when we call a View’s setVisibility() method with VISIBLEINVISIBLE, or GONE. The remaining Views in the ViewGroup can also animate into their new positions when we add or remove Views.

We can define the following animations in a LayoutTransition object by calling setAnimator() and passing in an Animator object with one of the following LayoutTransition constants:

  • APPEARING : This flag indicating the animation that runs on items that are appearing in the container.
  • CHANGE_APPEARING : A flag indicating the animation that runs on items that are changing due to a new item appearing in the container.
  • DISAPPEARING : This flag indicating the animation that runs on items that are disappearing from the container.
  • CHANGE_DISAPPEARING : A flag indicating the animation that runs on items that are changing due to an item disappearing from the container.

Animate View State Changes Using StateListAnimator

The StateListAnimator class lets us define animators that run when the state of a view changes. This object behaves as a wrapper for an Animator object, calling that animation whenever the specified view state (such as “pressed” or “focused”) changes.

The StateListAnimator can be defined in an XML resource with a root <selector> element and child <item> elements that each specify a different view state defined by the StateListAnimator class. Each <item> contains the definition for a property animation set.

For example, the following file creates a state list animator that changes the x and y scale of the view when it’s pressed:

res/xml/animate_scale.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- the pressed state; increase x and y size to 150% -->
    <item android:state_pressed="true">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
        </set>
    </item>
    <!-- the default, non-pressed state; set x and y size to 100% -->
    <item android:state_pressed="false">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
        </set>
    </item>
</selector>

To attach the state list animator to a view, add the android:stateListAnimator attribute as follows:

<Button android:stateListAnimator="@xml/animate_scale"
        ... />

Now the animations defined in animate_scale.xml are used when this button’s state changes.

Or, to instead assign a state list animator to a view in our code, use the AnimatorInflater.loadStateListAnimator() method, and assign the animator to our view with the View.setStateListAnimator() method.

Use a TypeEvaluator

If we want to animate a type unknown to the Android system, we can create our own evaluator by implementing the TypeEvaluator interface. The types that are known by the Android system are intfloat, or a color, which are supported by the IntEvaluatorFloatEvaluator, and ArgbEvaluator type evaluators.

There is only one method to implement in the TypeEvaluator interface, the evaluate() method. This allows the animator that we are using to return an appropriate value for our animated property at the current point of the animation. The FloatEvaluator class shows how to do this:

private class FloatEvaluator : TypeEvaluator<Any> {

    override fun evaluate(fraction: Float, startValue: Any, endValue: Any): Any {
        return (startValue as Number).toFloat().let { startFloat ->
            startFloat + fraction * ((endValue as Number).toFloat() - startFloat)
        }
    }

}

Use Interpolators

An interpolator define how calculate specific values in an animation as a function of time. For example, we can specify animations to happen linearly across the whole animation, meaning the animation moves evenly the entire time, or we can specify animations to use non-linear time, for example, using acceleration or deceleration at the beginning or end of the animation.

Interpolators in the animation system receive a fraction from Animators that represent the elapsed time of the animation. Interpolators modify this fraction to coincide with the type of animation that it aims to provide. The Android system provides a set of common interpolators in the android.view.animation package. If none of these suit our needs, we can implement the TimeInterpolator interface and create our own.

For example, we will see that how to compare the default interpolator AccelerateDecelerateInterpolator and the LinearInterpolator that calculates interpolated fractions. The LinearInterpolator has no effect on the elapsed fraction. The AccelerateDecelerateInterpolator accelerates into the animation and decelerates out of it.

AccelerateDecelerateInterpolator

override fun getInterpolation(input: Float): Float =
        (Math.cos((input + 1) * Math.PI) / 2.0f).toFloat() + 0.5f

LinearInterpolator

override fun getInterpolation(input: Float): Float = input

Specify Keyframes

Keyframe object consists of a time/value pair that lets us define a specific state at a specific time of an animation. Each keyframe can also have its own interpolator to control the behavior of the animation in the interval between the previous keyframe’s time and the time of this keyframe.

To instantiate a Keyframe object, we must use one of the factory methods, ofInt()ofFloat(), or ofObject() to get the appropriate type of Keyframe. We then call the ofKeyframe() factory method to get a PropertyValuesHolder object. Once we have the object, we can get an animator by passing in the PropertyValuesHolder object and the object to animate. 

val kf0 = Keyframe.ofFloat(0f, 0f)
val kf1 = Keyframe.ofFloat(.5f, 360f)
val kf2 = Keyframe.ofFloat(1f, 0f)
val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2)
ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation).apply {
    duration = 5000
}

Animate Views

The Property Animation system allows streamlined animation of View objects and offers a few advantages over the view animation system. The View Animation system transformed View objects by changing the way that they were drawn. This was handled in the container of each View, because the View itself had no properties to manipulate. This resulted in the View being animated, but caused no change in the View object itself. This led to behavior such as an object still existing in its original location, even though it was drawn on a different location on the screen.

The property animation system can animate Views on the screen by changing the actual properties in the View objects. In addition, Views also automatically calls the invalidate() method to refresh the screen whenever its properties are changed. The new properties in the View class that facilitate property animations are:

  • translationX and translationY: These properties control where the View is located as a delta from its left and top coordinates, which are set by its layout container.
  • rotationrotationX, and rotationY: These properties control the rotation in 2D (rotation property) and 3D around the pivot point.
  • scaleX and scaleY: These properties control the 2D scaling of a View around its pivot point.
  • pivotX and pivotY: These properties control the location of the pivot point, around which the rotation and scaling transforms occur. By default, the pivot point is located at the center of the object.
  • x and y: These are simple utility properties to describe the final location of the View in its container, as a sum of the left and top values and translationX and translationY values.
  • alpha: Represents the alpha transparency on the View. This value is 1 (opaque) by default, with a value of 0 representing full transparency (not visible).

To animate a property of a View object, such as its color or rotation value, all we need to do is create a property animator and specify the View property that we want to animate. For example:

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)

Animate Using ViewPropertyAnimator

The ViewPropertyAnimator provides a simple way to animate several properties of a View in parallel, using a single underlying Animator object. It behaves much like an ObjectAnimator, because it modifies the actual values of the view’s properties, but is more efficient when animating many properties at once. In addition, the code for using the ViewPropertyAnimator is much more concise and easier to read.

The following code snippets show the differences in using multiple ObjectAnimator objects, a single ObjectAnimator, and the ViewPropertyAnimator when simultaneously animating the x and y property of a view.

Multiple ObjectAnimator objects

val animX = ObjectAnimator.ofFloat(myView, "x", 50f)
val animY = ObjectAnimator.ofFloat(myView, "y", 100f)
AnimatorSet().apply {
    playTogether(animX, animY)
    start()
}

One ObjectAnimator

val pvhX = PropertyValuesHolder.ofFloat("x", 50f)
val pvhY = PropertyValuesHolder.ofFloat("y", 100f)
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()

ViewPropertyAnimator

myView.animate().x(50f).y(100f)

Declare Animations in XML

The property animation system lets us declare property animations with XML instead of doing it programmatically. By defining our animations in XML, we can easily reuse our animations in multiple activities and more easily edit the animation sequence.

The following property animation classes have XML declaration support with the following XML tags:

ValueAnimator - <animator>
ObjectAnimator - <objectAnimator>
AnimatorSet - <set>

The following example plays the two sets of object animations sequentially, with the first nested set playing two object animations together:

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

In order to run this animation, we must inflate the XML resources in our code to an AnimatorSet object, and then set the target objects for all of the animations before starting the animation set. Calling setTarget() sets a single target object for all children of the AnimatorSet as a convenience.

(AnimatorInflater.loadAnimator(myContext, R.animator.property_animator) as AnimatorSet).apply {
    setTarget(myObject)
    start()
}

We can also declare a ValueAnimator in XML, as shown in the following example:

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />

To use the previous ValueAnimator in our code, we must inflate the object, add an AnimatorUpdateListener, get the updated animation value, and use it in a property of one of our views, as shown in the following code:

(AnimatorInflater.loadAnimator(this, R.animator.animator) as ValueAnimator).apply {
    addUpdateListener { updatedAnimation ->
        textView.translationX = updatedAnimation.animatedValue as Float
    }

    start()
}

That’s all about in this article.

Related Other Articles / Posts

Conclusion

In this article, we understood about Property Animation Overview in Android. This article described about Property Animation related concepts like Workflow, benefits, Animates View using ValueAnimator, ObjectAnimator and AnimatorSet in Android.

Thanks for reading! I hope you enjoyed and learned about Property Animation concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolMonkTechie as below link :

You can also follow official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Android – Is Awesome Design Patterns Valuable In Kotlin?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (Is Awesome Design Patterns Valuable In Kotlin?).

In this article, we will learn about why design patterns are valuable and frequently used in Kotlin. When we are new in programming languages, we don’t know which design patterns we should use with it and how to implement them. Design Patterns determine certain factors to differentiate between a good code and a bad code in Kotlin. This may be the code structure or the comments used or the variable names or something else. Being able to use a relevant design pattern is a prerequisite to creating functional, high-quality, and secure applications in Android with use of Kotlin. 

So every developer should follow Design Patterns while writing the Kotlin code of an Android application.

A famous quote about learning is :

” One learns from books and example only that certain things can be done. Actual learning requires that you do those things. “

So Let’s begin.

Design Patterns: What they are and why know them ?

A software design pattern is a solution to a particular problem we might face when designing an app’s architecture. But unlike out-of-the-box services or open-source libraries, we can’t paste a design pattern into our application because it isn’t a piece of code. Rather, it’s a general concept for how to solve a problem. A design pattern is a template that tells us how to write code, but it’s up to us to fit our code to this template.

Design patterns bring several benefits:

  • Tested solutions. We don’t need to waste time and reinvent the wheel trying to solve a particular software development problem, as design patterns already provide the best solution and tell us how to implement it.
  • Code unification. Design patterns provide us with typical solutions that have tested for drawbacks and bugs, helping us make fewer mistakes when designing our app architecture.
  • Common vocabulary. Instead of providing in-depth explanations of how to solve this or that software development problem, we can say what design pattern we used and other developers will immediately understand what solutions we implemented.

Design Patterns: What is it ?

A Design Pattern is a general, reusable solution to a commonly occurring problem within a given context.

So, Design Patterns are a pattern solution that follows to solve a particular feature. These are the best practices that any programmer can use to build an application.

We use Design Patterns that makes our code easier to understand and more reusable in Android.

Design Patterns: Patterns Types In Kotlin

Before we describe the most common architecture patterns in Android Kotlin, we should first learn the three types of software design patterns and how they differ:

  • Creational Design Patterns
  • Structural Design Patterns
  • Behavioral Design Patterns

1. Creational Design Patterns

Creational software design patterns deal with object creation mechanisms, which increase flexibility and reuse of existing code. They try to instantiate objects in a manner suitable for the particular situation. 

This Pattern is used to create some object without showing the logic or the steps that involves in creating the object. So, every time we want an object, we need not instantiate the object by using the new operator. So, this makes creating an object easier and can be easily created again and again.

Here are several creational design patterns:

  • Builder Pattern
  • Singleton Pattern
  • Factory Method Pattern
  • Abstract Factory

2. Structural Design Patterns

Structural design patterns aim to simplify the design by finding a simple way of realizing relationships between classes and objects. These patterns explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient.

In this Design Pattern, we concern about the structure of the code. Here, we follow some particular structural pattern that will help in understanding the code and the working of code just by looking at the structure of the code. These are some structural architecture patterns:

  • Adapter Pattern
  • Facade Pattern
  • Decorator Pattern
  • Composite Pattern
  • Protection Proxy Pattern

3. Behavioral Design Patterns

Behaviour design patterns identify common communication patterns between entities and implement these patterns. This Patterns mainly tells how the objects of the classes will communicate with each other. These patterns help us in understanding the code in a better way because by viewing the code we can identify the pattern and then we can understand the code in a better way.

  • Observer / Listener Pattern
  • Command Pattern
  • Strategy Pattern
  • State Pattern
  • Chain of Responsibility Pattern
  • Visitor Pattern
  • Mediator Pattern
  • Memento Pattern

Most Frequently Used Design Patterns In Kotlin

We’re going to provide only the essential information about each software design pattern–namely, how it works from the technical point of view and when it should be applied. We’ll also give an illustrative example in the Kotlin programming language.

1. Creational: Builder Pattern

The builder pattern is used to create complex objects with constituent parts that must be created in the same order or using a specific algorithm. An external class controls the construction algorithm.

Example

For example, Let’s assume that external library provides Dialog class, and we have only accessed to Dialog Public interface, which can not be changed.

class Dialog {

    fun showTitle() = println("showing title")

    fun setTitle(text: String) = println("setting title text $text")

    fun setTitleColor(color: String) = println("setting title color $color")

    fun showMessage() = println("showing message")

    fun setMessage(text: String) = println("setting message $text")

    fun setMessageColor(color: String) = println("setting message color $color")

    fun showImage(bitmapBytes: ByteArray) = println("showing image with size ${bitmapBytes.size}")

    fun show() = println("showing dialog $this")
}

//Builder:
class DialogBuilder() {
    constructor(init: DialogBuilder.() -> Unit) : this() {
        init()
    }

    private var titleHolder: TextView? = null
    private var messageHolder: TextView? = null
    private var imageHolder: File? = null

    fun title(init: TextView.() -> Unit) {
        titleHolder = TextView().apply { init() }
    }

    fun message(init: TextView.() -> Unit) {
        messageHolder = TextView().apply { init() }
    }

    fun image(init: () -> File) {
        imageHolder = init()
    }

    fun build(): Dialog {
        val dialog = Dialog()

        titleHolder?.apply {
            dialog.setTitle(text)
            dialog.setTitleColor(color)
            dialog.showTitle()
        }

        messageHolder?.apply {
            dialog.setMessage(text)
            dialog.setMessageColor(color)
            dialog.showMessage()
        }

        imageHolder?.apply {
            dialog.showImage(readBytes())
        }

        return dialog
    }

    class TextView {
        var text: String = ""
        var color: String = "#00000"
    }
}

Usage

//Function that creates dialog builder and builds Dialog
fun dialog(init: DialogBuilder.() -> Unit): Dialog {
    return DialogBuilder(init).build()
}

val dialog: Dialog = dialog {
	title {
    	text = "Dialog Title"
    }
    message {
        text = "Dialog Message"
        color = "#333333"
    }
    image {
        File.createTempFile("image", "jpg")
    }
}

dialog.show()

Output

setting title text Dialog Title
setting title color #00000
showing title
setting message Dialog Message
setting message color #333333
showing message
showing image with size 0
showing dialog Dialog@5f184fc6

AlertDialog Example

One of the common examples of Builder pattern that we all use in our daily life is that of AlertDialog. In AleartDialog, we call only the required methods like:

AlertDialog.Builder(this)
    .setTitle("This is a title")
    .setMessage("This is some message")
    .show()

2. Creational: Singleton Pattern

The singleton pattern ensures that only one object of a particular class is ever created. All further references to objects of the singleton class refer to the same underlying instance. There are very few applications, do not overuse this pattern!

Example

object PrinterDriver {
    init {
        println("Initializing with object: $this")
    }

    fun print() = println("Printing with object: $this")
}

Usage

println("Start")
PrinterDriver.print()
PrinterDriver.print()

Output

Start
Initializing with object: PrinterDriver@6ff3c5b5
Printing with object: PrinterDriver@6ff3c5b5
Printing with object: PrinterDriver@6ff3c5b5

3. Creational: Factory Method Pattern

The factory pattern is used to replace class constructors, abstracting the process of object generation so that the type of the object instantiated can be determined at run-time.

Example

sealed class Country {
    object USA : Country()
}

object Spain : Country() 
class Greece(val someProperty: String) : Country()
data class Canada(val someProperty: String) : Country() 

class Currency(
    val code: String
)

object CurrencyFactory {

    fun currencyForCountry(country: Country): Currency =
        when (country) {
            is Greece -> Currency("EUR")
            is Spain -> Currency("EUR")
            is Country.USA -> Currency("USD")
            is Canada -> Currency("CAD")
        }  
}

Usage

val greeceCurrency = CurrencyFactory.currencyForCountry(Greece("")).code
println("Greece currency: $greeceCurrency")

val usaCurrency = CurrencyFactory.currencyForCountry(Country.USA).code
println("USA currency: $usaCurrency")

assertThat(greeceCurrency).isEqualTo("EUR")
assertThat(usaCurrency).isEqualTo("USD")

Output

Greece currency: EUR
US currency: USD

4. Creational: Abstract Factory Pattern

The abstract factory pattern is used to provide a client with a set of related or dependant objects. The “family” of objects created by the factory are determined at run-time.

Example

interface Plant

class OrangePlant : Plant

class ApplePlant : Plant

abstract class PlantFactory {
    abstract fun makePlant(): Plant

    companion object {
        inline fun <reified T : Plant> createFactory(): PlantFactory = when (T::class) {
            OrangePlant::class -> OrangeFactory()
            ApplePlant::class  -> AppleFactory()
            else               -> throw IllegalArgumentException()
        }
    }
}

class AppleFactory : PlantFactory() {
    override fun makePlant(): Plant = ApplePlant()
}

class OrangeFactory : PlantFactory() {
    override fun makePlant(): Plant = OrangePlant()
}

Usage

val plantFactory = PlantFactory.createFactory<OrangePlant>()
val plant = plantFactory.makePlant()
println("Created plant: $plant")

Output

Created plant: OrangePlant@4f023edb

5. Structural: Adapter Pattern

The adapter pattern is used to provide a link between two otherwise incompatible types by wrapping the “adaptee” with a class that supports the interface required by the client.

Example

interface Temperature {
    var temperature: Double
}

class CelsiusTemperature(override var temperature: Double) : Temperature

class FahrenheitTemperature(var celsiusTemperature: CelsiusTemperature) : Temperature {

    override var temperature: Double
        get() = convertCelsiusToFahrenheit(celsiusTemperature.temperature)
        set(temperatureInF) {
            celsiusTemperature.temperature = convertFahrenheitToCelsius(temperatureInF)
        }

    private fun convertFahrenheitToCelsius(f: Double): Double = (f - 32) * 5 / 9

    private fun convertCelsiusToFahrenheit(c: Double): Double = (c * 9 / 5) + 32
}

Usage

val celsiusTemperature = CelsiusTemperature(0.0)
val fahrenheitTemperature = FahrenheitTemperature(celsiusTemperature)

celsiusTemperature.temperature = 36.6
println("${celsiusTemperature.temperature} C -> ${fahrenheitTemperature.temperature} F")

fahrenheitTemperature.temperature = 100.0
println("${fahrenheitTemperature.temperature} F -> ${celsiusTemperature.temperature} C")

Output

36.6 C -> 97.88000000000001 F
100.0 F -> 37.77777777777778 C

6. Structural: Facade Pattern

The facade pattern is used to define a simplified interface to a more complex subsystem.

Example

class ComplexSystemStore(val filePath: String) {

    init {
        println("Reading data from file: $filePath")
    }

    val store = HashMap<String, String>()

    fun store(key: String, payload: String) {
        store.put(key, payload)
    }

    fun read(key: String): String = store[key] ?: ""

    fun commit() = println("Storing cached data: $store to file: $filePath")
}

data class User(val login: String)

//Facade:
class UserRepository {
    val systemPreferences = ComplexSystemStore("/data/default.prefs")

    fun save(user: User) {
        systemPreferences.store("USER_KEY", user.login)
        systemPreferences.commit()
    }

    fun findFirst(): User = User(systemPreferences.read("USER_KEY"))
}

Usage

val userRepository = UserRepository()
val user = User("coolmonktechie")
userRepository.save(user)
val resultUser = userRepository.findFirst()
println("Found stored user: $resultUser")

Output

Reading data from file: /data/default.prefs
Storing cached data: {USER_KEY=coolmonktechie} to file: /data/default.prefs
Found stored user: User(login=coolmonktechie)

7. Structural: Decorator Pattern

The decorator pattern is used to extend or alter the functionality of objects at run-time by wrapping them in an object of a decorator class. This provides a flexible alternative to using inheritance to change behaviour.

Example

interface CoffeeMachine {
    fun makeSmallCoffee()
    fun makeLargeCoffee()
}

class NormalCoffeeMachine : CoffeeMachine {
    override fun makeSmallCoffee() = println("Normal: Making small coffee")

    override fun makeLargeCoffee() = println("Normal: Making large coffee")
}

//Decorator:
class EnhancedCoffeeMachine(val coffeeMachine: CoffeeMachine) : CoffeeMachine by coffeeMachine {

    // overriding behaviour
    override fun makeLargeCoffee() {
        println("Enhanced: Making large coffee")
        coffeeMachine.makeLargeCoffee()
    }

    // extended behaviour
    fun makeCoffeeWithMilk() {
        println("Enhanced: Making coffee with milk")
        coffeeMachine.makeSmallCoffee()
        println("Enhanced: Adding milk")
    }
}

Usage

val normalMachine = NormalCoffeeMachine()
    val enhancedMachine = EnhancedCoffeeMachine(normalMachine)

    // non-overridden behaviour
    enhancedMachine.makeSmallCoffee()
    // overriding behaviour
    enhancedMachine.makeLargeCoffee()
    // extended behaviour
    enhancedMachine.makeCoffeeWithMilk()

Output

Normal: Making small coffee

Enhanced: Making large coffee
Normal: Making large coffee

Enhanced: Making coffee with milk
Normal: Making small coffee
Enhanced: Adding milk

8. Structural: Composite Pattern

The composite pattern is used to compose zero-or-more similar objects so it can manipulate them as one object.

Example

open class Equipment(private var price: Int, private var name: String) {
    open fun getPrice(): Int = price
}


/*
[composite]
*/

open class Composite(name: String) : Equipment(0, name) {
    val equipments = ArrayList<Equipment>()

    fun add(equipment: Equipment) {
        this.equipments.add(equipment)
    }

    override fun getPrice(): Int {
        return equipments.map { it.getPrice() }.sum()
    }
}


/*
 leafs
*/

class Cabbinet : Composite("cabbinet")
class FloppyDisk : Equipment(80, "Floppy Disk")
class HardDrive : Equipment(250, "Hard Drive")
class Memory : Equipment(280, "Memory")

Usage

var cabbinet = Cabbinet()
cabbinet.add(FloppyDisk())
cabbinet.add(HardDrive())
cabbinet.add(Memory())
println(cabbinet.getPrice())

Output

610

9. Structural: Protection Proxy Pattern

The proxy pattern is used to provide a surrogate or placeholder object, which references an underlying object. Protection proxy is restricting access.

Example

interface File {
    fun read(name: String)
}

class NormalFile : File {
    override fun read(name: String) = println("Reading file: $name")
}

//Proxy:
class SecuredFile : File {
    val normalFile = NormalFile()
    var password: String = ""

    override fun read(name: String) {
        if (password == "secret") {
            println("Password is correct: $password")
            normalFile.read(name)
        } else {
            println("Incorrect password. Access denied!")
        }
    }
}

Usage

val securedFile = SecuredFile()
securedFile.read("readme.md")

securedFile.password = "secret"
securedFile.read("readme.md")

Output

Incorrect password. Access denied!
Password is correct: secret
Reading file: readme.md

10. Behavioral: Observer / Listener Pattern

The observer pattern is used to allow an object to publish changes to its state. Other objects subscribe to be immediately notified of any changes.

Example

interface TextChangedListener {

    fun onTextChanged(oldText: String, newText: String)
}

class PrintingTextChangedListener : TextChangedListener {
    
    private var text = ""
    
    override fun onTextChanged(oldText: String, newText: String) {
        text = "Text is changed: $oldText -> $newText"
    }
}

class TextView {

    val listeners = mutableListOf<TextChangedListener>()

    var text: String by Delegates.observable("<empty>") { _, old, new ->
        listeners.forEach { it.onTextChanged(old, new) }
    }
}

Usage

val textView = TextView().apply {
    listener = PrintingTextChangedListener()
}

with(textView) {
    text = "old name"
    text = "new name"
}

Output

Text is changed <empty> -> old name
Text is changed old name -> new name

11. Behavioral: Command Pattern

The command pattern is used to express a request, including the call to be made and all of its required parameters, in a command object. The command may then be executed immediately or held for later use.

Example

interface OrderCommand {
    fun execute()
}

class OrderAddCommand(val id: Long) : OrderCommand {
    override fun execute() = println("Adding order with id: $id")
}

class OrderPayCommand(val id: Long) : OrderCommand {
    override fun execute() = println("Paying for order with id: $id")
}

class CommandProcessor {

    private val queue = ArrayList<OrderCommand>()

    fun addToQueue(orderCommand: OrderCommand): CommandProcessor =
        apply {
            queue.add(orderCommand)
        }

    fun processCommands(): CommandProcessor =
        apply {
            queue.forEach { it.execute() }
            queue.clear()
        }
}

Usage

CommandProcessor()
    .addToQueue(OrderAddCommand(1L))
    .addToQueue(OrderAddCommand(2L))
    .addToQueue(OrderPayCommand(2L))
    .addToQueue(OrderPayCommand(1L))
    .processCommands()

Output

Adding order with id: 1
Adding order with id: 2
Paying for order with id: 2
Paying for order with id: 1

12. Behavioral: Strategy Pattern

The strategy pattern is used to create an interchangeable family of algorithms from which the required process is chosen at run-time.

Example

class Printer(private val stringFormatterStrategy: (String) -> String) {

    fun printString(string: String) {
        println(stringFormatterStrategy(string))
    }
}

val lowerCaseFormatter: (String) -> String = { it.toLowerCase() }
val upperCaseFormatter = { it: String -> it.toUpperCase() }

Usage

val inputString = "OLD name NEW name "

val lowerCasePrinter = Printer(lowerCaseFormatter)
lowerCasePrinter.printString(inputString)

val upperCasePrinter = Printer(upperCaseFormatter)
upperCasePrinter.printString(inputString)

val prefixPrinter = Printer { "Prefix: $it" }
prefixPrinter.printString(inputString)

Output

old name new name
OLD NAME NEW NAME
Prefix: OLD name NEW name

13. Behavioral: State Pattern

The state pattern is used to alter the behaviour of an object as its internal state changes. The pattern allows the class for an object to apparently change at run-time.

Example

sealed class AuthorizationState

object Unauthorized : AuthorizationState()

class Authorized(val userName: String) : AuthorizationState()

class AuthorizationPresenter {

    private var state: AuthorizationState = Unauthorized

    val isAuthorized: Boolean
        get() = when (state) {
            is Authorized -> true
            is Unauthorized -> false
        }

    val userName: String
        get() {
            val state = this.state //val enables smart casting of state
            return when (state) {
                is Authorized -> state.userName
                is Unauthorized -> "Unknown"
            }
        }

    fun loginUser(userName: String) {
        state = Authorized(userName)
    }

    fun logoutUser() {
        state = Unauthorized
    }

    override fun toString() = "User '$userName' is logged in: $isAuthorized"
}

Usage

val authorizationPresenter = AuthorizationPresenter()

authorizationPresenter.loginUser("admin")
println(authorizationPresenter)

authorizationPresenter.logoutUser()
println(authorizationPresenter)

Output

User 'admin' is logged in: true
User 'Unknown' is logged in: false

14. Behavioral: Chain of Responsibility Pattern

The chain of responsibility pattern is used to process varied requests, each of which may be dealt with by a different handler.

Example

interface HeadersChain {
    fun addHeader(inputHeader: String): String
}

class AuthenticationHeader(val token: String?, var next: HeadersChain? = null) : HeadersChain {

    override fun addHeader(inputHeader: String): String {
        token ?: throw IllegalStateException("Token should be not null")
        return inputHeader + "Authorization: Bearer $token\n"
            .let { next?.addHeader(it) ?: it }
    }
}

class ContentTypeHeader(val contentType: String, var next: HeadersChain? = null) : HeadersChain {

    override fun addHeader(inputHeader: String): String =
        inputHeader + "ContentType: $contentType\n"
            .let { next?.addHeader(it) ?: it }
}

class BodyPayload(val body: String, var next: HeadersChain? = null) : HeadersChain {

    override fun addHeader(inputHeader: String): String =
        inputHeader + "$body"
            .let { next?.addHeader(it) ?: it }
}

Usage

//create chain elements
val authenticationHeader = AuthenticationHeader("123456")
val contentTypeHeader = ContentTypeHeader("json")
val messageBody = BodyPayload("Body:\n{\n\"username\"=\"coolmonktechie\"\n}")

//construct chain
authenticationHeader.next = contentTypeHeader
contentTypeHeader.next = messageBody

//execute chain
val messageWithAuthentication =
    authenticationHeader.addHeader("Headers with Authentication:\n")
println(messageWithAuthentication)

val messageWithoutAuth =
    contentTypeHeader.addHeader("Headers:\n")
println(messageWithoutAuth)

Output

Headers with Authentication:
Authorization: Bearer 123456
ContentType: json
Body:
{
"username"="coolmonktechie"
}

Headers:
ContentType: json
Body:
{
"username"="coolmonktechie"
}

15. Behavioral: Visitor Pattern

The visitor pattern is used to separate a relatively complex set of structured data classes from the functionality that may be performed upon the data that they hold.

Example

interface ReportVisitable {
    fun <R> accept(visitor: ReportVisitor<R>): R
}

class FixedPriceContract(val costPerYear: Long) : ReportVisitable {
    override fun <R> accept(visitor: ReportVisitor<R>): R = visitor.visit(this)
}

class TimeAndMaterialsContract(val costPerHour: Long, val hours: Long) : ReportVisitable {
    override fun <R> accept(visitor: ReportVisitor<R>): R = visitor.visit(this)
}

class SupportContract(val costPerMonth: Long) : ReportVisitable {
    override fun <R> accept(visitor: ReportVisitor<R>): R = visitor.visit(this)
}

interface ReportVisitor<out R> {

    fun visit(contract: FixedPriceContract): R
    fun visit(contract: TimeAndMaterialsContract): R
    fun visit(contract: SupportContract): R
}

class MonthlyCostReportVisitor : ReportVisitor<Long> {

    override fun visit(contract: FixedPriceContract): Long =
        contract.costPerYear / 12

    override fun visit(contract: TimeAndMaterialsContract): Long =
        contract.costPerHour * contract.hours

    override fun visit(contract: SupportContract): Long =
        contract.costPerMonth
}

class YearlyReportVisitor : ReportVisitor<Long> {

    override fun visit(contract: FixedPriceContract): Long =
        contract.costPerYear

    override fun visit(contract: TimeAndMaterialsContract): Long =
        contract.costPerHour * contract.hours

    override fun visit(contract: SupportContract): Long =
        contract.costPerMonth * 12
}

Usage

val projectAlpha = FixedPriceContract(costPerYear = 10000)
val projectGamma = TimeAndMaterialsContract(hours = 150, costPerHour = 10)
val projectBeta = SupportContract(costPerMonth = 500)
val projectKappa = TimeAndMaterialsContract(hours = 50, costPerHour = 50)

val projects = arrayOf(projectAlpha, projectBeta, projectGamma, projectKappa)

val monthlyCostReportVisitor = MonthlyCostReportVisitor()

val monthlyCost = projects.map { it.accept(monthlyCostReportVisitor) }.sum()
println("Monthly cost: $monthlyCost")
assertThat(monthlyCost).isEqualTo(5333)

val yearlyReportVisitor = YearlyReportVisitor()
val yearlyCost = projects.map { it.accept(yearlyReportVisitor) }.sum()
println("Yearly cost: $yearlyCost")
assertThat(yearlyCost).isEqualTo(20000)

Output

Monthly cost: 5333
Yearly cost: 20000

16. Behavioral: Mediator Pattern

Mediator design pattern is used to provide a centralized communication medium between different objects in a system. This pattern is very helpful in an enterprise application where multiple objects are interacting with each other.

Example

class ChatUser(private val mediator: ChatMediator, val name: String) {
    fun send(msg: String) {
        println("$name: Sending Message= $msg")
        mediator.sendMessage(msg, this)
    }

    fun receive(msg: String) {
        println("$name: Message received: $msg")
    }
}

class ChatMediator {

    private val users: MutableList<ChatUser> = ArrayList()

    fun sendMessage(msg: String, user: ChatUser) {
        users
            .filter { it != user }
            .forEach {
                it.receive(msg)
            }
    }

    fun addUser(user: ChatUser): ChatMediator =
        apply { users.add(user) }

}

Usage

val mediator = ChatMediator()
val user1 = ChatUser(mediator, "User1")

mediator
    .addUser(ChatUser(mediator, "User2"))
    .addUser(ChatUser(mediator, "User3"))
    .addUser(user1)
user1.send("Hello everyone!")

Output

User1: Sending Message= Hello everyone!
User2: Message received: Hello everyone!
user3: Message received: Hello everyone!

17. Behavioral: Memento Pattern

The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).

Example

data class Memento(val state: String)

class Originator(var state: String) {

    fun createMemento(): Memento {
        return Memento(state)
    }

    fun restore(memento: Memento) {
        state = memento.state
    }
}

class CareTaker {
    private val mementoList = ArrayList<Memento>()

    fun saveState(state: Memento) {
        mementoList.add(state)
    }

    fun restore(index: Int): Memento {
        return mementoList[index]
    }
}

Usage

val originator = Originator("initial state")
val careTaker = CareTaker()
careTaker.saveState(originator.createMemento())

originator.state = "State #1"
originator.state = "State #2"
careTaker.saveState(originator.createMemento())

originator.state = "State #3"
println("Current State: " + originator.state)
assertThat(originator.state).isEqualTo("State #3")

originator.restore(careTaker.restore(1))
println("Second saved state: " + originator.state)
assertThat(originator.state).isEqualTo("State #2")

originator.restore(careTaker.restore(0))
println("First saved state: " + originator.state)

Output

Current State: State #3
Second saved state: State #2
First saved state: initial state

That’s all about in this article.

Related Other Articles / Posts

Conclusion

In this article, we understood about why design patterns are valuable and most frequently used in Kotlin. This article shows the most frequently used design patterns in Kotlin with an authentic example.

Thanks for reading! I hope you enjoyed and learned about Valuable Design Patterns concepts in Kotlin. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolMonkTechie as below link :

You can also follow official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Android – How To Animate Drawable Graphics In Android ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Animate Drawable Graphics In Android ?).

In this article, we will learn about how to animate drawable graphics in Android. In some situations, images need to be animated on screen. This is useful if :

  • we want to display a custom loading animation comprising several images, or
  • if one icon to morph into another after a user’s action.

Android provides a couple options for animating drawable as below :

  • Animation Drawable – This allows us to specify several static drawable files that will display one at a time to create an animation.
  • Animated Vector Drawable – This animates the properties of a vector drawable.

This article shows the animated images on screen in android using Drawable and Vector Drawable.

A famous quote about learning is :

” Live as if you were to die tomorrow. Learn as if you were to live forever. “

So let’s begin.

1. Animation Drawable

One way to animate Drawables is to load a series of Drawable resources one after another to create an animation. This is a traditional animation in the sense that it creates with a sequence of different images. It played in order, like a roll of film. The AnimationDrawable class is the basis for Drawable animations.

While we can define the frames of an animation in our code, using the AnimationDrawable class API, it’s more accomplished with a single XML file that lists the frames that compose the animation. The XML file for this kind of animation belongs in the res/drawable/ directory of our Android project. Here, the instructions are the order and duration for each frame of the animation.

The XML file comprises an <animation-list> element as the root node and a series of child <item> nodes that each define a frame: a drawable resource for the frame and the frame duration. Here’s an example XML file for a Drawable animation:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="true">
    <item android:drawable="@drawable/rocket_thrust1" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust2" android:duration="200" />
    <item android:drawable="@drawable/rocket_thrust3" android:duration="200" />
</animation-list>

This animation runs for just three frames. By setting the android:oneshot attribute of the list to true, it will cycle just once, then stop and hold on the last frame. The animation will loop if set false. With this XML saved as rocket_thrust.xml in the res/drawable/ directory of the project, it can be added as the background image to a View and then called to play.

Here’s an example Activity, in which the animation is added to an ImageView and then animated when the screen is touched:

private lateinit var rocketAnimation: AnimationDrawable

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main)

    val rocketImage = findViewById<ImageView>(R.id.rocket_image).apply {
        setBackgroundResource(R.drawable.rocket_thrust)
        rocketAnimation = background as AnimationDrawable
    }

    rocketImage.setOnClickListener({ rocketAnimation.start() })
}

It’s important to note that the start() method called on the AnimationDrawable cannot be called during the onCreate() method of our Activity, because the AnimationDrawable does not fully attach to the window. If we want to play the animation immediately, without requiring interaction, then we might want to call it from the onStart()method in our Activity, which will get called when Android makes the view visible on screen.

2. Animated Vector Drawable

vector drawable is a drawable that is scalable without getting Pixelated or blurry. The AnimatedVectorDrawable class (and AnimatedVectorDrawableCompat for backward-compatibility) lets us animate the properties of a vector drawable, such as rotating it or changing the path data to morph it into a different image.

We normally define animated vector drawables in three XML files:

  • A vector drawable with the <vector> element in res/drawable/.
  • An animated vector drawable with the <animated-vector> element in res/drawable/.
  • One or more object animators with the <objectAnimator> element in res/animator/.

Animated vector drawables can animate the attributes of the <group> and <path> elements. The <group> elements defines a set of paths or subgroups, and the <path> element defines paths to be drawn.

When we define a vector drawable that we want to animate, use the android:name attribute to assign a unique name to groups and paths, so we can refer to them from our animator definitions.

res/drawable/vectordrawable.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="64dp"
    android:width="64dp"
    android:viewportHeight="600"
    android:viewportWidth="600">
    <group
        android:name="rotationGroup"
        android:pivotX="300.0"
        android:pivotY="300.0"
        android:rotation="45.0" >
        <path
            android:name="v"
            android:fillColor="#000000"
            android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
    </group>
</vector>

The animated vector drawable definition refers to the groups and paths in the vector drawable by their names:

res/drawable/animatorvectordrawable.xml

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
  android:drawable="@drawable/vectordrawable" >
    <target
        android:name="rotationGroup"
        android:animation="@animator/rotation" />
    <target
        android:name="v"
        android:animation="@animator/path_morph" />
</animated-vector>

The animation definitions represent ObjectAnimator or AnimatorSet objects. The first animator in this example rotates the target group 360 degrees:

res/animator/rotation.xml

<objectAnimator
    android:duration="6000"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360" />

For instance, the second animator morphs the vector drawable’s path from one shape to another. Both paths are compatible for morphing, because they must have the same number of commands and the same number of parameters for each command.

res/animator/path_morph.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <objectAnimator
        android:duration="3000"
        android:propertyName="pathData"
        android:valueFrom="M300,70 l 0,-70 70,70 0,0   -70,70z"
        android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0 z"
        android:valueType="pathType" />
</set>

Here is the resulting AnimatedVectorDrawable:

That’s all about in this article.

Related Other Articles / Posts

Conclusion

In this article, we understood about how to animate drawable graphics in Android. This article shows the animated images on screen in android using Drawable and Vector Drawable.

Thanks for reading! I hope you enjoyed and learned about drawable graphics concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolMonkTechie as below link :

You can also follow official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Android – The Most Popular Best Ways To Add Motion To UI

Hello Readers, CoolMonkTechie heartily welcomes you in this article (The most popular best ways to Add Motion To UI).

In this article, we will learn about different best ways to Add Motion To UI using Android Animation.

When our UI changes in response to user action, we should animate the layout transitions. These animations give users feedback on their actions and help keep them oriented to the UI. Android includes the transitions framework, which enables us to easily animate changes between two view hierarchies. The framework animates the views at runtime by changing some of their property values. The framework includes built-in animations for common effects and lets us create custom animations and transition life cycle callbacks. This article shows the overview of different ways we can Add Motion to UI using Android Animation.

A famous quote about learning is :

” I am always ready to learn although I do not always like being taught. “

So Let’s begin.

Animation Overview

Animations can add visual cues that notify users about what’s going on in our app.  They are especially useful when the UI changes state, such as when new content loads or new actions become available. Animations also add a polished look to your app, which gives it a higher quality look and feel. Android includes different animation APIs depending on what type of animation you want.

Different Ways To Add Motion To UI

Android provides the different ways to add motion to UI using Android animation:

1. Animate bitmaps

When we want to animate a bitmap graphic such as an icon or illustration, then we should use the drawable animation APIs. Usually, these animations define statically with a drawable resource, but we can also define the animation behavior at runtime.

Source: Android Developers (An animated drawable)

For example, animating a play button transforming into a pause button when tapped is a pleasant way to communicate to the user that related with the two actions, and that pressing one makes the other visible.

2. Animate UI visibility and motion

When we need to change the visibility or position of views in our layout, we include subtle animations to help the user understand how the UI is changing.

To move, reveal, or hide views within the current layout, we can use the property animation system provided by the android.animation package, available in Android 3.0 (API level 11) and higher. These APIs update the properties of our View objects over time, continuously redrawing the view as the properties change. 

Source: Android Developer ( A subtle animation when a dialog appears and disappears makes the UI change less jarring )

For example, when we change the position properties, the view moves across the screen, or when we change the alpha property, the view fades in or out.

To create these animations with the least amount of effort, we can enable animations on our layout so that when we change the visibility of a view, an animation applies automatically.

Physics-based motion

Our animations are natural-looking if it apply real-world physics. For example, they should maintain momentum when their target changes, and make smooth transitions during any changes.

The Android Support library includes physics-based animation APIs to provide these behaviors that rely on the laws of physics to control how our animations occur.

There are Two common physics-based animations as :

  • Spring Animation – Physics-based motion drives by force. Spring force is one such force that guides interactivity and motion. A spring force has the following properties: damping and stiffness. In a spring-based animation, the value and the velocity calculate based on the spring force that applies on each frame.
  • Fling Animation – Fling-based animation uses a friction force proportional to an object’s velocity. Use it to animate a property of an object and to end the animation gradually. It has an initial momentum, which receives from the gesture velocity, and gradually slows down. The animation ends when the velocity of the animation is low enough that it makes no visible change on the device screen.

Animations are not based on physics — such as those built with ObjectAnimator APIs—are fairly static and have a fixed duration. If the target value changes, we need to cancel the animation at the time of target value change, re-configure the animation with a new value as the new start value, and add the new target value. Visually, this process creates an abrupt stop in the animation, and a disjointed movement afterwards, as shown in below figure:

Source : Android Developer (Animation built with ObjectAnimator)

Whereas, animations built by with physics-based animation APIs such as DynamicAnimation are driven by force. The change in the target value results in a change in force. The new force applies on the existing velocity, which makes a continuous transition to the new target. This process results in a more natural-looking animation, as shown in figure .

Source : Android Developer (Animation built with physics-based APIs)

3. Animate layout changes

On Android 4.4 (API level 19) and higher, We can use the transition framework to create animations when we require swapping the layout within the current activity or fragment.

All we need to do is specify the starting and ending layout, and what type of animation we want to use. Then the system figures out and executes an animation between the two layouts. We can use this to swap out the entire UI or to move/replace just some views.

For example, when the user taps an item to see more information, we can replace the layout with the item details, applying a transition like the one shown in figure.

Source : Android Developer ( An animation to show more details can be achieved by either changing the layout or starting a new activity )

The starting and ending layout are each stored in a Scene, though the starting scene determines automatically from the current layout. We then create a Transition to tell the system what type of animation we want and then call TransitionManager.go() and the system runs the animation to swap the layouts.

4. Animate between activities

On Android 5.0 (API level 21) and higher, we can also create animations that transition between our activities. They base this on the same transition framework, but it allows us to create animations between layouts in separate activities.

We can apply simple animations such as sliding the new activity in from the side or fading it in, but we can also create animations that transition between shared views in each activity. For example, when the user taps an item to see more information, we can transition into a new activity with an animation that seamlessly grows that item to fill the screen.

As usual, we call startActivity(), but pass it a bundle of options provided by ActivityOptions.makeSceneTransitionAnimation(). This bundle of options may include which views are shared between the activities so the transition framework can connect them during the animation.

That’s all about in this article.

Related Other Articles / Posts

Conclusion

In this article, we understood about different best ways to add motions to UI using Android Animation. This article showed the overview of different ways to handle motion in Android UI.

Thanks for reading! I hope you enjoyed and learned about UI Motion Concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolmonkTechie as below link :

You can also follow official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

React Native – The Most Popular Best Ways To Make REST API Calls In React Native

Hello Readers, CoolMonkTechie heartily welcomes you in this article (The most popular best ways to make REST API Calls in React Native ).

In this article, we will learn about different best ways to make REST API Calls in React Native. Many mobile apps need to load resources from a remote URL. We may want to make a POST request to a REST API, or we may need to fetch a chunk of static content from another server.

Applications on the web and mobile devices often cannot store all the information that they need. Therefore, they have to reach out to the web to update data, retrieve data, or request a service from a third-party. The transaction that takes place between two devices (client and server) is called an API call. An API (application programming interface) typically will use the REST design paradigm to manage all how it can be accessed (called).

This article shows the fetch function and the axios library. Both methods achieve the same task, making an HTTP call, but they go about it in slightly different ways.

A famous quote about learning is :

” The expert in anything was once a beginner. “

So Let’s begin.

Using Fetch

React Native provides the Fetch API for our networking needs. Fetch will seem familiar if we have used XMLHttpRequest or other networking APIs before. The Fetch API is a built-in Javascript function. We can use fetch by providing the URL for the location of our content. This will send a GET request to that URL.

Making Requests

In order to fetch content from an arbitrary URL, we can pass the URL to fetch:

fetch('https://mywebsite.com/mydata.json');

Fetch also takes an optional second argument that allows us to customize the HTTP request. We may want to specify additional headers, or make a POST request:

fetch('https://mywebsite.com/endpoint/', {
  method: 'POST',
  headers: {
    Accept: 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    firstParam: 'yourValue',
    secondParam: 'yourOtherValue'
  })
});

We can follow at the Fetch Request docs for a full list of properties.

Handling The Response

The above examples show how we can make a request. Most times, we will want to do something with the response.

Networking is an inherently asynchronous operation. Fetch methods will return a Promise that makes it straightforward to write code that works asynchronously:

const getMoviesFromApi = () => {
  return fetch('https://reactnative.dev/movies.json')
    .then((response) => response.json())
    .then((json) => {
      return json.movies;
    })
    .catch((error) => {
      console.error(error);
    });
};

We can read through the above code like;

  1. Fetch the data at the URL 'https://reactnative.dev/movies.json'
  2. Then, transform the response into JSON
  3. Then, return the movies property on the JSON object
  4. If there is an error, log the error to the console.

We can also use the async / await syntax in a React Native app:

const getMoviesFromApiAsync = async () => {
  try {
    let response = await fetch(
      'https://reactnative.dev/movies.json'
    );
    let json = await response.json();
    return json.movies;
  } catch (error) {
    console.error(error);
  }
};

We need to catch any errors that may be thrown by fetch, otherwise they will be dropped silently.

Fetch Example

import React, { useEffect, useState } from 'react';
import { ActivityIndicator, FlatList, Text, View } from 'react-native';

export default App = () => {
  const [isLoading, setLoading] = useState(true);
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch('https://reactnative.dev/movies.json')
      .then((response) => response.json())
      .then((json) => setData(json.movies))
      .catch((error) => console.error(error))
      .finally(() => setLoading(false));
  }, []);

  return (
    <View style={{ flex: 1, padding: 24 }}>
      {isLoading ? <ActivityIndicator/> : (
        <FlatList
          data={data}
          keyExtractor={({ id }, index) => id}
          renderItem={({ item }) => (
            <Text>{item.title}, {item.releaseYear}</Text>
          )}
        />
      )}
    </View>
  );
};

By default, iOS will block any request that’s not encrypted using SSL. If we need to fetch from a cleartext URL (one that begins with http), then App Transport Security exception will first need to add.

It is more secure to add exceptions only for those domains, If we know ahead of time to need to access the domains; if we do not know the domains until runtime, ATS can completely disable.

This is a simple way to send HTTP requests as soon as possible. However, another popular method for sending HTTP requests is with third-party libraries.

Using XMLHttpRequest API

The XMLHttpRequest API is built into React Native. This means that we can use third-party libraries such as frisbee or axios that depend on it, or we can use the XMLHttpRequest API directly if we prefer.

var request = new XMLHttpRequest();
request.onreadystatechange = (e) => {
  if (request.readyState !== 4) {
    return;
  }

  if (request.status === 200) {
    console.log('success', request.responseText);
  } else {
    console.warn('error');
  }
};

request.open('GET', 'https://mywebsite.com/endpoint/');
request.send();

The security model for XMLHttpRequest differs from on web, as there is no concept of CORS in native apps.

Using WebSocket Protocal

React Native also supports WebSockets , a protocol which provides full-duplex communication channels over a single TCP connection.

var ws = new WebSocket('ws://host.com/path');

ws.onopen = () => {
  // connection opened
  ws.send('something'); // send a message
};

ws.onmessage = (e) => {
  // a message was received
  console.log(e.data);
};

ws.onerror = (e) => {
  // an error occurred
  console.log(e.message);
};

ws.onclose = (e) => {
  // connection closed
  console.log(e.code, e.reason);
};

Using Axios Library

The Axios is a Javascript library used to make HTTP requests, and it supports the Promise API that is native to JS ES6. The Axios library has grown in popularity alongside the increase in apps that exchange information using JSON. Three things that make the Axios library so useful are the ability to;

  • Intercept requests and responses
  • Transform request and response data
  • Automatically transform data for JSON

Axios Library Example

import Axios from 'axios'

Axios({
  url: '/data',
  method: 'get',
  baseURL: 'https://example.com',
  transformRequest: [function (data, headers) {
    // Do whatever you want to transform the data
 
    return data;
  }],
  transformResponse: [function (data) {
    // Do whatever you want to transform the data
 
    return data;
  }],
  headers: {'X-Requested-With': 'XMLHttpRequest'},
  data: {
    name: 'Some Name'
  },
})

In the above example, we have passed a configuration object that calls an example URL with a GET request. We’ll notice that some parameter names are common between fetch and Axios.

Next, let’s look at a smaller GET request.

axios.get('https://example.com/data')
  .then(function (response) {
    return response.names;
  })
  .catch(function (error) {
    console.log(error);
  })

We are calling the same URL as we did with the fetch API, but we don’t have to manually transform the response object into JSON. Axios has more features that can be useful with bigger applications.

That’s all about in this article.

Conclusion

In this article, we understood about different best ways to make REST API Calls in React Native. This article showed the fetch and axios library usage with code snippets in React Native .

Thanks for reading! I hope you enjoyed and learned about REST API Calls concepts in React Native. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolmonkTechie as below link :

You can also follow official website and tutorials of React Native as below links :

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING!!???

React Native – How To Use Redux With React Hooks In React Native Application ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article.

In this article, we will learn how to use Redux with React Hooks by building a real React Native Application. Redux is a popular React and React Native state management library, meant to be used in complex React and React Native apps where sharing state between multi-level components can get extremely difficult to manage. React Hooks provide us  the ability to use functional components in React or React Native apps. Their ability to provide support to manage a component’s state and handle side-effects offers an alternative to class components.

In this article, let us look at some hooks provided by the react-redux library that provides a way to avoid writing boilerplate code when using the connect() High Order Component.

To understand the use of Redux with React Hooks in React Native application, We will cover the below steps as below :

  1. Configure a New React Native Application
  2. Create Mock Screens
  3. Set Up a Stack Navigator
  4. Create an Overlay Modal With Transparent Background
  5. Navigate to the Modal Screen
  6. Add a Custom Modal With a Transparent Background
  7. Adding a Text Input in the Custom Modal Screen
  8. Add a Custom Header to the List Screen
  9. Change the StatusBar Appearance
  10. Add a List view
  11. Creating a Root Reducer
  12. Configuring a Store
  13. Implement the useSelector Hook
  14. Adding Items to the List by Dispatching an Action Creator
  15. Adding FlatList to Display the Items in the List
  16. Updating the Header
  17. Removing an Item
  18. The final code of the demo application
  19. The final output of the demo application

A famous quote about learning is :

” The purpose of learning is growth, and our minds, unlike our bodies, can continue growing as we continue to live. “

So Let’s begin.


Step 1 – Configure a New React Native Application

To configure a new React Native app, we are going to use expo-cli with a blank template to generate a new app.

expo init projectname

Firstly, we have generated the project, Then we navigate inside the directory and install the following dependencies.

yarn add redux react-redux @react-navigation/native @react-navigation/stack

We are going to use Stack Navigator from the react-navigation library for two different screens in this demo app. For expo-cli, we run the following command to install required dependencies for the navigator to work.

expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view


Step 2 – Create Mock Screens

In this section, let us create two screens that the app is going to use to display a list of items and allow the user to add to each item. We create a new directory called src/screens and then create two new ListScreen.js and ModalScreen.js files.

Each of these screen files is going to have some random data to display until the stack navigator is set up.

Here is the code snippet for ListScreen.js :

import React from "react";import { StyleSheet, Text, View } from "react-native";function ListScreen() {return (<View style={styles.container}><Text>List Screen</Text></View>);}const styles = StyleSheet.create({container: {flex: 1,backgroundColor: "#fff",alignItems: "center",justifyContent: "center",},});export default ListScreen;

Here is the code snippet for ModalScreen.js :

import React from "react";import { StyleSheet, Text, View } from "react-native";function ModalScreen() {return (<View style={styles.container}><Text>Modal Screen</Text></View>);}const styles = StyleSheet.create({container: {flex: 1,backgroundColor: "#fff",alignItems: "center",justifyContent: "center",},});export default ModalScreen;


Step 3 – Set Up a Stack Navigator

A Stack Navigator allows the app to transit between different or multiple screens and manages the navigation history. We create a new file called AppNavigator.js inside the src/navigation directory. This file is going to contain all the configurations required to set up a Stack Navigator.

With the recent release of react-navigation version 5, the way to configure a stack navigator has changed. The major highlight of these new changes is the component-based configuration. Navigation patterns are now more component-based, common use cases can now be handled with pre-defined Hooks, the new architecture allowing us to configure and update a screen from within the component itself.

import * as React from "react";import { NavigationContainer } from "@react-navigation/native";import { createStackNavigator } from "@react-navigation/stack";import ListScreen from "../screens/ListScreen";import ModalScreen from "../screens/ModalScreen";

The NavigationContainer is a component provided by the react-navigation library that manages the navigation tree. It contains the navigation state and wraps all the navigator’s structure.

The createStackNavigator is a function that implements a stack navigation pattern. This function returns two React components: Screen and Navigator, that are going to allow us to configure each component screen.

const Stack = createStackNavigator();function MainStackNavigator() {return (<NavigationContainer><Stack.Navigator><Stack.Screen name="List" component={ListScreen} /><Stack.Screen name="Modal" component={ModalScreen} /></Stack.Navigator></NavigationContainer>);}export default MainStackNavigator;

Open the App.js file and import the MainStackNavigator in the root of the app as shown below:

import React from "react";import MainStackNavigator from "./src/navigation/AppNavigator";export default function App() {return <MainStackNavigator />;}

Now, we go the terminal window and execute the command expo start. In the simulator or the real device we are running the Expo client, we are going to notice similar results as shown below:

The first screen in the Stack Navigator is ListScreen, which is shown as above.


Step 4 – Create an Overlay Modal With Transparent Background

The modal can be easily configured with a stack navigator with an overlay of transparent background on the screen as they display it. Since the ListScreen is going to be the first screen and display a list of items, the ModalScreen is going to be a dialog. This dialog appears by clicking a button from the ListScreen in the current application and adds a transparent layer on the screen behind it when opened. The previous screen will be visible underneath this dialog.

This can be done by configuring screenOptions on a Stack Navigator. The react-navigation library provides a way to enable the overlay with a property called cardOverlayEnabled.

Modify AppNavigator.js file as following :

function MainStackNavigator() {return (<NavigationContainer><Stack.Navigatormode="modal"headerMode="none"screenOptions={{cardStyle: { backgroundColor: "transparent" },cardOverlayEnabled: true,cardStyleInterpolator: ({ current: { progress } }) => ({cardStyle: {opacity: progress.interpolate({inputRange: [0, 0.5, 0.9, 1],outputRange: [0, 0.25, 0.7, 1],}),},overlayStyle: {opacity: progress.interpolate({inputRange: [0, 1],outputRange: [0, 0.5],extrapolate: "clamp",}),},}),}}><Stack.Screen name="List" component={ListScreen} /><Stack.Screen name="Modal" component={ModalScreen} /></Stack.Navigator></NavigationContainer>);}

We have to configure the styles of both the screens and add a way for the modal dialog to open from the ListScreen.


Step 5 – Navigate to the Modal Screen

To navigate to the modal screen, let’s add a floating action button with an icon to the ListScreen.js screen component.

This button is going to be touchable and on a single touch is going to navigate to the ModalScreen. The navigation is going to be handled by the navigation prop that can be passed as an argument to the functional component ListScreen. This is only possible because the ListScreen is part of the Stack navigator.

Any screen in a React Native app that utilizes a react-navigation library is a route or a part of a navigation pattern that has access to navigation prop.

Modify the ListScreen.js file as below:

import React from "react";import {StyleSheet,StatusBar,Text,View,TouchableOpacity,} from "react-native";import { Ionicons } from "@expo/vector-icons";function ListScreen({ navigation }) {return (<View style={styles.container}><View style={styles.fabContainer}><TouchableOpacityonPress={() => navigation.navigate("Modal")}style={styles.fabButton}><Ionicons name="ios-add" color="#fff" size={70} /></TouchableOpacity></View></View>);}const styles = StyleSheet.create({container: {flex: 1,backgroundColor: "blue",},fabContainer: {justifyContent: "flex-end",flexDirection: "row",position: "absolute",right: 10,bottom: 20,},fabButton: {backgroundColor: "blue",borderRadius: 35,width: 70,height: 70,alignItems: "center",justifyContent: "center",},});export default ListScreen;

We go back to the simulator device, and we are going to notice the changes. The first thing to notice is the action button floating at the bottom right corner.

On pressing this button, a full-screen modal will open.


Step 6 – Add a Custom Modal With a Transparent Background

In this section, let’s change the behavior of how the modal appears on the ListScreen right now and how we want it to be. As an overlay, we also want it to take the only 1/3rd of the current screen.

This modal is going to have an input field in the future to let the user add items to the list. However, for now, it is going to display a text and a close button.

The close button is going to dismiss the modal when the user wants to go back to the List screen without taking any other action. The close button is going to be placed using position: absolute property and is going to use navigation.goBack() pre-defined method to go back to the List screen.

Here is the complete code for the modal screen at this point. Open ModalScreen.js and modify it.

import React from "react";import { StyleSheet, TouchableOpacity, Text, View } from "react-native";import { Ionicons } from "@expo/vector-icons";function ModalScreen({ navigation }) {return (<View style={styles.container}><View style={styles.innerContainer}><View style={styles.closeButtonContainer}><TouchableOpacitystyle={styles.closeButton}onPress={() => navigation.goBack()}><Ionicons name="ios-close" color="#101010" size={40} /></TouchableOpacity></View><View style={styles.modalContainer}><Text style={{ color: "#444", fontSize: 20 }}>What do you want to do?</Text></View></View></View>);}const styles = StyleSheet.create({container: {flex: 1,},innerContainer: {borderTopLeftRadius: 10,borderTopRightRadius: 10,justifyContent: "flex-end",flexDirection: "row",height: "30%",width: "100%",position: "absolute",bottom: 0,right: 0,backgroundColor: "#fff",},closeButtonContainer: {position: "absolute",alignItems: "flex-end",right: 10,},closeButton: {backgroundColor: "#d3d3d3",borderRadius: 20,width: 40,height: 40,top: 10,alignItems: "center",justifyContent: "center",},modalContainer: {justifyContent: "center",alignItems: "center",position: "absolute",margin: 60,top: 10,left: 50,},});export default ModalScreen;

Here is the output you are going to get in the device after this step:


Step 7 – Adding a Text Input in the Custom Modal Screen

In this section, let’s add a text input component from the react-native core. This is going to allow the user to enter the name of the item they want to add to the list. For now, since we haven’t configured the Redux to manage the app state, let us use the hook useState to manage the component state locally.

Open ModalScreen.js and import TextInput from react-native core.

import {StyleSheet,TouchableOpacity,Text,View,TextInput,} from "react-native";

Next, inside the View that has the style of modalContainer add the following TextInput component as well as a touchable submit button. This touchable button is going to navigate back to the list screen when the user has entered a value in the input field.

<View style={styles.modalContainer}><Text style={{ color: "#444", fontSize: 20 }}>What do you want to do?</Text><TextInputstyle={{height: 50,width: 200,padding: 5,borderColor: "gray",borderBottomWidth: 1,}}numberOfLines={1}onChangeText={(value) => setValue(value)}value={value}clearButtonMode="while-editing"/><TouchableOpacitystyle={{marginTop: 10,backgroundColor: "blue",width: 50,height: 50,\alignItems: "center",justifyContent: "center",borderRadius: 5,}}onPress={() => navigation.navigate("List")}><Ionicons name="ios-arrow-dropright-circle" size={40} color="#fff" /></TouchableOpacity></View>;


Step 8 – Add a Custom Header to the List Screen

Create a new file called Header.js inside the directory src/components. This functional component is going to display the header title in the List screen.

Add the following code snippet to the file. We have just created:

import React from "react";import { View, Text, StyleSheet } from "react-native";function Header(props) {const { title } = props;return (<View style={styles.container}><Text style={styles.text}>{title}</Text></View>);}const styles = StyleSheet.create({container: {alignItems: "center",justifyContent: "center",backgroundColor: "blue",height: 125,paddingTop: 20,},text: {color: "#fff",fontSize: 28,fontWeight: "500",},});export default Header;

Next, we go to the ListScreen.js and import this functional component below the other statements.

// after other import statementsimport Header from '../components/Header'

Then add the component to render before the floating button.

function ListScreen({ navigation }) {return (<View style={styles.container}><Header title={"List"} />{/* rest of the code remains same */}</View>);}


Step 9 – Change the StatusBar Appearance

To change the StatusBar appearance, let us import it from the react-native core API.

import {StyleSheet,StatusBar,Text,View,TouchableOpacity,} from "react-native";

Next, using React Fragment short hand with angle brackets, modify the return statement of ListScreen component as below:

function ListScreen({ navigation }) {return (<><StatusBar barStyle="light-content" /><View style={styles.container}><Header title={"List"} /><View style={styles.fabContainer}><TouchableOpacityonPress={() => navigation.navigate("Modal")}style={styles.fabButton}><Ionicons name="ios-add" color="#fff" size={70} /></TouchableOpacity></View></View></>);}

We will now notice that the Status bar has a white appearance.


Step 10 – Add a List view

In this section, let’s implement the main view that is going to display a list of items. In ListScreen.js, add the following functional component called ListView.

function ListView() {return (<Viewstyle={{backgroundColor: "white",flex: 1,borderTopLeftRadius: 20,borderTopRightRadius: 20,paddingHorizontal: 20,paddingVertical: 20,}}><Text>Here goes list items</Text></View>);}

Then, modify the ListScreen to display it below the Header.

<View style={styles.container}><Header title={"List"} /><ListView />{/* rest of the code remains same */}</View>;

Go to the device, we are running the app, and we are going to notice a major difference in its appearance.


Step 11 – Creating a Root Reducer

In this section, we create a new directory called src/redux/ and inside it, a new file called reducer.js. This file is going to have the definition of action types, action creators and the only reducer we are going to create in this app. This reducer is going to be called rootReducer.

Redux is used to manage the state of the whole application. Therefore, it represents this state by one JavaScript object. Think of this object as read-only (immutable), since we cannot change this state or the object because they represent it in the form of a tree. It requires an action to do so.

Actions are similar to events in Redux. And, they can be triggered in the button press, timers or network requests.

Start by defining an action type as below.

export const ADD_ITEM = 'ADD_ITEM'

Then we define the action creator called addItem that is going to take an item from the user’s input and add it to the list. This is the action creator function that we are going to trigger later.

export const addItem = (item) => ({type: ADD_ITEM,payload: item,});

Define an initial state which is going to have an empty array called itemList. Whenever an action is triggered, the state of the application changes. The handling of the application’s state is done by the reducers.

This initial state is going to be passed as a parameter to the rootReducer. Calling the create action is going to invoke the logic defined for the same action type in the reducer.

Using a reducer, we either want to initiate the current app state or update it, without modifying the whole state on each action trigger. The spread operator returned in the action type ADD_ITEM indicates that.

To update the state, in our case, to add an item object to the itemList array, let us use the contact() that is going to return a new array whenever an item is added to the list. This also satisfies the redux philosophy of not mutating the state directly.

const initialState = {itemList: [],};const rootReducer = (state = initialState, action) => {switch (action.type) {case ADD_ITEM:return {...state,itemList: state.itemList.concat({id: Math.random(),name: action.payload,}),};default:return state;}};export default rootReducer;


Step 12 – Configuring a Store

In this section, we create a new file src/redux/store.js. A store is an object that brings the actions and reducers together. This file is going to implement that.

The store provides and holds the state at the application level instead of individual components. Add the following code snippet to it:

import { createStore } from "redux";import rootReducer from "./reducer";const store = createStore(rootReducer);export default store;

Now, we connect this store to the app, we open App.js file and import the store from this file as well as the High Order Component Provider from react-redux npm package. This HOC helps us to pass the store down to the rest of the components of the current app.

import React from "react";import { Provider as StateProvider } from "react-redux";import store from "./src/redux/store";import MainStackNavigator from "./src/navigation/AppNavigator";export default function App() {return (<StateProvider store={store}><MainStackNavigator /></StateProvider>);}


Step 13 – Implement the useSelector Hook

To access state when managing it with Redux, the useSelector hook is provided in the library. It is similar to mapStateToProps argument that is passed inside the connect(). It allows us to extract data from the Redux store state using a selector function.

The major difference between the hook and the argument (the older way) is that the hook may return any value as a result, not just an object.

Inside ListScreen.js add the following import statement.

import { useSelector } from 'react-redux'

Then, we fetch the listItems array using the hook useSelector inside the ListView component. Also, modify its return statement by displaying a message if the list is empty or not.

function ListView() {const listItems = useSelector((state) => state.itemList);return (<Viewstyle={{backgroundColor: "white",flex: 1,borderTopLeftRadius: 20,borderTopRightRadius: 20,paddingHorizontal: 20,paddingVertical: 20,}}>{listItems.length !== 0 ? (<Text>Contains List items</Text>) : (<Text style={{ fontSize: 30 }}>You list is empty :'(</Text>)}</View>);}


Step 14 – Adding Items to the List by Dispatching an Action Creator

The useDispatch() hook completely refers to the dispatch function from the Redux store. This hook is used only when there is a need to dispatch an action. In the ModalScreen.js to add an item based on the value of TextInput, the state has to be updated. This can be done by triggering the action creator method called addItem defined when creating actions inside redux/reducer.js file.

Start by importing the following statements:

import { useDispatch } from 'react-redux'import { addItem } from '../redux/reducer'

Next, inside the ModalScreen component, we create a helper method called onSaveNote which when triggered on submission of the text input, is going to trigger the action creator as well as take the user back to the List screen.

function ModalScreen({ navigation }) {const [value, setValue] = useState("");const dispatch = useDispatch();const onSaveNote = (value) => {dispatch(addItem(value));navigation.navigate("List");};/* rest of the code remains same */}

Lastly, add this helper method as the value of onPress on the submission button.

<TouchableOpacitystyle={{marginTop: 10,backgroundColor: "blue",width: 50,height: 50,alignItems: "center",justifyContent: "center",borderRadius: 5,}}onPress={() => onSaveNote(value)}><Ionicons name="ios-arrow-dropright-circle" size={40} color="#fff" /></TouchableOpacity>;


Step 15 – Adding FlatList to Display the Items in the List

To display a list of items on List screen, we open the file ListScreen.js and import the FlatList from react-native.

import {StyleSheet,StatusBar,Text,View,TouchableOpacity,FlatList,} from "react-native";

Then, modify the ListView render function as below:

{listItems.length !== 0 ? (<FlatListdata={listItems}keyExtractor={(item) => item.id.toString()}renderItem={({ item }) => (<View style={styles.listItemContainer}><View style={styles.listItemMetaContainer}><Text style={styles.itemTitle} numberOfLines={1}>{item.name}</Text></View></View>)}/>) : (<Text style={{ fontSize: 30 }}>You list is empty :'(</Text>);}


Step 16 – Updating the Header

In this section, using the current app’s state, let us display the number of items in the list to be shown in the header as well. This can be done by using useSelector hook from react-redux.

Modify the file components/Header.js as the following:

import React from "react";import { View, Text, StyleSheet } from "react-native";import { useSelector } from "react-redux";function Header(props) {const { title } = props;const listItems = useSelector((state) => state.itemList);return (<View style={styles.container}><Text style={styles.title}>{title}</Text><Text style={styles.subTitle}>Left: {listItems.length}</Text></View>);}const styles = StyleSheet.create({container: {alignItems: "center",justifyContent: "center",backgroundColor: "blue",height: 125,paddingTop: 20,},title: {color: "#fff",fontSize: 28,fontWeight: "500",},subTitle: {paddingTop: 5,fontSize: 18,color: "#fff",},});export default Header;

Here is the updated header bar when there is one item on the list.


Step 17 – Removing an Item

Since we have gone through the process of understanding how redux hooks work with React Native apps, we try adding a remove item button that is going to delete an item from the list as shown below.

Here is the updated redux/reducer file that has action type REMOVE_ITEM.

export const ADD_ITEM = "ADD_ITEM";export const REMOVE_ITEM = "REMOVE_ITEM";export const addItem = (item) => ({type: ADD_ITEM,payload: item,});export const removeItem = (id) => ({type: REMOVE_ITEM,payload: id,});const initialState = {itemList: [],};const rootReducer = (state = initialState, action) => {switch (action.type) {case ADD_ITEM:return {...state,itemList: state.itemList.concat({id: Math.random(),name: action.payload,}),};case REMOVE_ITEM:return {...state,itemList: state.itemList.filter((item) => item.id !== action.payload),};default:return state;}};export default rootReducer;

Also, here is the updated ListScreen.js where we add the button to remove items with corresponding styles.

To trigger an action, we will have to make use of useDispatch() hook.


Step 18 – The final code of the demo application

Here is the final source code of the demo app:


1. src/redux/reducer.js

export const ADD_ITEM = "ADD_ITEM";export const REMOVE_ITEM = "REMOVE_ITEM";export const addItem = (item) => ({type: ADD_ITEM,payload: item,});export const removeItem = (id) => ({type: REMOVE_ITEM,payload: id,});const initialState = {itemList: [],};const rootReducer = (state = initialState, action) => {switch (action.type) {case ADD_ITEM:return {...state,itemList: state.itemList.concat({id: Math.random(),name: action.payload,}),};case REMOVE_ITEM:return {...state,itemList: state.itemList.filter((item) => item.id !== action.payload),};default:return state;}};export default rootReducer;


2. src/redux/store.js

import { createStore } from "redux";import rootReducer from "./reducer";const store = createStore(rootReducer);export default store;


3. src/components/Header.js

import React from "react";import { View, Text, StyleSheet } from "react-native";import { useSelector } from "react-redux";function Header(props) {const { title } = props;const listItems = useSelector((state) => state.itemList);return (<View style={styles.container}><Text style={styles.title}>{title}</Text><Text style={styles.subTitle}>Left: {listItems.length}</Text></View>);}const styles = StyleSheet.create({container: {alignItems: "center",justifyContent: "center",backgroundColor: "blue",height: 125,paddingTop: 20,},title: {color: "#fff",fontSize: 28,fontWeight: "500",},subTitle: {paddingTop: 5,fontSize: 18,color: "#fff",},});export default Header;


4. src/navigation/AppNavigator.js

import * as React from "react";import { NavigationContainer } from "@react-navigation/native";import { createStackNavigator } from "@react-navigation/stack";import ListScreen from "../screens/ListScreen";import ModalScreen from "../screens/ModalScreen";const Stack = createStackNavigator();function MainStackNavigator() {return (<NavigationContainer><Stack.Navigatormode="modal"headerMode="none"screenOptions={{cardStyle: { backgroundColor: "transparent" },cardOverlayEnabled: true,cardStyleInterpolator: ({ current: { progress } }) => ({cardStyle: {opacity: progress.interpolate({inputRange: [0, 0.5, 0.9, 1],outputRange: [0, 0.25, 0.7, 1],}),},overlayStyle: {opacity: progress.interpolate({inputRange: [0, 1],outputRange: [0, 0.5],extrapolate: "clamp",}),},}),}}><Stack.Screen name="List" component={ListScreen} /><Stack.Screen name="Modal" component={ModalScreen} /></Stack.Navigator></NavigationContainer>);}export default MainStackNavigator;


5. src/screens/ListScreen.js

import React from "react";import {StyleSheet,StatusBar,Text,View,TouchableOpacity,FlatList,} from "react-native";import { Ionicons } from "@expo/vector-icons";import { useSelector, useDispatch } from "react-redux";import { removeItem } from "../redux/reducer";import Header from "../components/Header";function ListScreen({ navigation }) {return (<><StatusBar barStyle="light-content" /><View style={styles.container}><Header title={"List"} /><ListView /><View style={styles.fabContainer}><TouchableOpacityonPress={() => navigation.navigate("Modal")}style={styles.fabButton}><Ionicons name="ios-add" color="#fff" size={70} /></TouchableOpacity></View></View></>);}function ListView() {const listItems = useSelector((state) => state.itemList);console.log({ listItems });const dispatch = useDispatch();return (<Viewstyle={{backgroundColor: "white",flex: 1,borderTopLeftRadius: 20,borderTopRightRadius: 20,paddingHorizontal: 20,paddingVertical: 20,}}>{listItems.length !== 0 ? (<FlatListdata={listItems}keyExtractor={(item) => item.id.toString()}renderItem={({ item }) => (<View style={styles.listItemContainer}><Text style={styles.itemTitle} numberOfLines={1}>{item.name}</Text><TouchableOpacityonPress={() => dispatch(removeItem(item.id))}style={styles.button}><Ionicons name="ios-trash" color="#fff" size={20} /></TouchableOpacity></View>)}/>) : (<Text style={{ fontSize: 30 }}>You list is empty :'(</Text>)}</View>);}const styles = StyleSheet.create({container: {flex: 1,backgroundColor: "blue",},fabContainer: {justifyContent: "flex-end",flexDirection: "row",position: "absolute",right: 10,bottom: 20,},fabButton: {backgroundColor: "blue",borderRadius: 35,width: 70,height: 70,alignItems: "center",justifyContent: "center",},listItemContainer: {flex: 1,flexDirection: "row",paddingTop: 10,paddingBottom: 5,paddingRight: 5,justifyContent: "space-between",width: "100%",borderBottomWidth: 0.25,},itemTitle: {fontSize: 22,fontWeight: "400",},button: {borderRadius: 8,backgroundColor: "#ff333390",padding: 5,},});export default ListScreen;


6. src/screens/ModalScreen.js

import React, { useState, useCallback } from "react";import {StyleSheet,Text,View,TouchableOpacity,TextInput,} from "react-native";import { Ionicons } from "@expo/vector-icons";import { useDispatch } from "react-redux";import { addItem } from "../redux/reducer";function ModalScreen({ navigation }) {const [value, setValue] = useState("");const dispatch = useDispatch();const onSaveNote = (value) => {dispatch(addItem(value));navigation.navigate("List");};return (<View style={styles.container}><View style={styles.innerContainer}><View style={styles.closeButtonContainer}><TouchableOpacitystyle={styles.closeButton}onPress={() => navigation.goBack()}><Ionicons name="ios-close" color="#101010" size={40} /></TouchableOpacity></View><View style={styles.modalContainer}><Text style={{ color: "#444", fontSize: 20 }}>What do you want to do?</Text><TextInputstyle={{height: 50,width: 200,padding: 5,borderColor: "gray",borderBottomWidth: 1,}}numberOfLines={1}onChangeText={(value) => setValue(value)}value={value}clearButtonMode="while-editing"/><TouchableOpacitystyle={{marginTop: 10,backgroundColor: "blue",width: 50,height: 50,alignItems: "center",justifyContent: "center",borderRadius: 5,}}onPress={() => onSaveNote(value)}><Ioniconsname="ios-arrow-dropright-circle"size={40}color="#fff"/></TouchableOpacity></View></View></View>);}const styles = StyleSheet.create({container: {flex: 1,},innerContainer: {borderTopLeftRadius: 10,borderTopRightRadius: 10,justifyContent: "flex-end",flexDirection: "row",height: "30%",width: "100%",position: "absolute",bottom: 0,right: 0,backgroundColor: "#fff",},closeButtonContainer: {position: "absolute",alignItems: "flex-end",right: 10,},closeButton: {backgroundColor: "#d3d3d3",borderRadius: 20,width: 40,height: 40,top: 10,alignItems: "center",justifyContent: "center",},modalContainer: {justifyContent: "center",alignItems: "center",position: "absolute",margin: 60,top: 10,left: 50,},});export default ModalScreen;


7. App.js

import React from "react";import { Provider as StateProvider } from "react-redux";import store from "./src/redux/store";import MainStackNavigator from "./src/navigation/AppNavigator";export default function App() {return (<StateProvider store={store}><MainStackNavigator /></StateProvider>);}

19. The final output of the demo application

Here is the final output of the demo app:

That’s all about in this article.


Conclusion

In this article, we understood how to use Redux with React Hooks by building a real React Native application. This article showed the demo code step by step to use Redux with React Hooks by building a real React Native application. The addition to hooks in react-redux such as useSelector and useDispatch reduces the need to write plentiful boilerplate code and also provides the advantage to use functional components. We also discussed better understanding of the basics of react navigation in real React native application.

Thanks for reading! I hope you enjoyed and learned about Redux and React Hooks practical usage in React Native application. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolmonkTechie as below link :

You can also follow official website and tutorials of React Native as below links :

React Native Official Website

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING!!???

iOS – Why Are Architecture Patterns Important In iOS Application ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (Why are Architecture Patterns important in iOS application) .

In this article, we will learn about Architecture Patterns importance in iOS application. Every iOS developer is familiar with issues related to product testing, code refactoring and support via ViewController. We also know the latter as a Massive View Controller. In search for solutions, we’ve delved into a profound investigation of programming patterns for iOS. At some point we realize even this is not enough and time comes for iOS architecture patterns. So we analyzed top-5 patterns, putting them to test in real-life projects. It is important to understand that architectural patterns are not the solution for all problems. They only describe approaches to design mobile applications. Details of realization depend and vary. So we can ask some questions from ourself like these :

  • Who supposed to own networking request: a Model or a Controller?
  • How do I pass a Model into a View Model of a new View?
  • Who creates a new VIPER module: Router or Presenter?

In this article, we cover the following iOS architecture patterns which is used in iOS Application development :

  • Classic MVC
  • Apple’s MVC
  • MVP
  • MVVM
  • VIPER

A famous quote about learning is :

” That is what learning is. You suddenly understand something you’ve understood all your life, but in a new way.”

So Let’s begin.


Why care about choosing the architecture?

Because if we don’t, one day, debugging a huge class with dozens of different things, we’ll find ourself being unable to find and fix any bugs in our class. Naturally, it is hard to keep this class in mind as complete entity, thus, we’ll always be missing some important details. If we are already in this situation with our application, it is likely that:

  • This class is the UIViewController subclass.
  • Our data stored directly in the UIViewController
  • Our UIViews do almost nothing
  • The Model is a dumb data structure
  • Our Unit Tests cover nothing

And this can happen, even despite the fact that we are following Apple’s guidelines and implementing Apple’s MVC pattern, so don’t feel bad. There is something wrong with the Apple’s MVC, but we’ll get back to it later.

Let’s define features of a good architecture:

  1. Balanced distribution of responsibilities among entities with strict roles. The easiest way to defeat complexity is to divide responsibility among multiple entities following the single responsibility principle.
  2. Testability usually comes from the first feature (and don’t worry: it is easy with appropriate architecture).
  3. Ease of use and a low maintenance cost. The least code we have to use, the fewer bugs we have to worry about.


Why is Distribution required?

Distribution keeps a fair load on our brain while we are trying to figure out how things work. If we think the more we develop, the better our brain will adapt to understanding complexity, then we are right. But this ability doesn’t scale linearly and reaches the cap quickly. So the easiest way to defeat complexity is to divide responsibilities among multiple entities following the Single Responsibility Principle.


Why is Testability required?

This is usually not a question for those who already felt gratitude to unit tests, which failed after adding new features or because of refactoring some intricacies of the class. This means the tests saved those developers from finding issues in runtime, which might happen when an app is on a user’s device and the fix takes a week to reach the user.


Why Ease of use?

This does not require an answer, but it is worth mentioning that the best code is the code that has never been written. Therefore, the less code we have, the fewer bugs we have. This means that desire to write less code should never be explained solely by laziness of a developer, and we should not favour a smarter solution closing our eyes to its maintenance cost.


Essential Aspects of Application Architecture

There are five essential aspects of Application Architecture as below :

  • Separation of concern
  • State sharing
  • State propagation
  • View controller communication
  • Parallelism


Aim of Application Architecture

The purpose of Application Architecture is :

  • Fix the massive-view-controller issue
  • Increase testability
  • Improve maintainability
  • Scale with team size


iOS Architecture Patterns

Nowadays we have many options when it comes to architecture design patterns:

  • Classic MVC
  • Apple’s MVC
  • MVP
  • MVVM
  • VIPER

First three of them assume putting the entities of the app into one of 3 categories:

  • Models — responsible for the domain data or a data access layer which manipulates the data, think of ‘Person’ or ‘PersonDataProvider’ classes.
  • Views — responsible for the presentation layer (GUI), for iOS environment think of everything starting with ‘UI’ prefix.
  • Controller/Presenter/ViewModel — the glue or the mediator between the Model and the View responsible for altering the Model by reacting to the user’s actions performed on the View and updating the View with changes from the Model.

Having entities divided allows us to:

  • Understand them better (as we already know)
  • Reuse them (mostly applicable to the View and the Model)
  • Test them independently

So Let’s start with one by one patterns


1. Classic MVC

Here, the View is stateless. It is simply rendered by the Controller once the Model is changed. Think of the web page completely reloaded once. We press on the link to navigate somewhere else. Although it is possible to implement the traditional MVC in iOS application, it doesn’t make much sense because of the architectural problem — it tightly couples all three entities, each entity knows about the other two. This dramatically reduces reusability of each of them — that is not what you want to have in your application. For this reason, we skip even trying to write a canonical MVC example.

Traditional MVC doesn’t seem to be applicable to modern iOS development.


2. Apple’s MVC


Expection

The Controller is a mediator between the View and the Model so that they don’t know about each other. The least reusable is the Controller and this is usually fine for us, since we must have a place for all that tricky business logic that doesn’t fit into the Model.

In theory, it looks very straightforward, but we feel that something is wrong, right? We even heard people abbreviating MVC as the Massive View Controller. View controller offloading became an important topic for the iOS developers. Why does this happen if Apple just took the traditional MVC and improved it a bit?


Reality

Cocoa MVC encourages us to write Massive View Controllers, because they are so involved in View’s life cycle that it’s hard to say they are separate. Although we still have ability to offload some business logic and data transformation to the Model, we have a little choice for offloading work to the View, at most of the times all the responsibility of the View is to send actions to the Controller. The view controller ends up being a delegate and a data source of everything, and is usually responsible for dispatching and cancelling the network requests and… we name it.

How many times have we seen code like this:

var userCell = tableView.dequeueReusableCellWithIdentifier("identifier") as UserCell
userCell.configureWithUser(user)

The cell which is the View configured directly with the Model, so MVC guidelines are violated, but this happens all the time, and usually people don’t feel it is wrong. If we strictly follow the MVC, then we supposed to configure the cell from the controller, and don’t pass the Model into the View, and this will increase the size of our Controller even more.

Cocoa MVC is reasonably unabbreviated as the Massive View Controller.

The problem might not be clear until it comes to the Unit Testing (hopefully, it does in our project). Since our view controller is tightly coupled with the view, it becomes difficult to test because we have to be very creative in mocking views and their life cycle, while writing the view controller’s code in such a way, that our business logic is separated as much as possible from the view layout code.

Let’s have a look on the simple playground example:

/**
 * MVC iOS Design Pattern
 *
 */

import UIKit
import PlaygroundSupport

struct Person { // Model
    let firstName: String
    let lastName: String
}

class GreetingViewController : UIViewController {   // View + Controller
    var person: Person!
    var showGreetingButton: UIButton!
    var greetingLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
        
        self.setupUIElements()
        self.layout()
    }
    
    func setupUIElements() {
        self.title = "Test"
        
        self._setupButton()
        self._setupLabel()
    }
    
    private func _setupButton() {
        self.showGreetingButton = UIButton()
        self.showGreetingButton.setTitle("Click me", for: .normal)
        self.showGreetingButton.setTitle("You badass", for: .highlighted)
        self.showGreetingButton.setTitleColor(UIColor.white, for: .normal)
        self.showGreetingButton.setTitleColor(UIColor.red, for: .highlighted)
        self.showGreetingButton.translatesAutoresizingMaskIntoConstraints = false
        self.showGreetingButton.addTarget(self, action: #selector(didTapButton(sender:)), for: .touchUpInside)
        self.view.addSubview(self.showGreetingButton)
    }
    
    private func _setupLabel() {
        self.greetingLabel = UILabel()
        self.greetingLabel.textColor = UIColor.white
        self.greetingLabel.textAlignment = .center
        self.greetingLabel.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(self.greetingLabel)
    }
    
    func layout() {
        self._layoutButton()
        self._layoutLabel()
        
        self.view.layoutIfNeeded()
    }
    
    private func _layoutButton() {
        // layout button at the center of the screen
        let cs1 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)
        let cs2 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1.0, constant: 1.0)
        
        self.view.addConstraints([cs1, cs2])
    }
    
    private func _layoutLabel() {
        // layout label at the center, bottom of the screen
        let cs1 = NSLayoutConstraint(item: self.greetingLabel, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)
        let cs2 = NSLayoutConstraint(item: self.greetingLabel, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: -10)
        let cs3 = NSLayoutConstraint(item: self.greetingLabel, attribute: .width, relatedBy: .equal, toItem: self.view, attribute: .width, multiplier: 0.70, constant: 0)
        
        self.view.addConstraints([cs1, cs2, cs3])
    }
    
    @objc func didTapButton(sender: UIButton) {
        self.greetingLabel.text = "Hello " + self.person.firstName + " " + self.person.lastName
    }
}

let model = Person(firstName: "Wasin", lastName: "Thonkaew")
let vc = GreetingViewController()
vc.person = model

PlaygroundPage.current.liveView = vc.view

MVC assembling can be performed in the presenting view controller.

This doesn’t seem very testable, right? We can move generation of greeting into the new GreetingModel class and test it separately, but we can’t test any presentation logic (although there is not much of such logic in the example above) inside the GreetingViewController without calling the UIView related methods directly (viewDidLoad, didTapButton) which might cause loading all views, and this is bad for the unit testing.

In fact, loading and testing UIViews on one simulator doesn’t guarantee that it would work fine on the other devices (e.g. iPad), so we’d recommend removing “Host Application” from our Unit Test target configuration and run our tests without our application running on simulator.

The interactions between the View and the Controller aren’t really testable with Unit Tests.

With all that said, it might seems that Cocoa MVC is a pretty bad pattern to choose. But let’s assess it in terms of features defined in the beginning of the article:

  • Distribution —the View and the Model in fact separated, but it tightly couples the View and the Controller.
  • Testability — because of the bad distribution, we’ll probably only test our Model.
  • Ease of use — the least amount of code among other patterns. In addition, everyone is familiar with it, thus, it’s easily maintained even by the unexperienced developers.

Cocoa MVC is the pattern of our choice if we are not ready to invest more time in our architecture, and we feel that something with higher maintenance cost is an overkill for our tiny pet project.

Cocoa MVC is the best architectural pattern in terms of the speed of the development.


3. MVP

MVP (Model View Presenter) is a further development of the MVC pattern. The Controller is replaced by the Presenter. Presenter, unlike Controller in the classic MVC:

  • holds the state of the View.
  • changes the state of the View.
  • handles events of the View.
  • transforms a Domain Model into a ViewModel.

Otherwise, Presenter is like the Controller from the classic MVC:

  • owns the Model.
  • changes the state of the Model (through calling appropriate methods) in response to external stimuli.
  • may contain Application Logic.


MVP – Cocoa MVC’s promises delivered (Passive View Variant)

This diagram doesn’t it look exactly like the Apple’s MVC? Yes, it does, and its name is MVP (Passive View variant). Here, the MVP’s mediator (Presenter) has nothing to do with the life cycle of the view controller, and the View can be mocked easily, so there is no layout code in the Presenter at all, but it updates the View with data and state. 

In terms of the MVP, the UIViewController subclasses are in fact the Views and not the Presenters. This distinction provides superb testability, which comes at a cost of the development speed, because we have to make manual data and event binding, as we can see from the example:

/**
 * MVP iOS Design Pattern
 *
 */

import UIKit
import PlaygroundSupport

struct Person { // Model
    let firstName: String
    let lastName: String
}

protocol GreetingView: class {
    func setGreeting(greeting: String)
}

protocol GreetingViewPresenter {
    init(view: GreetingView, person: Person)
    func showGreeting()
}

class GreetingPresenter : GreetingViewPresenter {
    weak var view: GreetingView?
    let person: Person
    
    required init(view: GreetingView, person: Person) {
        self.view = view
        self.person = person
    }
    
    func showGreeting() {
        let greeting = "Hello " + self.person.firstName + " " + self.person.lastName
        self.view?.setGreeting(greeting: greeting)
    }
}

class GreetingViewController : UIViewController, GreetingView {
    var presenter: GreetingViewPresenter!
    var showGreetingButton: UIButton!
    var greetingLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)
        
        self.setupUIElements()
        self.layout()
    }
    
    func setGreeting(greeting: String) {
        self.greetingLabel.text = greeting
    }
    
    func setupUIElements() {
        self.title = "Test"
        
        self._setupButton()
        self._setupLabel()
    }
    
    private func _setupButton() {
        self.showGreetingButton = UIButton()
        self.showGreetingButton.setTitle("Click me", for: .normal)
        self.showGreetingButton.setTitle("You badass", for: .highlighted)
        self.showGreetingButton.setTitleColor(UIColor.white, for: .normal)
        self.showGreetingButton.setTitleColor(UIColor.red, for: .highlighted)
        self.showGreetingButton.translatesAutoresizingMaskIntoConstraints = false
        self.showGreetingButton.addTarget(self, action: #selector(didTapButton(sender:)), for: .touchUpInside)
        self.view.addSubview(self.showGreetingButton)
    }
    
    private func _setupLabel() {
        self.greetingLabel = UILabel()
        self.greetingLabel.textColor = UIColor.white
        self.greetingLabel.textAlignment = .center
        self.greetingLabel.translatesAutoresizingMaskIntoConstraints = false
        
        self.view.addSubview(self.greetingLabel)
    }
    
    func layout() {
        self._layoutButton()
        self._layoutLabel()
        
        self.view.layoutIfNeeded()
    }
    
    private func _layoutButton() {
        // layout button at the center of the screen
        let cs1 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)
        let cs2 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1.0, constant: 1.0)
        
        self.view.addConstraints([cs1, cs2])
    }
    
    private func _layoutLabel() {
        // layout label at the center, bottom of the screen
        let cs1 = NSLayoutConstraint(item: self.greetingLabel, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)
        let cs2 = NSLayoutConstraint(item: self.greetingLabel, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: -10)
        let cs3 = NSLayoutConstraint(item: self.greetingLabel, attribute: .width, relatedBy: .equal, toItem: self.view, attribute: .width, multiplier: 0.70, constant: 0)
        
        self.view.addConstraints([cs1, cs2, cs3])
    }
    
    @objc func didTapButton(sender: UIButton) {
        self.presenter.showGreeting()
    }
}

// Assembling of MVP
// Note: Very important that these following lines will be within View when actually creating normal XCode project and follow design here.
let model = Person(firstName: "Wasin", lastName: "Thonkaew")
let view = GreetingViewController()
let presenter = GreetingPresenter(view: view, person: model)
view.presenter = presenter

PlaygroundPage.current.liveView = view.view

The MVP is the first pattern that reveals the assembly problem, which happens because of having three separate layers. Since we don’t want the View to know about the Model, it is not right to perform assembly in presenting view controller (which is the View), thus we have to do it somewhere else. For example, we can make the app-wide Router service, which will perform assembly and the View-to-View presentation. This issue arises and has to be addressed not only in the MVP but also in all the following patterns.

Let’s look on the features of the MVP:

  • Distribution — we have the most of the responsibilities divided between the Presenter and the Model, with the pretty dumb View (in the example above the Model is dumb as well).
  • Testability — is excellent, we can test most of the business logic because of the dumb View.
  • Easy of use — in our unrealistically simple example, the amount of code is doubled compared to the MVC, but, idea of the MVP is very clear.

MVP in iOS means superb testability and a lot of code.


MVP – With Bindings and Hooters (Supervising Presenter Variant)

There is the other flavour of the MVP — the Supervising Controller MVP. This variant includes direct binding of the View and the Model while the Presenter (The Supervising Controller) still handles actions from the View and is capable of changing the View.

But as we have already learned before, vague responsibility separation is bad, and tight coupling of the View and the Model. That is like how things work in Cocoa desktop development.

Same as with the traditional MVC, we don’t see a point in writing an example for the flawed architecture.


4. MVVM

Despite all the advantages of MVP, with the IDE development and frameworks, it didn’t fit in the automated application development, because it required “manual” work. The next pattern should solve these problems. MVVM (Model View ViewModel) was developed by engineers from Microsoft Ken Cooper and Ted Peters and announced by John Gossman in his blog in 2005.

The purpose of the pattern is separation between the user interface from development and business logic development, and facilitating the application testing using the main features of WPF and Silverlight platforms. Although the pattern of specialization was conceived for Microsoft technology, it can be used in Cocoa / CocoaTouch framework.

In theory, the Model-View-ViewModel looks very good. The View and the Model are already familiar to us, but also the Mediator, represented as the View Model.

It is pretty similar to the MVP:

  • The MVVM treats the view controller as the View
  • There is no tight coupling between the View and the Model

In addition, it does binding like the Supervising version of the MVP; however, this time not between the View and the Model, but between the View and the View Model.

So what is the View Model in the iOS reality? It is basically UIKit independent representation of our View and its state. The View Model invokes changes in the Model and updates itself with the updated Model, and since we have a binding between the View and the View Model, the first is updated accordingly.


Bindings

Bindings come out of a box for the OS X development, but we don’t have them in the iOS toolbox. Of course we have the KVO and notifications, but they aren’t as convenient as bindings.

So, provided we don’t want to write them ourselves, we have two options:

  • One of the KVO based binding libraries like the RZDataBinding or the SwiftBond
  • The full scale functional reactive programming beasts like ReactiveCocoaRxSwift or PromiseKit.

In fact, nowadays, if we hear “MVVM” — we think ReactiveCocoa, and vice versa. Although it is possible to build the MVVM with the simple bindings, ReactiveCocoa (or siblings) will allow us to get most of the MVVM.

There is one bitter truth about reactive frameworks: the great power comes with the great responsibility. It’s really easy to mess up things when we go reactive. In other words, if we do something wrong, we might spend a lot of time debugging the app, so just take a look at this call stack.

In our simple example, the FRF framework or even the KVO is an overkill, instead we’ll explicitly ask the View Model to update using showGreeting method and use the simple property for greetingDidChange callback function.

/*** MVVM iOS Design Pattern**/ import UIKitimport PlaygroundSupportstruct Person { let firstName: String let lastName: String} protocol GreetingViewModelProtocol: class {var greeting: String? { get }var greetingDidChange: ((GreetingViewModelProtocol) -> ())? { get set }init(person: Person)func showGreeting()}class GreetingViewModel : GreetingViewModelProtocol {let person: Personvar greeting: String? {didSet {self.greetingDidChange?(self)}}var greetingDidChange: ((GreetingViewModelProtocol) -> ())?required init(person: Person) {self.person = person}func showGreeting() {self.greeting = "Hello " + self.person.firstName + " " + self.person.lastName}}class GreetingViewController : UIViewController {var viewModel: GreetingViewModelProtocol? {didSet {self.viewModel?.greetingDidChange = { [unowned self] viewModel inself.greetingLabel.text = viewModel.greeting}}}var showGreetingButton: UIButton!var greetingLabel: UILabel!override func viewDidLoad() {super.viewDidLoad()self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)self.setupUIElements()self.layout()}func setupUIElements() {self.title = "Test"self._setupButton()self._setupLabel()}private func _setupButton() {self.showGreetingButton = UIButton()self.showGreetingButton.setTitle("Click me", for: .normal)self.showGreetingButton.setTitle("You badass", for: .highlighted)self.showGreetingButton.setTitleColor(UIColor.white, for: .normal)self.showGreetingButton.setTitleColor(UIColor.red, for: .highlighted)self.showGreetingButton.translatesAutoresizingMaskIntoConstraints = falseself.showGreetingButton.addTarget(self, action: #selector(didTapButton(sender:)), for: .touchUpInside)self.view.addSubview(self.showGreetingButton)}private func _setupLabel() {self.greetingLabel = UILabel()self.greetingLabel.textColor = UIColor.whiteself.greetingLabel.textAlignment = .centerself.greetingLabel.translatesAutoresizingMaskIntoConstraints = falseself.view.addSubview(self.greetingLabel)}func layout() {self._layoutButton()self._layoutLabel()self.view.layoutIfNeeded()}private func _layoutButton() { let cs1 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)let cs2 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1.0, constant: 1.0)self.view.addConstraints([cs1, cs2])}private func _layoutLabel() { let cs1 = NSLayoutConstraint(item: self.greetingLabel, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)let cs2 = NSLayoutConstraint(item: self.greetingLabel, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: -10)let cs3 = NSLayoutConstraint(item: self.greetingLabel, attribute: .width, relatedBy: .equal, toItem: self.view, attribute: .width, multiplier: 0.70, constant: 0)self.view.addConstraints([cs1, cs2, cs3])}@objc func didTapButton(sender: UIButton) {guard let vm = self.viewModel else { return }vm.showGreeting()}} let model = Person(firstName: "Wasin", lastName: "Thonkaew")let view = GreetingViewController()let viewModel = GreetingViewModel(person: model)view.viewModel = viewModelPlaygroundPage.current.liveView = view.view 

And again back to our feature assessment:

  • Distribution — it is not clear in our tiny example, but, in fact, the MVVM’s View has more responsibilities than the MVP’s View. Because the first one updates its state from the View Model by setting up bindings, when the second one just forwards all events to the Presenter and doesn’t update itself.
  • Testability — the View Model knows nothing about the View, this allows us to test it easily. The View might be also tested, but since it is UIKit dependant we might want to skip it.
  • Easy of use — its has the same amount of code as the MVP in our example, but in the real app where we’d have to forward all events from the View to the Presenter and to update the View manually, MVVM would be much skinnier if we used bindings.

The MVVM is very attractive, since it combines benefits of the aforementioned approaches, and, in addition, it doesn’t require extra code for the View updates because of the bindings on the View side. Nevertheless, testability is still on a good level.


5. VIPER

The examined above architectural patterns have one disadvantage. If we try to divide the architecture into layers, it is likely we will have difficulty with the Presenter or with the View Model. Which layer do they belong to? This question has no simple answer: we can introduce a separate Presentation layer for the Presenter, or it can belong to the Application Logic. The same with MVVM. This ambiguity creates another problem.

It is very difficult to separate the Application Logic from the Domain Model Logic. So often there’s no separation and are in the same layer. Besides, the presence of an Application Logic in a Presenter sometimes makes it difficult to test different Use Cases. Another problem in previous architectures is assembly and navigation. In large projects for several dozens of scenes, this is a responsibility of a separate module Router.

In 2012  a remarkable article was published. The Clean Architecture and several speeches on the subject. Later, in the Mutual Mobile, we’ve adapted a little for iOS, and a new pattern VIPER enters. It is the acronym of View, Interactor, Presenter, Entity, Router–basic components that make up the application. See how they interact below. This gives the LEGO building experience transferred into the iOS app design.

By now, we agree that the granularity in responsibilities is very good. VIPER makes another iteration on the idea of separating responsibilities, and this time we have five layers.

  • View — As with MVP (Passive View), it is a visualization of the data that comes from the Presenter. The View communicates with the Presenter through a protocol at a higher level than the level of UI classes. The Presenter is not aware of specific classes that make up a hierarchy of the View. To share data between the View and the Presenter, it is convenient to use separate structures (i.e. classes that have no methods that could change its state). Only the View and the Presenter know about these classes.
  • Interactor — This layer contains business logic related to the data (Entities) or networking, like creating new instances of entities or fetching them from the server. For those purposes we’ll use some Services and Managers which are not considered as a part of VIPER module but an external dependency.
  • Presenter — This layer contains the UI related (but UIKit independent) business logic, invokes methods on the Interactor.
  • Entities — Our plain data objects, not the data access layer, because that is a responsibility of the Interactor.
  • Router — This layer is responsible for the segues between the VIPER modules. Wireframe and Presenter have the responsibilities for the navigation in VIPER.

Basically, VIPER module can be a one screen or the whole user story of our application — think of authentication, which can be one screen or several related ones. How small are our “LEGO” blocks supposed to be? — It’s up to us.

If we compare it with the other patterns, we’ll see a few differences of the distribution of responsibilities:

  • Model (data interaction) logic shifted into the Interactor with the Entities as dumb data structures.
  • Only the UI representation duties of the Controller/Presenter/ViewModel moved into the Presenter, but not the data altering capabilities.
  • VIPER is the first pattern which explicitly addresses navigation responsibility, which is supposed to be resolved by the Router.

Proper way of doing routing is a challenge for the iOS applications, the other patterns (MVC, MVP, MVVM) simply don’t address this issue.

The example doesn’t cover routing or interaction between modules, as those topics are not covered by the other patterns at all.

/*** VIPER iOS Design Pattern**/import UIKitimport PlaygroundSupportstruct Person { let firstName: Stringlet lastName: String}struct GreetingData {  let greeting: Stringlet subject: String}protocol GreetingProvider {func provideGreetingData()}protocol GreetingOutput: class {func receiveGreetingData(greetingData: GreetingData)}class GreetingInteractor: GreetingProvider {weak var output: GreetingOutput!func provideGreetingData() {let person = Person(firstName: "Wasin", lastName: "Thonkaew")let subject = person.firstName + " " + person.lastNamelet greeting = GreetingData(greeting: "Hello", subject: subject)self.output.receiveGreetingData(greetingData: greeting)}}protocol GreetingViewEventHandler {func didTapShowGreetingButton()}protocol GreetingView: class {func setGreeting(greeting: String)}class GreetingPresenter: GreetingOutput, GreetingViewEventHandler {weak var view: GreetingView!var greetingProvider: GreetingProvider!func didTapShowGreetingButton() {self.greetingProvider.provideGreetingData()}func receiveGreetingData(greetingData: GreetingData) {let greeting = greetingData.greeting + " " + greetingData.subjectself.view.setGreeting(greeting: greeting)}}class GreetingViewController : UIViewController, GreetingView {var eventHandler: GreetingViewEventHandler!var showGreetingButton: UIButton!var greetingLabel: UILabel!override func viewDidLoad() {super.viewDidLoad()self.view.frame = CGRect(x: 0, y: 0, width: 320, height: 480)self.setupUIElements()self.layout()}func setGreeting(greeting: String) {self.greetingLabel.text = greeting}func setupUIElements() {self.title = "Test"self._setupButton()self._setupLabel()}private func _setupButton() {self.showGreetingButton = UIButton()self.showGreetingButton.setTitle("Click me", for: .normal)self.showGreetingButton.setTitle("You badass", for: .highlighted)self.showGreetingButton.setTitleColor(UIColor.white, for: .normal)self.showGreetingButton.setTitleColor(UIColor.red, for: .highlighted)self.showGreetingButton.translatesAutoresizingMaskIntoConstraints = falseself.showGreetingButton.addTarget(self, action: #selector(didTapButton(sender:)), for: .touchUpInside)self.view.addSubview(self.showGreetingButton)}private func _setupLabel() {self.greetingLabel = UILabel()self.greetingLabel.textColor = UIColor.whiteself.greetingLabel.textAlignment = .centerself.greetingLabel.translatesAutoresizingMaskIntoConstraints = falseself.view.addSubview(self.greetingLabel)}func layout() {self._layoutButton()self._layoutLabel()self.view.layoutIfNeeded()}private func _layoutButton() { let cs1 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)let cs2 = NSLayoutConstraint(item: self.showGreetingButton, attribute: .centerY, relatedBy: .equal, toItem: self.view, attribute: .centerY, multiplier: 1.0, constant: 1.0)self.view.addConstraints([cs1, cs2])}private func _layoutLabel() { let cs1 = NSLayoutConstraint(item: self.greetingLabel, attribute: .centerX, relatedBy: .equal, toItem: self.view, attribute: .centerX, multiplier: 1.0, constant: 1.0)let cs2 = NSLayoutConstraint(item: self.greetingLabel, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: -10)let cs3 = NSLayoutConstraint(item: self.greetingLabel, attribute: .width, relatedBy: .equal, toItem: self.view, attribute: .width, multiplier: 0.70, constant: 0)self.view.addConstraints([cs1, cs2, cs3])}@objc func didTapButton(sender: UIButton) {self.eventHandler.didTapShowGreetingButton()}}let view = GreetingViewController()let presenter = GreetingPresenter()let interactor = GreetingInteractor()view.eventHandler = presenterpresenter.view = viewpresenter.greetingProvider = interactorinteractor.output = presenterPlaygroundPage.current.liveView = view.view

Yet again, we back to the features:

  • Distribution — undoubtedly, VIPER is a champion in distribution of responsibilities.
  • Testability — no surprises here, better distribution — better testability.
  • Easy of use — finally, two above come in cost of maintainability as we already guessed. We have to write a huge amount of interface for classes with very small responsibilities.


So what about LEGO?

While using VIPER, we might feel like building The Empire State Building from LEGO blocks, and that is a signal that we have a problem. Maybe, it’s too early to adopt VIPER for our application and we should consider something simpler. Some people ignore this and continue shooting out of cannon into sparrows. We assume they believe that their apps will benefit from VIPER at least in the future, even if now the maintenance cost is unreasonably high. If we believe the same, then we’d recommend trying Generamba — a tool for generating VIPER skeletons.

That’s all about in this article.


Conclusion

In this article, we understood Why Are Architecture Patterns important in iOS. This article reviewed some popular ones and compare them in theory and practice going over a few tiny examples.

Thanks for reading! I hope you enjoyed and learned about Architecture patterns importance in iOS Application. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe us on this blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolmonkTechie as below link :

You can also follow other website and tutorials of iOS as below links :

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING!!???

Android – Understanding Kotlin Style Guide

Hello Readers, CoolMonkTechie heartily welcomes you in this article (Understanding Kotlin Style Guide).

In this article, We will learn about the Kotlin Style Guide. This article serves as the complete definition of Google’s Android coding standards for source code in the Kotlin Programming Language. A Kotlin source file is described as being in Google Android Style if and only if it adheres to the rules herein.

Like other programming style guides, the issues covered span not only aesthetic issues of formatting, but other types of conventions or coding standards as well. However, this article focuses primarily on the hard-and-fast rules that we follow universally, and avoids giving advice that isn’t clearly enforceable (whether by human or tool).

A famous quote about learning is :

” We now accept the fact that learning is a lifelong process of keeping abreast of change. And the most pressing task is to teach people how to learn. “

So Let’s begin.


1. Source Files

All source files must be encoded as UTF-8.


1.1. Naming

If a source file contains only a single top-level class, the file name should reflect the case-sensitive name plus the .kt extension. Otherwise, if a source file contains multiple top-level declarations, choose a name that describes the contents of the file, apply PascalCase, and append the .kt extension.

// MyClass.kt
class MyClass { }
// Bar.kt
class Bar { }
fun Runnable.toBar(): Bar = // …
// Map.kt
fun <T, O> Set<T>.map(func: (T) -> O): List<O> = // …
fun <T, O> List<T>.map(func: (T) -> O): List<O> = // …


1.2. Special Characters


1.2.1. Whitespace Characters

Aside from the line terminator sequence, the ASCII horizontal space character (0x20) is the only whitespace character that appears anywhere in a source file. This implies that:

  • All other whitespace characters in string and character literals are escaped.
  • Tab characters are not used for indentation.


1.2.2. Special Escape Sequences

For any character that has a special escape sequence (\b\n\r\t\'\"\\, and \$), that sequence is used rather than the corresponding Unicode (e.g., \u000a) escape.


1.2.3. Non-ASCII Characters

For the remaining non-ASCII characters, either the actual Unicode character (e.g., ) or the equivalent Unicode escape (e.g., \u221e) is used. The choice depends only on which makes the code easier to read and understand. Unicode escapes are discouraged for printable characters at any location and are strongly discouraged outside of string literals and comments.

ExampleDiscussion
val unitAbbrev = "μs"Best: perfectly clear even without a comment.
val unitAbbrev = "\u03bcs" // μsPoor: there’s no reason to use an escape with a printable character.
val unitAbbrev = “\u03bcs”`Poor: the reader has no idea what this is.
return "\ufeff" + contentGood: use escapes for non-printable characters, and comment if necessary.


1.3. Structure

.kt file comprises the following, in order:

  • Copyright and/or license header (optional)
  • File-level annotations
  • Package statement
  • Import statements
  • Top-level declarations

Exactly one blank line separates each of these sections.


1.3.1. Copyright / License

If a copyright or license header belongs in the file it should be placed at the immediate top in a multi-line comment.

/*
 * Copyright 2017 Google, Inc.
 *
 * ...
 */
 

Do not use a KDoc-style or single-line-style comment.

/**
 * Copyright 2017 Google, Inc.
 *
 * ...
 */
// Copyright 2017 Google, Inc.
//
// ...


1.3.2. File-level Annotations

Annotations with the “file” use-site target are placed between any header comment and the package declaration.


1.3.3. Package Statement

The package statement is not subject to any column limit and is never line-wrapped.


1.3.4. Import Statements

Import statements for classes, functions, and properties are grouped together in a single list and ASCII sorted.

Wildcard imports (of any type) are not allowed.

Similar to the package statement, import statements are not subject to a column limit and they are never line-wrapped.


1.3.5. Top-level Declarations

.kt file can declare one or more types, functions, properties, or type aliases at the top-level.

The contents of a file should be focused on a single theme. Examples of this would be a single public type or a set of extension functions performing the same operation on multiple receiver types. Unrelated declarations should be separated into their own files and public declarations within a single file should be minimized.

No explicit restriction is placed on the number nor order of the contents of a file.

Source files are usually read from top-to-bottom meaning that the order, in general, should reflect that the declarations higher up will inform understanding of those farther down. Different files may choose to order their contents differently. Similarly, one file may contain 100 properties, another 10 functions, and yet another a single class.

What is important is that each class uses some logical order, which its maintainer could explain if asked. For example, new functions are not just habitually added to the end of the class, as that would yield “chronological by date added” ordering, which is not a logical ordering.


1.3.6. Class Member Ordering

The order of members within a class follow the same rules as the top-level declarations.


2. Formatting


2.1. Braces

Braces are not required for when branches and if statement bodies which have no else if/else branches and which fit on a single line.

if (string.isEmpty()) return

when (value) {
    0 -> return
    // …
}

Braces are otherwise required for any ifforwhen branch, do, and while statements, even when the body is empty or contains only a single statement.

if (string.isEmpty())
    return  // WRONG!

if (string.isEmpty()) {
    return  // Okay
}


2.1.1. Non-empty Blocks

The braces follow the Kernighan and Ritchie style (“Egyptian brackets”) for nonempty blocks and block-like constructs:

  • Firstly, No line break before the opening brace.
  • Secondly, Line break after the opening brace.
  • Thirdly, Line break before the closing brace.
  • And finally, Line break after the closing brace, only if that brace terminates a statement or terminates the body of a function, constructor, or named class. For example, there is no line break after the brace if it is followed by else or a comma.

return Runnable {
    while (condition()) {
        foo()
    }
}

return object : MyClass() {
    override fun foo() {
        if (condition()) {
            try {
                something()
            } catch (e: ProblemException) {
                recover()
            }
        } else if (otherCondition()) {
            somethingElse()
        } else {
            lastThing()
        }
    }
}


2.1.2. Empty Blocks

An empty block or block-like construct must be in K&R style.

try {
    doSomething()
} catch (e: Exception) {} // WRONG!
try {
    doSomething()
} catch (e: Exception) {
} // Okay


2.1.3. Expressions

An if/else conditional that is used as an expression may omit braces only if the entire expression fits on one line.

val value = if (string.isEmpty()) 0 else 1  // Okay
val value = if (string.isEmpty())  // WRONG!
    0
else
    1
val value = if (string.isEmpty()) { // Okay
    0
} else {
    1
}


2.1.4. Indentation

Each time a new block or block-like construct is opened, the indent increases by four spaces. When the block ends, the indent returns to the previous indent level. The indent level applies to both code and comments throughout the block.


2.1.5. One Statement Per Line

Each statement is followed by a line break. Semicolons are not used.


2.1.6. Line Wrapping

Code has a column limit of 100 characters. Except as noted below, any line that would exceed this limit must be line-wrapped, as explained below.

Exceptions:

  • Lines where obeying the column limit is not possible (for example, a long URL in KDoc)
  • package and import statements
  • Command lines in a comment that may be cut-and-pasted into a shell


2.1.7. Where to break

The prime directive of line-wrapping is: prefer to break at a higher syntactic level. Also:

  • When a line is broken at an operator or infix function name, the break comes after the operator or infix function name.
  • When a line is broken at the following “operator-like” symbols, the break comes before the symbol:
    • The dot separator (.?.).
    • The two colons of a member reference (::).
  • A method or constructor name stays attached to the open parenthesis (() that follows it.
  • A comma (,) stays attached to the token that precedes it.
  • A lambda arrow (->) stays attached to the argument list that precedes it.


2.1.8. Functions

When a function signature does not fit on a single line, break each parameter declaration onto its own line. Parameters defined in this format should use a single indent (+4). The closing parenthesis ()) and return type are placed on their own line with no additional indent.

fun <T> Iterable<T>.joinToString(
    separator: CharSequence = ", ",
    prefix: CharSequence = "",
    postfix: CharSequence = ""
): String {
    // …
}


2.1.9. Expression Functions

When a function contains only a single expression it can be represented as an expression function.

override fun toString(): String {
    return "Hey"
}
override fun toString(): String = "Hey"

The only time an expression function should wrap to multiple lines is when it opens a block.

fun main() = runBlocking {
  // …
}

Otherwise, if an expression function grows to require wrapping, use a normal function body, a return declaration, and normal expression wrapping rules instead.


2.1.10. Properties

When a property initializer does not fit on a single line, break after the equals sign (=) and use an indent.

private val defaultCharset: Charset? =
        EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)

Properties declaring a get and/or set function should place each on their own line with a normal indent (+4). Format them using the same rules as functions.

var directory: File? = null
    set(value) {
        // …
    }

Read-only properties can use a shorter syntax which fits on a single line.

val defaultExtension: String get() = "kt"


2.2. Whitespace


2.2.1. Vertical

A single blank line appears:

  • Between consecutive members of a class: properties, constructors, functions, nested classes, etc.
    • Exception: A blank line between two consecutive properties (having no other code between them) is optional. Such blank lines are used as needed to create logical groupings of properties and associate properties with their backing property, if present.
    • Exception: Blank lines between enum constants are covered below.
  • Between statements, as needed to organize the code into logical subsections.
  • Optionally before the first statement in a function, before the first member of a class, or after the last member of a class (neither encouraged nor discouraged).

Multiple consecutive blank lines are permitted, but not encouraged or ever required.


2.2.2. Horizontal

Beyond where required by the language or other style rules, and apart from literals, comments, and KDoc, a single ASCII space also appears in the following places only:

  • Separating any reserved word, such as iffor, or catch from an open parenthesis (() that follows it on that line.
// WRONG!
for(i in 0..1) {
}
// Okay
for (i in 0..1) {
}
  • Separating any reserved word, such as else or catch, from a closing curly brace (}) that precedes it on that line.
// WRONG!
}else {
}
// Okay
} else {
}
  • Before any open curly brace ({).
// WRONG!
if (list.isEmpty()){
}
// Okay
if (list.isEmpty()) {
}
  • Before a colon (:) only if used in a class declaration for specifying a base class or interfaces, or when used in a where clause for generic constraints.
// WRONG!
class Foo: Runnable
// Okay
class Foo : Runnable
// WRONG
fun <T: Comparable> max(a: T, b: T)
// Okay
fun <T : Comparable> max(a: T, b: T)
// WRONG
fun <T> max(a: T, b: T) where T: Comparable<T>
// Okay
fun <T> max(a: T, b: T) where T : Comparable<T>
  • After a comma (,) or colon (:).
// WRONG!
val oneAndTwo = listOf(1,2)
// Okay
val oneAndTwo = listOf(1, 2)
// WRONG!
class Foo :Runnable
// Okay
class Foo : Runnable
  • On both sides of the double slash (//) that begins an end-of-line comment. Here, multiple spaces are allowed, but not required.
// WRONG!
var debugging = false//disabled by default
// Okay
var debugging = false // disabled by default
  • On both sides of any binary operator.
// WRONG!
val two = 1+1
// Okay
val two = 1 + 1

This also applies to the following “operator-like” symbols:

  • the arrow in a lambda expression (->).
// WRONG!
ints.map { value->value.toString() }
// Okay
ints.map { value -> value.toString() }

But not:

  • the two colons (::) of a member reference.
// WRONG!
val toString = Any :: toString
// Okay
val toString = Any::toString
  • the dot separator (.).
// WRONG
it . toString()
// Okay
it.toString()
  • the range operator (..).
// WRONG
 for (i in 1 .. 4) print(i)
 
 // Okay
 for (i in 1..4) print(i)

This rule is never interpreted as requiring or forbidding additional space at the start or end of a line; it addresses only interior space.


2.3. Specific Constructs


2.3.1. Enum Classes

An enum with no functions and no documentation on its constants may optionally be formatted as a single line.

enum class Answer { YES, NO, MAYBE }

When the constants in an enum are placed on separate lines, a blank line is not required between them except in the case where they define a body.

enum class Answer {
    YES,
    NO,

    MAYBE {
        override fun toString() = """¯\_(ツ)_/¯"""
    }
}

Since enum classes are classes, all other rules for formatting classes apply.


2.3.2. Annotations

Member or type annotations are placed on separate lines immediately prior to the annotated construct.

@Retention(SOURCE)
@Target(FUNCTION, PROPERTY_SETTER, FIELD)
annotation class Global

Annotations without arguments can be placed on a single line.

@JvmField @Volatile
var disposable: Disposable? = null

When only a single annotation without arguments is present, it may be placed on the same line as the declaration.

@Volatile var disposable: Disposable? = null

@Test fun selectAll() {
    // …
}

@[...] syntax may only be used with an explicit use-site target, and only for combining 2 or more annotations without arguments on a single line.

@field:[JvmStatic Volatile]
var disposable: Disposable? = null


2.3.3. Implicit Return/Property Types

If an expression function body or a property initializer is a scalar value or the return type can be clearly inferred from the body then it can be omitted.

override fun toString(): String = "Hey"
// becomes
override fun toString() = "Hey"
private val ICON: Icon = IconLoader.getIcon("/icons/kotlin.png")
// becomes
private val ICON = IconLoader.getIcon("/icons/kotlin.png")

When writing a library, retain the explicit type declaration when it is part of the public API.


2.4. Naming

Identifiers use only ASCII letters and digits, and, in a small number of cases noted below, underscores. Thus each valid identifier name is matched by the regular expression \w+.

Special prefixes or suffixes, like those seen in the examples name_mNames_name, and kName, are not used except in the case of backing properties.


2.4.1. Package Names

Package names are all lowercase, with consecutive words simply concatenated together (no underscores).

// Okay
package com.example.deepspace
// WRONG!
package com.example.deepSpace
// WRONG!
package com.example.deep_space


2.4.2. Type Names

Class names are written in PascalCase and are typically nouns or noun phrases. For example, Character or ImmutableList. Interface names may also be nouns or noun phrases (for example, List), but may sometimes be adjectives or adjective phrases instead (for example Readable).

Test classes are named starting with the name of the class they are testing, and ending with Test. For example, HashTest or HashIntegrationTest.


2.4.3. Function Names

Function names are written in camelCase and are typically verbs or verb phrases. For example, sendMessage or stop.

Underscores are permitted to appear in test function names to separate logical components of the name.

@Test fun pop_emptyStack() {
    // …
}

Functions annotated with @Composable that return Unit are PascalCased and named as nouns, as if they were types.

@Composable
fun NameTag(name: String) {
    // …
}


2.4.4. Constant Names

Constant names use UPPER_SNAKE_CASE: all uppercase letters, with words separated by underscores. But what is a constant, exactly?

Constants are val properties with no custom get function, whose contents are deeply immutable, and whose functions have no detectable side-effects. This includes immutable types and immutable collections of immutable types as well as scalars and string if marked as const. If any of an instance’s observable state can change, it is not a constant. Merely intending to never mutate the object is not enough.

const val NUMBER = 5
val NAMES = listOf("Alice", "Bob")
val AGES = mapOf("Alice" to 35, "Bob" to 32)
val COMMA_JOINER = Joiner.on(',') // Joiner is immutable
val EMPTY_ARRAY = arrayOf()

These names are typically nouns or noun phrases.

Constant values can only be defined inside of an object or as a top-level declaration. Values otherwise meeting the requirement of a constant but defined inside of a class must use a non-constant name.

Constants which are scalar values must use the const modifier.


2.4.5. Non-constant Names

Non-constant names are written in camelCase. These apply to instance properties, local properties, and parameter names.

val variable = "var"
val nonConstScalar = "non-const"
val mutableCollection: MutableSet = HashSet()
val mutableElements = listOf(mutableInstance)
val mutableValues = mapOf("Alice" to mutableInstance, "Bob" to mutableInstance2)
val logger = Logger.getLogger(MyClass::class.java.name)
val nonEmptyArray = arrayOf("these", "can", "change")

These names are typically nouns or noun phrases.


2.4.6. Backing Properties

When a backing property is needed, its name should exactly match that of the real property except prefixed with an underscore.

private var _table: Map? = null

val table: Map
    get() {
        if (_table == null) {
            _table = HashMap()
        }
        return _table ?: throw AssertionError()
    }


2.4.7. Type Variable Names

Each type variable is named in one of two styles:

  • A single capital letter, optionally followed by a single numeral (such as ETXT2).
  • A name in the form used for classes, followed by the capital letter T (such as RequestTFooBarT).


2.4.8. Camel Case

Sometimes there is more than one reasonable way to convert an English phrase into camel case, such as when acronyms or unusual constructs like “IPv6” or “iOS” are present. To improve predictability, use the following scheme.

Beginning with the prose form of the name:

  1. Convert the phrase to plain ASCII and remove any apostrophes. For example, “Müller’s algorithm” might become “Muellers algorithm”.
  2. Divide this result into words, splitting on spaces and any remaining punctuation (typically hyphens). Recommended: if any word already has a conventional camel-case appearance in common usage, split this into its constituent parts (e.g., “AdWords” becomes “ad words”). Note that a word such as “iOS” is not really in camel case per se; it defies any convention, so this recommendation does not apply.
  3. Now lowercase everything (including acronyms), then do one of the following:
    • Uppercase the first character of each word to yield pascal case.
    • Uppercase the first character of each word except the first to yield camel case.
  4. Finally, join all the words into a single identifier.

We note that the casing of the original words is almost entirely disregarded.

Prose formCorrectIncorrect
“XML Http Request”XmlHttpRequestXMLHTTPRequest
“new customer ID”newCustomerIdnewCustomerID
“inner stopwatch”innerStopwatchinnerStopWatch
“supports IPv6 on iOS”supportsIpv6OnIossupportsIPv6OnIOS
“YouTube importer”YouTubeImporterYoutubeImporter*

(* Acceptable, but not recommended.)


2.5. Documentation


2.5.1. Formatting

The basic formatting of KDoc blocks is seen in this example:

/**
 * Multiple lines of KDoc text are written here,
 * wrapped normally…
 */
fun method(arg: String) {
    // …
}

…or in this single-line example:

/** An especially short bit of KDoc. */

The basic form is always acceptable. The single-line form may be substituted when the entirety of the KDoc block (including comment markers) can fit on a single line. Note that this only applies when there are no block tags such as @return.


2.5.2. Paragraphs

One blank line—that is, a line containing only the aligned leading asterisk (*)—appears between paragraphs, and before the group of block tags if present.


2.5.3. Block Tags

Any of the standard “block tags” that are used appear in the order @constructor@receiver@param@property@return@throws@see, and these never appear with an empty description. When a block tag doesn’t fit on a single line, continuation lines are indented 4 spaces from the position of the @.


2.5.4. Summary Fragment

Each KDoc block begins with a brief summary fragment. This fragment is very important: it is the only part of the text that appears in certain contexts such as class and method indexes.

This is a fragment–a noun phrase or verb phrase, not a complete sentence. It does not begin with “A `Foo` is a...“, or “This method returns...“, nor does it have to form a complete imperative sentence like “Save the record.“. However, the fragment is capitalized and punctuated as if it were a complete sentence.


2.5.5. Usage

At the minimum, KDoc is present for every public type, and every public or protected member of such a type, with a few exceptions noted below.


2.5.5.1. Exception: Self-explanatory Functions

KDoc is optional for “simple, obvious” functions like getFoo and properties like foo, in cases where there really and truly is nothing else worthwhile to say but “Returns the foo”.

It is not appropriate to cite this exception to justify omitting relevant information that a typical reader might need to know. For example, for a function named getCanonicalName or property named canonicalName, don’t omit its documentation (with the rationale that it would say only /** Returns the canonical name. */) if a typical reader may have no idea what the term “canonical name” means!

2.5.5.2. Exception: Overrides

KDoc is not always present on a method that overrides a supertype method.

That’s all about in this article.


Conclusion

In this article, We understood about Kotlin Style Guide for Android application development. This article served as the complete definition of Google’s Android coding standards for source code in the Kotlin Programming Language. We discussed about Source code and formatting style guideline standard for Kotlin which is used in android application development.

Thanks for reading ! I hope you enjoyed and learned about Kotlin Style Guide concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolMonkTechie as below link :

You can also follow official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Android – How To Apply Common Kotlin Patterns In Android Application ?

Hello Readers, CoolMonkTechie heartily welcomes you in this article (How To Apply Common Kotlin Patterns In Android Application ?)

In this article, We will learn how to apply common Kotlin patterns in Android apps. This article will focus on some of the most useful aspects of the Kotlin language when developing for Android.

A famous quote about learning is :

” Anyone who stops learning is old, whether at twenty or eighty. Anyone who keeps learning stays young. The greatest thing in life is to keep your mind young. “

So Let’s begin.


Work with fragments

In this sections, we use Fragment examples to highlight some of Kotlin’s best features as below:


Inheritance

We can declare a class in Kotlin with the class keyword. In the following example, LoginFragment is a subclass of Fragment. We can indicate inheritance by using the : operator between the subclass and its parent:

class LoginFragment : Fragment()

In this class declaration, LoginFragment is responsible for calling the constructor of its superclass, Fragment.

Within LoginFragment, we can override a number of lifecycle callbacks to respond to state changes in our Fragment. To override a function, use the override keyword, as shown in the following example:

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
): View? {
    return inflater.inflate(R.layout.login_fragment, container, false)
}

To reference a function in the parent class, use the super keyword, as shown in the following example:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
}


Nullability and Initialization

In the previous examples, some of the parameters in the overridden methods have types suffixed with a question mark ?. This indicates that the arguments passed for these parameters can be null. Be sure to handle their nullability safely.

In Kotlin, we must initialize an object’s properties when declaring the object. This implies that when we obtain an instance of a class, we can immediately reference any of its accessible properties. The View objects in a Fragment, however, aren’t ready to be inflated until calling Fragment#onCreateView, so we need a way to defer property initialization for a View.

The lateinit lets us defer property initialization. When using lateinit, we should initialize our property as soon as possible.

The following example demonstrates using lateinit to assign View objects in onViewCreated:

class LoginFragment : Fragment() {

    private lateinit var usernameEditText: EditText
    private lateinit var passwordEditText: EditText
    private lateinit var loginButton: Button
    private lateinit var statusTextView: TextView

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        usernameEditText = view.findViewById(R.id.username_edit_text)
        passwordEditText = view.findViewById(R.id.password_edit_text)
        loginButton = view.findViewById(R.id.login_button)
        statusTextView = view.findViewById(R.id.status_text_view)
    }

    ...
}

We aware that if we access a property before it is initialized, Kotlin throws an UninitializedPropertyAccessException.


SAM Conversion

We can listen for click events in Android by implementing the OnClickListener interface. Button objects contain a setOnClickListener() function that takes in an implementation of OnClickListener.

OnClickListener has a single abstract method, onClick(), that we must implement. Because setOnClickListener() always takes an OnClickListener as an argument, and because OnClickListener always has the same single abstract method, this implementation can be represented using an anonymous function in Kotlin. This process is known as Single Abstract Method conversion, or SAM conversion.

SAM conversion can make our code considerably cleaner. The following example shows how to use SAM conversion to implement an OnClickListener for a Button:

loginButton.setOnClickListener {
    val authSuccessful: Boolean = viewModel.authenticate(
            usernameEditText.text.toString(),
            passwordEditText.text.toString()
    )
    if (authSuccessful) {
        // Navigate to next screen
    } else {
        statusTextView.text = requireContext().getString(R.string.auth_failed)
    }
}

The code within the anonymous function passed to setOnClickListener() executes when a user clicks loginButton.


Companion Objects

The Companion objects provide a mechanism for defining variables or functions that linked conceptually to a type but do not tie to a particular object. Companion objects are similar to using Java’s static keyword for variables and methods.

In the following example, TAG is a String constant. We don’t need a unique instance of the String for each instance of LoginFragment, so we should define it in a companion object:

class LoginFragment : Fragment() {

    ...

    companion object {
        private const val TAG = "LoginFragment"
    }
}

We could define TAG at the top level of the file, but the file might also have a large number of variables, functions, and classes that are also defined at the top level. Companion objects help to connect variables, functions, and the class definition without referring to any particular instance of that class.


Property Delegation

When initializing properties, we might repeat some of Android’s more common patterns, such as accessing a ViewModel within a Fragment. To avoid excess duplicate code, we can use Kotlin’s property delegation syntax.

private val viewModel: LoginViewModel by viewModels()

Property delegation provides a common implementation that we can reuse throughout our app. Android KTX provides some property delegates for us. viewModels, for example, retrieves a ViewModel that is scoped to the current Fragment.

Property delegation uses reflection, which adds some performance overhead. The tradeoff is a concise syntax that saves development time.


Nullability

Kotlin provides strict nullability rules that maintain type-safety throughout our app. In Kotlin, references to objects cannot contain null values by default. To assign a null value to a variable, we must declare a nullable variable type by adding ? to the end of the base type.

As an example, the following expression is illegal in Kotlin. name is of type String and isn’t nullable:

val name: String = null

To allow a null value, we must use a nullable String type, String?, as shown in the following example:

val name: String? = null


Interoperability

Kotlin’s strict rules make our code safer and more concise. These rules lower the chances of having a NullPointerException that would cause our app to crash. Moreover, they reduce the number of null checks, we need to make in our code.

Often, we must also call into non-Kotlin code when writing an Android app, as most Android APIs are written in the Java programming language.

Nullability is a key area where Java and Kotlin differ in behavior. Java is less strict with nullability syntax.

As an example, the Account class has a few properties, including a String property called name. Java does not have Kotlin’s rules around nullability, instead relying on optional nullability annotations to explicitly declare whether we can assign a null value.

Because the Android framework is written primarily in Java, we might run into this scenario when calling into APIs without nullability annotations.


Platform Types

If we use Kotlin to reference a unannotated name member that is defined in a Java Account class, the compiler doesn’t know whether the String maps to a String or a String? in Kotlin. This ambiguity is represented via a platform typeString!.

String! has no special meaning to the Kotlin compiler. String! can represent either a String or a String?, and the compiler lets us assign a value of either type. Note that we risk throwing a NullPointerException if we represent the type as a String and assign a null value.

To address this issue, we should use nullability annotations whenever we write code in Java. These annotations help both Java and Kotlin developers.

For example, here’s the Account class as it’s defined in Java:

public class Account implements Parcelable {
    public final String name;
    public final String type;
    private final @Nullable String accessId;

    ...
}

One of the member variables, accessId, is annotated with @Nullable, indicating that it can hold a null value. Kotlin would then treat accessId as a String?.

To indicate that a variable can never be null, use the @NonNull annotation:

public class Account implements Parcelable {
    public final @NonNull String name;
    ...
}

In this scenario, name is considered a non-nullable String in Kotlin.

Nullability annotations are included in all new Android APIs and many existing Android APIs. Many Java libraries have added nullability annotations to better support both Kotlin and Java developers.


Handling nullability

If we are unsure about a Java type, we should consider it to be nullable. As an example, the name member of the Account class is not annotated, so we should assume it to be a nullable String.

If we want to trim name so that its value does not include leading or trailing whitespace, we can use Kotlin’s trim function. We can safely trim a String? in a few different ways. One of these ways is to use the not-null assertion operator!!, as shown in the following example:

val account = Account("name", "type")
val accountName = account.name!!.trim()

The !! operator treats everything on its left-hand side as non-null, so in this case, we are treating name as a non-null String. If the result of the expression to its left is null, then our app throws a NullPointerException. This operator is quick and easy, but it should be used sparingly, as it can reintroduce instances of NullPointerException into our code.

A safer choice is to use the safe-call operator?., as shown in the following example:

val account = Account("name", "type")
val accountName = account.name?.trim()

Using the safe-call operator, if name is non-null, then the result of name?.trim() is a name value without leading or trailing whitespace. If name is null, then the result of name?.trim() is null. This means that our app can never throw a NullPointerException when executing this statement.

While the safe-call operator saves us from a potential NullPointerException, it does pass a null value to the next statement. We can instead handle null cases immediately by using an Elvis operator (?:), as shown in the following example:

val account = Account("name", "type")
val accountName = account.name?.trim() ?: "Default name"

If the result of the expression on the left-hand side of the Elvis operator is null, then the value on the right-hand side is assigned to accountName. This technique is useful for providing a default value that would otherwise be null.

We can also use the Elvis operator to return from a function early, as shown in the following example:

fun validateAccount(account: Account?) {
    val accountName = account?.name?.trim() ?: "Default name"

    // account cannot be null beyond this point
    account ?: return

    ...
}


Android API changes

Android APIs are becoming increasingly Kotlin-friendly. Many of Android’s most-common APIs, including AppCompatActivity and Fragment, contain nullability annotations, and certain calls like Fragment#getContext have more Kotlin-friendly alternatives.

For example, accessing the Context of a Fragment is almost always non-null, since most of the calls that we make in a Fragment occur while the Fragment is attached to an Activity (a subclass of Context). That said, Fragment#getContext does not always return a non-null value, as there are scenarios where a Fragment is not attached to an Activity. Thus, the return type of Fragment#getContext is nullable.

Since the Context returned from Fragment#getContext is nullable (and is annotated as @Nullable), we must treat it as a Context? in our Kotlin code. This means applying one of the previously-mentioned operators to address nullability before accessing its properties and functions. For some of these scenarios, Android contains alternative APIs that provide this convenience. Fragment#requireContext, for example, returns a non-null Context and throws an IllegalStateException if called when a Context would be null. This way, we can treat the resulting Context as non-null without the need for safe-call operators or workarounds.


Property Initialization

Properties in Kotlin are not initialized by default. They must be initialized when their enclosing class is initialized.

We can initialize properties in a few different ways. The following example shows how to initialize an index variable by assigning a value to it in the class declaration:

class LoginFragment : Fragment() {
    val index: Int = 12
}

This initialization can also be defined in an initializer block:

class LoginFragment : Fragment() {
    val index: Int

    init {
        index = 12
    }
}

In the examples above, index is initialized when a LoginFragment is constructed.

However, we might have some properties that can’t be initialized during object construction. For example, we might want to reference a View from within a Fragment, which means that the layout must be inflated first. Inflation does not occur when a Fragment is constructed. Instead, it’s inflated when calling Fragment#onCreateView.

One way to address this scenario is to declare the view as nullable and initialize it as soon as possible, as shown in the following example:

class LoginFragment : Fragment() {
    private var statusTextView: TextView? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)

            statusTextView = view.findViewById(R.id.status_text_view)
            statusTextView?.setText(R.string.auth_failed)
    }
}

While this works as expected, we must now manage the nullability of the View whenever we reference it. A better solution is to use lateinit for View initialization, as shown in the following example:

class LoginFragment : Fragment() {
    private lateinit var statusTextView: TextView

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)

            statusTextView = view.findViewById(R.id.status_text_view)
            statusTextView.setText(R.string.auth_failed)
    }
}

The lateinit keyword allows us to avoid initializing a property when an object is constructed. If our property is referenced before being initialized, Kotlin throws an UninitializedPropertyAccessException, so be sure to initialize our property as soon as possible.

That’s all about in this article.

Related Other Articles / Posts


Conclusion

In this article, We understood about how to apply common Kotlin patterns in Android apps. This article demonstrated the most useful aspects of the Kotlin language like Working with Fragments and Nullability when developing for Android.

Thanks for reading ! I hope you enjoyed and learned about common Kotlin patterns concepts in Android. Reading is one thing, but the only way to master it is to do it yourself.

Please follow and subscribe to the blog and support us in any way possible. Also like and share the article with others for spread valuable knowledge.

You can find Other articles of CoolMonkTechie as below link :

You can also follow official website and tutorials of Android as below links :

If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.

Thanks again Reading. HAPPY READING !!???

Exit mobile version