Hello Readers, CoolMonkTechie heartily welcomes you in this article.
In this article, we will learn when and how to use GradientDrawable in android. The UI of modern-day apps is getting better and better. Designers are trying out different styles and combinations which go best with the Android App. One of the key components of Android being used these days is called GradientDrawable.
A famous quote about learning is :
” I am always ready to learn although I do not always like being taught.”
So Let’s Begin.
Introduction
A GradientDrawable is drawable with a color gradient for buttons, backgrounds, etc.
Let’s start by taking a basic example of creating a button in Android having an aqua colored background:
This can be done simply by setting the android:background attribute in the XML:
Here we have given a <gradient /> tag and added the three color codes – startColor, centerColor and endColor. We have also given the angle as 0 which denotes LEFT-RIGHT orientation.
We note that Angles can only be multiples of 45. Also, max 3 colors can be specified in XML – startColor, centerColor and endColor. All 3 will occupy equal space.
Then make changes in the <Button /> tag to make it use this background drawable:
android:background="@drawable/tri_color_drawable"
2. GradientDrawable
We can get the same functionality programmatically as well by using GradientDrawable. In our activity, create the GradientDrawable.
val gradientDrawable = GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
intArrayOf(
0XFFD98880.toInt(),
0XFFF4D03F.toInt(),
0XFF48C9B0.toInt()
))
Here we have given the orientation as LEFT_RIGHT (which corresponds to 0 we added in XML earlier). We have also given the same three colors we used earlier.
Next, set this gradientDrawable as the background of the button.
val continueBtn: Button = findViewById(R.id.continue_btn)
continueBtn.background = gradientDrawable
The need of GradientDrawable
So why do we need GradientDrawable if the work can be done using XML?
Example 1
Let’s take a recap of the previous example.
Here each color is taking equal space. We can say that color 1 is starting at 0 percent, color 2 at ~33 percent, and color 3 at ~66 percent. What if you don’t want all colors to occupy equal space? Instead, you want something like this:
If we notice here, color 1 is taking half of the entire space (50 percent) whereas the other two colors are equally covering the remaining space. (25 percent each).
This cannot be achieved via XML. This is where the actual power of GradientDrawable is unleashed.
To achieve the above result, we can do something like this:
val gradientDrawable = GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT,
intArrayOf(
0XFFD98880.toInt(),
0XFFD98880.toInt(),
0XFFF4D03F.toInt(),
0XFF48C9B0.toInt()
))
Here color 1 is used twice, hence it will take 50 percent of the total space. Remaining two colors will equally occupy the remaining 50 percent space.
Example 2
As another example, let’s say we want a 5 colors gradient.
This also is not possible via XML but can be easily done using GradientDrawable like this:
Here we have given 5 colors and each will cover equal space. You can give as many colors as required.
GradientDrawable’s Main Components
let’s take a closer look at GradientDrawable’s main components:
1. Orientation
It can be one of the orientations as defined in the enum GradientDrawable.Orientation. In our examples, we have used LEFT_RIGHT. Other possible orientations are TOP_BOTTOM, TR_BL, RIGHT_LEFT, etc. They are self-explanatory.
2. Array of Colors
Here we need to provide an array of hexadecimal color values. For 0XFFD98880,
0X -Represents a hexadecimal number.
FF – Represents the alpha value to be applied to the color. Alpha can be 0 to 100. Here FF represents 100% alpha.
D98880 – Represents the RRGGBB hexadecimal color value.
That’s all about in this article.
Conclusion
In this article, we learned about how to use GradientDrawable in Android. We have also discussed different ways and main components of GradientDrawable in Android with examples.
Thanks for reading ! I hope you enjoyed and learned about GradientDrawable Concept 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 and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.
Hello Readers, CoolMonkTechie heartily welcomes you in this article.
In this article, we will learn how Flow APIs work in Kotlin and how can we start using it in our android projects.
If we are working as an Android developer and looking to build an app asynchronously we might be using RxJava as it has an operator for almost everything. RxJava has become one of the most important things to know in Android.
But with Kotlin a lot of people tend to use Co-routines. With Kotlin Coroutine 1.2.0 alpha release Jetbrains came up with Flow API as part of it. With Flow in Kotlin now you can handle a stream of data that emits values sequentially.
“In Kotlin, Coroutine is just the scheduler part of RxJava but now with Flow APIs coming along side it, it can be alternative to RxJava in Android.”
We will cover the following topics to understanding the Flow API :
What is Flow APIs in Kotlin Coroutines?
Start Integrating Flow APIs in your project
Builders in Flows
Few examples using Flow Operators.
A famous quote about learning is :
“Learn as though you would never be able to master it; hold it as though you would be in fear of losing it.”
So Let’s begin.
What is Flow APIs in Kotlin Coroutines?
Flow API in Kotlin is a better way to handle the stream of data asynchronously that executes sequentially.
So, in RxJava, Observables type is an example of a structure that represents a stream of items. Its body does not get executed until it is subscribed to by a subscriber and once it is subscribed, subscriber starts getting the data items emitted. Similarly, Flow works on the same condition where the code inside a flow builder does not run until the flow is collected.
Start Integrating Flow APIs in your Project
Let us create an android project and then let’s start integrating the Kotlin Flow APIs.
Now, let’s begin the implementation of Flow APIs in MainActivity. In onCreate() function of Activity lets add two function like,
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupFlow()
setupClicks()
}
Here, setupFlow() is the function where we will define the flow and setupClicks() is the function where we will click the button to display the data which is emitted from the flow.
We will declare a lateinit variable of Flow of Int type,
lateinit var flow: Flow<Int>
Step 04
Now, in setupFlow() I will emit items after 500milliseconds delay.
To emit the number we will use emit() which collects the value emitted. It is part of FlowCollector which can be used as a receiver.
and, at last, we use flowOn operator which means that shall be used to change the context of the flow emission. Here, we can use different Dispatchers like IO, Default, etc.
” flowOn() is like subscribeOn() in RxJava.”
Step 05
Now, we need to write setupClicks() function where we need to print the values which we will emit from the flow.
When we click the button we will print the values one by one.
Here,
flow.collect now will start extracting/collection the value from the flow on the Main thread as Dispatchers.Main is used in launch coroutine builder in CoroutineScope. The output which will be printed in Logcat is,
Here, we converted a range of values from 1 to 5 as flow and emitted each of them at a delay of 300ms. When we attach a collector to the flow, we get the output,
If both flows doesn’t have the same number of item, then the flow will stop as soon as one of the flow completes.
That’s all about in this article.
Conclusion
In this article, we learned about how Flow APIs work in Kotlin and how can we start using it in our android projects. We have also discussed builders in flow and zip operators.
Thanks for reading ! I hope you enjoyed and learned about Flow API concepts in Kotlin. 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 and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.
Hello Readers, CoolMonkTechie heartily welcomes you in this article.
In this article, we will learn how to optimize application size (APK) in Android. Most of the user would not like to download a large APK as it might consume most of his Network/Wifi Bandwidth, also most importantly, space inside the mobile device. The size of our APK has an impact on how fast our app loads, how much memory it uses, and how much power it consumes.
It’s important to optimize the size of the app since mobiles are always memory and space constraint devices. We will discuss about the various ways in which we can improve our apk size in Android-Development.
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.
Understanding Android App Bundles
An Android App Bundle is a publishing format that includes all your app’s compiled code and resources, and defers APK generation and signing to Google Play.
Google Play uses our app bundle to generate and serve optimized APKs for each device configuration, so only the code and resources that are needed for a specific device are downloaded to run our app. We no longer have to build, sign, and manage multiple APKs to optimize support for different devices, and users get smaller, more optimized downloads.
App Bundles are Publishing formats
An Android App Bundle is a file (with the .aab file extension) that we upload to Google Play. App bundles are signed binaries that organize your app’s code and resources into modules. Code and resources for each module are organized similarly to what you would find in an APK—and that makes sense because each of these modules may be generated as separate APKs. Google Play then uses the app bundle to generate the various APKs that are served to users, such as the base APK, feature APKs, configuration APKs, and (for devices that do not support split APKs) multi-APKs. The directories that are colored in blue—such as the drawable/, values/, and lib/ directories—represent code and resources that Google Play uses to create configuration APKs for each module.
Android App Bundles — File Targeting and Serving
How do these Android App bundles help in File targeting? It’s simple, let’s say we have hdpi, xhdpi, xxhdpi resources in our application. Based on the device on which the application is being downloaded, if it’s a hdpi device (for example), only the resources from hdpi will be installed on the device.
If the application is targeting multiple languages (English, Spanish, French, etc.), only the specific string resources will be downloaded onto the device.
This helps in saving the space on the device’s memory.
Building the Android App Bundle
Building the Android app bundle is straight forward. Just select the Build option from the Android Studio menu and select Build Bundles.
Android Size Analyzer
In order to understand which files are actually taking up more space in the application, use the Android Size Analyser plugin inside Android Studio. For installing the plugin.
Select File > Settings (or on Mac, Android Studio > Preferences.)
Select the Plugins section in the left panel.
Click the Marketplace tab.
Search for the “Android Size Analyzer” plugin.
Click the Install button for the analyzer plugin.
Restart the IDE after installing the plugin. Now, to analyze the application, go to Analyze > Analyze App Size from the menu bar. We will get a window something similar to this:
The recommendations can help us in reducing the app size in a much better way.
Remove Unused Resources
As we have already discussed the size of the apk has an impact on how fast the app loads, how much memory it uses, and how much memory power it consumes. Hence, one of the main things that can be implemented to reduce apk size is to remove unused resources in the application.
Also, it is advised to use scalable drawable objects(importing vector assets) instead of other image formats like PNG, JPEG, etc.
Using Vector Drawable is one of the best ways to reduce the size significantly.
Using Lint
Lint actually helps in generating warnings or unused code inside the application. So this can actually help in removing the same thereby helping in reducing the size of the application.
Reduce libraries size
Check if we can reduce the size when it comes to the usage of libraries. For example, use only specific libraries of Google Play Services. Compile only which is required.
Reuse Code
Object-Oriented Programming has solved a lot of problems in the programming world. Try reusing the code as much as possible instead of repetitive code. Repetitive code also leads to increased file size thereby effecting the Apk size.
Compress PNG and JPEG files
If using PNG and JPEG files is something mandatory in our project, we can compress them using image quality tools like TinyPNG.
In most of the applications, Images are used to convey messages or improve the UX. But the biggest drawback here might be using a lot of images that can bloat up the size of the app. Ensure that the Image Compression techniques are understood and implemented to reduce the size of the apk before releasing the app to the play store.
Use WebP file format
As we have seen in the image shared for the Android Analyser plugin above, one of the recommendations was to change the PNG file to a WebP file format.
Use Proguard
Every time we build a new project, we see the following piece of code in the app-level build.gradle file.
ProGuard makes the following impact on our project,
It reduces the size of the application.
It removes the unused classes and methods that contribute to the 64K method counts limit of an Android application.
It makes the application difficult to reverse engineer by obfuscating the code.
Create Multiple APKs
If we are not using App Bundles, we can go with the traditional way. We can create multiple APKs similar to App Bundle. Multiple apk is mainly used to generate specific APKs for different screen densities and different CPU architecture.
ShrinkResources
Reduce resources where ever possible. Using shrinkResources attribute in the Gradle will remove all the resources which are not being used anywhere in the project. Enable this in your app-level build.gradle file by adding below line:
Remove the localized resources which are not needed by using resConfigs. All the support libraries may have localized folders for the other languages which we don’t need.
The Gradle resource shrinker removes only resources that are not referenced by our app code, which means it will not remove alternative resources(device/location-specific) for different device configurations. If necessary, you can use the Android Gradle plugin’s resConfigs property to remove alternative resource files that our app does not need.
The following snippet shows how to limit our language resources to just English and French:
As discussed, having unused language resources only swells the apk size. Hence it is important to remove unused files and resources.
DebugImplementation
Remove any debug library you have in the app. It can be done by using debugImplementation while building testing debug apk.
Use R8 to reduce APK size
R8 shrinking is a process in which we reduce the amount of code of our application and by doing so, the APK size automatically gets reduced. R8 does most of the work as Proguard.
So Why do we need to prefer it?
The reason is it works with Proguard rules and shrinks the code faster while improving the output size.
So understanding and implementing these different methods in our applications to deliver an optimized apk.
That’s all about in this article.
Conclusion
In this article, we learned about different methods to optimize app size in Android.
Thanks for reading ! I hope you enjoyed and learned about Reducing application size 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 and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.
Hello Readers, CoolMonkTechie heartily welcomes you in this article.
In this article, we will learn how we can use the ViewModel in our application to communicate between various fragments in our application. We say it as SharedViewModel. Communication between Activities or Fragments in Android is a very common thing. Almost every application has some communication between various activities or fragments.
Using SharedViewModel, we can communicate between fragments.
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.
Data sharing between Fragments
we can communicate between fragments using SharedViewModel. If we consider two fragments, both the fragments can access the ViewModel through their activity.
In this example, one fragment updates the data within the ViewModel which is shared between both the fragments and another fragment observes the changes on that data. We need to add the dependencies required for ViewModel, LiveData in the example.
First, we will create a class SharedViewModel.
class SharedViewModel : ViewModel() {
val message = MutableLiveData<String>()
fun sendMessage(text: String) {
message.value = text
}
}
Now, we are going to create two Fragments:
MessageReceiverFragment: This Fragment is going to receive the message which will be sent by MessageSenderFragment. It will have a TextView which will show the received message.
MessageSenderFragment: This Fragment is going to send the message which will be received by MessageReceiverFragment. It will have a Button to send the message.
MessageReceiverFragment class:
class MessageReceiverFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_receiver, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val model = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
model.message.observe(viewLifecycleOwner, Observer {
textViewReceiver.text = it
})
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
Now, we are ready to run the app, see if it is working as expected. It should work.
Let’s discuss how it is working:
Here, we have created an activity that consists of two fragments. The same activity is the host for both the fragment.
In both the fragment, we have created the object of SharedViewModel which is the same object as we are using the same single activity as an owner. This is the reason it is shared. Notice that we have used the requireActivity().
In the MessageSenderFragment, we are sending the message on button click, which sets the value of message - LiveData in the SharedViewModel.
Then, in the MessageReceiverFragment, we are observing the change in the message - LiveData, and whenever the message is coming, we are updating the data in textView.
This is how it works.
That’s all about in this article.
Conclusion
In this article, we learned about Shared ViewModel in Android to communicate with other fragments. In the last, we saw one example of fragment communication using Shared ViewModel
Thanks for reading ! I hope you enjoyed and learned about Shared ViewModel in Android to communicate with other fragments. 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 and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.
Hello Readers, CoolMonkTechie heartily welcomes you in this article.
In this article, We will learn about Dex and Multidex in Android. Most of developers get 64K method limit exceeded error while building APK. This error comes due to DEX. So, in this article, we will try to gain more knowledge about Dex and Multidex in Android.
In Java, if we compile our code then that code is converted into a .class file by the compiler because our machine understands that .class file. The same is the process for the Android Application. In Android, the compiler converts our source code into DEX(Dalvik Executable) file.
So understanding the Dex and Multidex Concepts, We will cover the below topics:
Android Build System
Multidex in Android
Enabling Multidex support in Project
Limitations of Multidex support library
A famous quote about Learning is :
” The beautiful thing about learning is that nobody can take it away from you.”
So Let’s Start.
Android Build System
Before moving on to DEX, let’s revise some concepts of the build system of Android. Android compiles the resources and source code of our app and packages them into APKs. Conversion of our project into an APK requires many tools and involves many processes. You can understand the build process with the help of the below diagram:
The build process of a typical Android App can be summarized as below:
The compilers convert the source code into DEX (Dalvik Executable) files, which include the bytecode that runs on Android devices.
After having the DEX files, the APK Packager combines the DEX files and compiled resources into a single APK.
After that, the APK Packager signs the APK.
Before generating the final APK, various methods are followed to optimize the code so as to avoid the unused code.
Multidex in Android
In Android, the compilers convert your source code into DEX files. This DEX file contains the compiled code used to run the app. But there is a limitation with the DEX file. The DEX file limits the total number of methods that can be referenced within a single DEX file to 64K i.e. 65,536 methods. So, you can’t use more than 64K methods in a particular DEX file. These 64K methods include Android framework methods, library methods, and methods in our code also. This limit of 64K is referred to as the “64K reference limit“.
So, if our app exceeds 65,536 methods, we will encounter a build error that indicates our app has reached the limit of the Android build architecture. The error is as follows:
Too many field references: 131000; max is 65536.
You may try using --multi-dex option.
Older versions of the build system report a different error, which also indicates the same problem:
Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536
Both the above error are known as 64K reference limit i.e we are trying to use more than 64K methods in our code. So, here comes the role of Multidex support in our Android Project. Next time if we want to use more than 64K methods in our project then we can use the Multidex to achieve this.
If we are getting some type of DEX error then before configuring our app for multidex to enable the use of 64K or more reference methods, we should try to reduce the total number of reference methods that are being used by our app. Try the following strategies to avoid using 64K reference methods:
Manage dependencies: While importing some dependencies for our project, sometimes we import each and every library and use only a few of them. So, instead of keeping all those dependencies, we can keep only the required one.
Remove unused code: Enable code shrinking in your project. By doing so, we are not shipping unused code with our APK.
Enabling Multidex support in Project
Here, we will see how to enable multidex for following:
API level lower than 21: That is if your minSdkVersion is set to lower than 21.
API level 21 and higher.
We follow only one of the following based on the minSdkVersion.
Multidex support for API level lower than 21
Multidex support for API level 21 and higher
Multidex support for API level lower than 21
For executing app code, versions of Android lower than Android 5.0 (API level 21) use the Dalvik runtime, and the limitation of using Dalvik is that you can’t use more than one classes.dex bytecode file per APK. To overcome this limitation, you will have to add the Multidex support library.
To add the multidex support library to our project for API level lower than 21, add the following dependency in our build.gradle file.
By adding this library, our app can manage the access of additional DEX files. In other words, if we are having more than 64K methods, then we will be having more than one DEX file and these DEX files will be managed by using this multidex support library.
Then, modify the module-level build.gradle file to enable multidex using multiDexEnabled true.
2. If you do override the Application class, change it to extend MultiDexApplication (if possible) as follows:
public class MyApplication extends MultiDexApplication { ... }
3. Or if we do override the Application class but it’s not possible to change the base class, then we can instead override the attachBaseContext() method and call MultiDex.install(this) to enable multidex:
public class MyApplication extends SomeOtherApplication {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
Multidex support for API level 21 and higher
Our task becomes easier if we are making an app for Android version 5.0 (API level 21) or higher. API level 21 uses a runtime called ART which supports loading multiple DEX files from APK files. So, we do NOT need to add the support library for multidex in Android 5.0 or higher.
Now, for API level 21 or higher, we need to set multiDexEnabled to true in our module-level build.gradle file, as shown here:
2. If we do override the Application class, change it to extend MultiDexApplication (if possible) as follows:
public class MyApplication extends MultiDexApplication { ... }
3. Or if we do override the Application class but it’s not possible to change the base class, then we can instead override the attachBaseContext() method and call MultiDex.install(this) to enable multidex:
public class MyApplication extends SomeOtherApplication {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
So, now when we build our app, one primary DEX file will be constructed, and supporting DEX files will also be added to it. The primary DEX file is normally marked as classes.dex and other supporting DEX files as classes1.dex, classes2.dex, … and so on.
Limitations of Multidex support library
A good Android Developer know about the benefits of using some library but a professional developer also knows the limitations of using that library.
The multidex support library has some known limitations that we should be aware of before using it in our app. Some of the limitations are:
If our secondary DEX file is larger than the primary DEX file, then we may encounter some Application Not Responding (ANR) error. In this case, we should apply code shrinking to minimize the size of DEX files and remove unused portions of code.
If we are targeting API levels lower than 21, test thoroughly on those versions of the platform, because our app might have issues at startup or when particular groups of classes are loaded.
Code Shrinking can reduce or possibly eliminate these issues.
That’s all about in this article.
Conclusion
In this article, We understood about Dex and Multidex in Android. If we are working on an Android application that includes more than 64K methods , then to avoid the 64K reference limit, we can use Multidex in our application. So, implement Multidex in our project, but before doing so, make sure that our code is totally shrunk because code shrinking will help in reducing or possibly eliminating the Dex error.
Thanks for reading ! I hope you enjoyed and learned about Dex and Multidex 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 and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.
Hello Readers, CoolMonkTechie heartily welcomes you in this article.
In this article, We will learn about Extension Functions and Static Utility Class differences in Kotlin. We will talk about when to use extension function and when to use static utility class in the android project using Kotlin.
To understand the extension function and static utility class concepts, We will focus on the below topics :
What are Extension function and Util Class?
Use Cases
Where we use them?
Advantages of using Wrapper like Extension or Util Class.
A famous quote about Learning is :
” Change is the end result of all true learning.”
So Let’s Start.
What are Extension function and Util class?
Extension function:
These are like extensive property attached to any class in Kotlin. It provides additional methods to that class without manually inheriting the class.
For example, Let’s say, we have views where we need to play with the visibility of the views. So, we can create extension function for views like,
fun View.show() {
this.visibility = View.VISIBLE
}
fun View.hide() {
this.visibility = View.GONE
}
and to use it we use, like,
toolbar.hide()
Here, we have attached an additional feature of hide() and show() to views in android.
Here, these above two extension functions can only be used by View Type and not any other type. For example, String can’t use the functions here.
And to access, that view in the function we use this.
Util Class:
Util class is like a collection of static functions whose code can be reused again and again by passing the reference of the type as a parameter.
For example, if we take the above example of visibility Gone and Visible then update the code according to Util class way,
object Util {
fun show(view: View){
view.visibility = View.VISIBLE
}
fun hide(view: View){
view.visibility = View.GONE
}
}
and to use it we use,
Util.show(imageView)
Here, we can see unlike extension function, we need to pass the reference of the view as a parameter.
Or we can directly write the util functions without Util object. For example, we will create a file Util.kt and update the file as,
fun show(view: View){
view.visibility = View.VISIBLE
}
fun hide(view: View){
view.visibility = View.GONE
}
and to use this we can directly call,
show(imageView)
We can create Util both ways mentioned above.
Use Cases
Consider this like, when creating an extension function in Kotlin, it creates a property for that specific type(let’s say ImageView) which can be accessed across by all the ImageViews.
But, when Using Util, we don’t add this property again, and again to use the util functions, we need to explicitly call it.
So, now let’s understand this with an example.
Consider we have an ImageView, where we want to load image from url. We might consider using Glide or Picasso for this. So, here I will use Glide and create an extension function like
fun ImageView.loadImage(url: String) {
Glide.with(this.context).load(url).into(this)
}
Here, we can see, we created an extension function called loadImage() which takes url as a parameter and we load the url into the ImageView.
Now, to use this we just use,
imageViewProfile.loadImage("url")
This loadImage property is accessed by the imageViewProfile and here the extension function is helping the ImageView to load the image from url.
Now, think this as this won’t be just accessible for imageViewProfile but for all the ImageViews in the app but will only be accesible by ImageViews. loadImage() has become like property to ImageViews now. All the ImageViews can access it but just using the dot operator.
Now, Consider the same example using Util class. Here we need to pass both them imageView and the url to load the image.
fun loadImage(imgView:ImageView,url:String){
Glide.with(this.context).load(url).into(imgView)
}
and to use this we have to call
Util.loadImage()
Now, here it is not associated with imageView. We are passing the ImageView as a parameter to this function along with the url to load the url in the image view.
When to use Extension function or Util function?
Let’s say if we have a multiple occurrence of the same action across the app like loading image from url in ImageView in multiple ImageViews across the app, we should prefer using extension function as it is like the major use-case here and so it gets attached to the imageView as a property.
But, when we don’t want to use the property heavily and its a very rare occurrence in the app like once or twice we should use util functions.
Advantages of using Wrapper like Extension or Util Class
In any case, using either extension functions or util class helps us a lot while developing the android app. It helps us,
to make the code reusable.
to make the code more readable.
to migrate to another library very easily.
Let’s say in the above example,
fun ImageView.loadImage(url: String) {
Glide.with(this.context).load(url).into(this)
}
Here, the loading of image using glide is written once and can be used multiple times.
Using the loadImage() as the name makes our code readable as when we use it like,
imageViewProfile.loadImage("url")
the name itself makes it more clear that we are trying to load a url to the ImageView.
And at last, the biggest advantage of using a wrapper helps us in migration to another library if required. For example, let’s say someday we need to remove the Glide library and replace the code with Picasso to load images.
Then we need to only update the code only once in either extension function or the util class and the library would be replaced successfully. We would not have to change or tweak any code in view files.
That’s all about in this article.
Conclusion
In this article, We understood about Extension Functions and Static Utility Class Usage in Kotlin. Depending upon the use-case we should decide which way to use to make our code reusable.
Thanks for reading ! I hope you enjoyed and learned about Extension Functions and Static Utility Class Concepts in Kotlin. 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 and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.
Hello Readers, CoolMonkTechie heartily welcomes you in this article.
In this article, We will learn about three best available options to handle multithreading in iOS. Production applications will often need to perform heavier operations such as downloading high-resolution images or a executing non-cached database queries. To prevent stalling the main thread (and a hit in frame rate), Apple has provided a few tools to help us. We will discuss below available options to handle multithreading:
Grand Central Dispatch
NSOperation and NSOperationQueue
performSelectorInBackground
A famous quote about Learning is :
” I am always ready to learn although I do not always like being taught. “
So Let’s begin.
1. Grand Central Dispatch
Grand Central Dispatch is a technology that abstracts away the low-level details of multithreading. When using GCD, we only have to think about the tasks we want to perform. These tasks can then be added to serial or concurrent queues. Moreover, we can add tasks to groups and run code after all tasks within the group complete.
Let’s walk through an example where we download an image from a remote URL and then use it to populate a UIImageView.
// Assume we have an `imageView` property on self
private func loadWallpaper() {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { [weak self] in
guard
let wallpaperURL = NSURL(string: "http://wallpapers.wallhaven.cc/wallpapers/full/wallhaven-157301.jpg"),
let imageData = NSData(contentsOfURL: wallpaperURL)
else {
return
}
dispatch_async(dispatch_get_main_queue()) {
self?.imageView.image = UIImage(data: imageData)
}
}
}
Most uses of GCD start with a call to dispatch_async, which takes in a queue to use and the block to execute. In our example, we’d like to execute the wallpaper download on a background queue, so we make use of the system-defined global queue with a background quality of service (QoS), DISPATCH_QUEUE_PRIORITY_BACKGROUND. The flag passed into dispatch_get_global_queue should always be 0.
Now we have the block of work to execute. We construct a NSURL via its fail-able String initializer and then fetch the data associated with that resource via NSData(contentsOfURL:). If the above step completes successfully (else we just return from the block), we now have our data at hand.
To update imageView‘s image property, we need to make sure we return to the main thread via dispatch_async(dispatch_get_main_queue()) { /* ... */ }. Remember in iOS, all UI updates should be performed on the main thread. Inside the main thread block, we set the image using the NSData initializer on UIImage.
Now that we’ve seen a one-off block example, let’s dive into how we can accomplish groups of dependent tasks. Imagine we wanted to download multiple wallpapers and present an alert to the user when all of the images finish loading. Dispatch groups will be our best friends in these scenarios.
First, let’s refactor the loadWallpaper function from the previous example to accept a dispatch_group_t and a target URL.
private func loadWallpaper(group: dispatch_group_t, url: String) {
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { [weak self] in
defer {
dispatch_group_leave(group)
}
guard
let wallpaperURL = NSURL(string: url),
let imageData = NSData(contentsOfURL: wallpaperURL)
else {
// In production scenarios, we would want error handing here
return
}
// Use imageData in some manner, e.g. persisting to a cache, present in view hierarchy, etc.
print("Image downloaded \(url)")
}
}
The function has been modified slightly to accept a parameter group of type dispatch_group_t (we’ll go into how to create these groups in the next snippet) and a target URL. Additionally, our previous call to dispatch_async has been replaced with dispatch_group_async, signalling that the block should be associated with group. Lastly, after completing our work with the resulting imageData we must notify group that the block is complete via dispatch_group_leave.
To use loadWallpaper(_:url:) a call site could look like so:
private func fetchAllWallpapers() {
let urls = [
"http://wallpapers.wallhaven.cc/wallpapers/full/wallhaven-329991.jpg",
"http://wallpapers.wallhaven.cc/wallpapers/full/wallhaven-329805.jpg",
"http://wallpapers.wallhaven.cc/wallpapers/full/wallhaven-330201.jpg"
]
let wallpaperGroup = dispatch_group_create()
urls.forEach {
dispatch_group_enter(wallpaperGroup)
loadWallpaper(wallpaperGroup, url: $0)
}
dispatch_group_notify(wallpaperGroup, dispatch_get_main_queue()) { [weak self] in
let alertController = UIAlertController(title: "Done!", message: "All images have downloaded", preferredStyle: .Alert)
alertController.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
self?.presentViewController(alertController, animated: true, completion: nil)
}
}
We start by creating a dispatch group, wallpaperGroup, using dispatch_group_create(). With the group in hand, we loop over all of the wallpaper URLs, first signalling to the group that we are about to start an operation by making a call to dispatch_group_enter(wallpaperGroup) (each group entry call must pair with a group leave call). We then proceed to call loadWallpaper(_:url:).
To run code after completion of the group, we specify a block in a dispatch_group_notify call. In our case, we’ll simply present a UIAlertController letting the user know that all of the downloads have finished.
While GCD can be extremely powerful, it can be a bit cumbersome to work with in practice. To help with this, we can use Swifty GCD wrapper .
protocol ExcutableQueue {
var queue: dispatch_queue_t { get }
}
extension ExcutableQueue {
func execute(closure: () -> Void) {
dispatch_async(queue, closure)
}
}
enum Queue: ExcutableQueue {
case Main
case UserInteractive
case UserInitiated
case Utility
case Background
var queue: dispatch_queue_t {
switch self {
case .Main:
return dispatch_get_main_queue()
case .UserInteractive:
return dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)
case .UserInitiated:
return dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)
case .Utility:
return dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)
case .Background:
return dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)
}
}
}
enum SerialQueue: String, ExcutableQueue {
case DownLoadImage = "myApp.SerialQueue.DownLoadImage"
case UpLoadFile = "myApp.SerialQueue.UpLoadFile"
var queue: dispatch_queue_t {
return dispatch_queue_create(rawValue, DISPATCH_QUEUE_SERIAL)
}
}
Using this wrapper, our example above could be rewritten as:
Queue.Background.execute {
guard
let url = NSURL(string: "http://wallpapers.wallhaven.cc/wallpapers/full/wallhaven-157301.jpg"),
let data = NSData(contentsOfURL: url)
else {
return
}
Queue.Main.execute { [weak self] in
self?.imageView.image = UIImage(data: data)
}
}
2. NSOperation and NSOperationQueue
NSOperations and NSOperationQueues provide you with a higher-level API, when compared to GCD. They were first introduced in iOS 4 and are actually implemented with GCD under the hood. Typically, we’ll want to use this API over GCD, unless you’re performing a simple unit of work on a specific queue. NSOperations provide us with powerful functionality such as cancellation and dependencies.
To start, we’ll port the wallpaper downloading example to use an NSBlockOperation. NSBlockOperation is a simple wrapper on a block of work that can be added to a queue.
The initializer for NSBlockOperation simply takes a block to run. In our case, we’ll download the data from wallpaperURL and return to the main queue to set the image property on imageView. After initializing downloadOperation, we add it to queue.
When creating an NSOperationQueue, we have a few points of customization.
let queue = NSOperationQueue()
queue.maxConcurrentOperationCount = 1
// If you want to hold the queue, use the `suspended` property
queue.suspended = true
The maxConcurrentOperationCount property allows us to set a limit on how many operations may run concurrently in a given queue. Setting this to 1, implies our queue will be serial (queing order may not be preserved, as operations only run when their ready flag is set to true). If this property isn’t set, it defaults to NSOperationQueueDefaultMaxConcurrentOperationCount, which is dictated by system conditions.
By default, all operations that are ready (ready property is true) are run when added to a queue. We can halt all execution on a queue by setting the suspended property to true.
NSOperations become really powerful when we separate them out into operation subclasses. To demonstrate this, let’s make a wallpaper resizing operation. We’ll need to subclass a customwrapper of NSOperation that has the proper KVO notifications in place.
class ResizeImageOperation: Operation {
enum Error {
case FileReadError
case ResizeError
case WriteError
}
let targetSize: CGSize
let path: NSURL
var error: Error?
init(size: CGSize, path: NSURL) {
self.targetSize = size
self.path = path
}
override func execute() {
// Need to signal KVO notifications for operation completion
defer {
finish()
}
guard let sourceImage = UIImage(contentsOfFile: path.absoluteString) else {
error = Error.FileReadError
return
}
let finalWidth: CGFloat, finalHeight: CGFloat
let ratio = sourceImage.size.width / sourceImage.size.height
// Scale aspect fit the image
if sourceImage.size.width >= sourceImage.size.height {
finalWidth = targetSize.width
finalHeight = finalWidth / ratio
} else {
finalHeight = targetSize.height
finalWidth = finalHeight * ratio
}
let imageSize = CGSize(width: finalWidth, height: finalHeight)
UIGraphicsBeginImageContextWithOptions(imageSize, true, 0.0)
defer { UIGraphicsEndImageContext() }
let rect = CGRect(origin: .zero, size: imageSize)
sourceImage.drawInRect(rect)
guard
let resizedImage = UIGraphicsGetImageFromCurrentImageContext(),
let imageData = UIImageJPEGRepresentation(resizedImage, 1.0)
else {
error = Error.ResizeError
return
}
guard imageData.writeToFile(path.absoluteString, atomically: true) else {
error = Error.WriteError
return
}
}
}
To help with error handling, we add a nested Error enum with a few cases.
ResizeImageOperation can be initialized with a target size and path to write,
The meat of the operation is placed in the execute method (overridden from Operation). We need to make sure to defer a call to finish(), so that the Operation superclass can signal the proper KVO notifications.
We then proceed with the resizing the image (scale aspect fit) and saving it to disk.
Now that we have a resizing operation in hand, let’s refactor our download operation a bit to work with it:
We now return an NSOperation and have the operation write the image data to disk. Lastly, to make the download and resize operations dependent, we can use them like so:
// Assume self has `imageView` and `wallpaperQueue` properties
if
let cacheDirectory = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true).first,
let cacheDirectoryURL = NSURL(string: cacheDirectory)
{
let targetURL = cacheDirectoryURL.URLByAppendingPathComponent("wallpaper.jpg")
let downloadOperation = downloadWallpaper(NSURL(string: "http://wallpapers.wallhaven.cc/wallpapers/full/wallhaven-329991.jpg")!, path: targetURL)
let resizeOperation = ResizeImageOperation(size: CGSize(width: imageView.bounds.size.width * 2, height: imageView.bounds.size.height * 2), path: targetURL)
resizeOperation.addDependency(downloadOperation)
resizeOperation.completionBlock = { [weak self, weak resizeOperation] in
if let error = resizeOperation?.error {
print(error)
return
}
guard
let path = resizeOperation?.path,
let imageData = NSData(contentsOfFile: path.absoluteString)
else {
return
}
NSOperationQueue.mainQueue().addOperationWithBlock {
self?.imageView.image = UIImage(data: imageData)
}
}
wallpaperQueue.suspended = true
wallpaperQueue.addOperation(downloadOperation)
wallpaperQueue.addOperation(resizeOperation)
wallpaperQueue.suspended = false
}
The key line to notice is resizeOperation.addDependency(downloadOperation). That’s how we express the resizing operation’s dependency on downloadOperation.
Moreover, in the completion block of resizeOperation, we check for errors and proceed with displaying the resized image.
Note: we make sure to suspend the queue first, then add the operations. This prevents the operations from beginning immediately upon addition.
3. PerformSelectorInBackground
To wrap up, let’s show a simple example of performSelectorInBackground. Assuming self has a method sleepAndPrint(_:), we can make the following call:
In this article, We understood about three best available options to handle multithreading in iOS. We’ve discussed about GCD, NSoperations, and NSObject‘s performSelectorInBackground method as means of performing work in a multithreaded fashion. If we have small units of work to perform, we’ll want to reach for GCD or performSelectorInBackground. On the other hand, if we have larger operations that may have dependencies, NSOperation should be our tool of choice.
Thanks for reading ! I hope you enjoyed and learned about three best available options to handle multithreading in iOS. 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 and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.
Hello Readers, CoolMonkTechie heartily welcomes you in this article.
In this article, We will learn about Property Delegation concepts in Kotlin. The Kotlin programming language has native support for class properties. Properties are usually backed directly by corresponding fields, but it does not always need to be like this – as long as they are correctly exposed to the outside world, they still can be considered properties. This can be achieved by handling this in getters and setters, or by leveraging the power of Delegates.
We will discuss the below topics to understand the Property Delegation Concepts in Kotlin:
What is Property Delegation?
What are Default Delegated Properties in Kotlin?
How to create a custom Delegation Property?
How to use Kotlin Delegation Property with Android Development?
A famous quote about Learning is :
” The beautiful thing about learning is that nobody can take it away from you.”
So Let’s begin.
What is Property Delegation?
A “Delegate” is just a class that provides the value of a property and handles its changes. This will help us to delegate(assign or pass on), the getter-setter logic altogether to a different class so that it can help us in reusing the code.
Simply put, delegated properties are not backed by a class field and delegate getting and setting to another piece of code. This allows for delegated functionality to be abstracted out and shared between multiple similar properties – e.g. storing property values in a map instead of separate fields.
Delegated properties are used by declaring the property and the delegate that it uses. The by keyword indicates that the property is controlled by the provided delegate instead of its own field.
This is an alternative to inheritance property.
What are Default Delegated Properties in Kotlin?
Kotlin contains some inbuilt examples for Delegated Properties such as:
lazy properties: the value gets computed only upon first access;
observable properties: listeners get notified about changes to this property;
storing properties in a map, instead of a separate field for each property.
How to create a custom Delegation Property?
For the case of simplicity, let’s take a very simple use-case. Let’s consider a scenario where we want a String property that always gets trimmed and has a common appended string after the original value.
The general way we would do this is:
var string: String = ""
set(value) {
field = "${value.trim()} is a String!"
}
fun main() {
string = "checking..... "
println(string)
}
//output: checking..... is a String!
We can see that the extra spaces are trimmed and the required string is appended.
Well, this is good for one variable. What if we have a bunch of string variables containing the same functionality?
We have to keep adding this setter property repeatedly:
var stringOne: String = ""
set(value) {
field = "${value.trim()} is a String!"
}
var stringTwo: String = ""
set(value) {
field = "${value.trim()} is a String!"
}
var stringThree: String = ""
set(value) {
field = "${value.trim()} is a String!"
}
.
.
.
.
This meets our requirement but we see a lot of repetitive code. So, how can we resolve this?
Yes, We can resolve by Property Delegation!
Here, we shall “delegate” the property of trimming and appending the common string to a separate class so that we can reuse this wherever required.
There will be times that we want to write our delegates, rather than using ones that already exist. This relies on writing a class that extends one of two interfaces – ReadOnlyProperty or ReadWriteProperty.
Both of these interfaces define a method called getValue – which is used to supply the current value of the delegated property when it is read. This takes two arguments and returns the value of the property:
thisRef – a reference to the class that the property is in
property – a reflection description of the property being delegated
The ReadWriteProperty interface additionally defines a method called setValue that is used to update the current value of the property when it is written. This takes three arguments and has no return value:
thisRef – A reference to the class that the property is in
property – A reflection description of the property being delegated
value – The new value of the property
Let’s understand by String Trimming Example below as step by step .
Let’s create a custom class, let’s name it TrimAppendDelegate
To use this class as a Delegate, we have to implement the ReadWriteProperty<> interface
Once we implement the interface, we have to implement the abstract members in the interface which are getValue and setValue
Finally, we define a private variable of String type(since we are defining a custom Delegate for our String property) inside our Delegate class and define the getValue and setValue properties.
class TrimAppendDelegate : ReadWriteProperty<Any, String> {
private var trimAppendedString = ""
override fun getValue(thisRef: Any, property: KProperty<*>) = trimAppendedString
override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
trimAppendedString = "${value.trim()} is a String!"
}
}
}
//Usage :
private var trimmedString by TrimAppendDelegate()
trimmedString = "This.. "
println(trimmedString)
//Output : This.. is a String!
This is how we can achieve the property of Delegation.
How to use Kotlin Delegation Property with Android Development?
Using Built-in delegated property Lazy:
Most of the time, we see the usage of lateinit var in our UI(Activities/Fragments) classes or View Models. We can use the concept of the lazy delegate in place of lateinit var. The variable will be initialized the first time it is used.
Using Built-in delegated property Observable:
Most of our Android applications use Recycler views. We know that every recycler view is associated with its respective adapter. Every time the data structure (list of objects) changes in the adapter, we call notifyDataSetChanged to update our Recycler view.
We can use the inbuilt delegated property “Observable” to get the old and changed values of the data structure(list of objects)
private var users: ArrayList<User> by Delegates.observable(arrayListOf()) { property, oldValue, newValue ->
Log.d("Old value ${property.name} size", "${oldValue.size}")
Log.d("New value ${property.name} size", "${newValue.size}")
notifyDataSetChanged()
}
In the above code snippet, we can consider User as one of the model classes, and “users” is a list of User objects. We can access the old and new values whenever the value for “user” changes. Finally, we can call notifyDataSetChanged, if there is a change in oldValue and newValue by comparison.
To access the advantage of this Observable delegated property, the parameter should be a “var” instead of “val”. Else the changes cannot be identified, because, val, in Kotlin, is read-only.
Using Built-in delegated property Observable along with Lazy:
We can also use this “Observable” property along with “lazy” for updating our views. This can be very helpful if we are not using the concept of LiveData in our application.
Let’s understand the code snippet :
class MainActivity : AppCompatActivity() {
private val textView: TextView by lazy { textview }
private var text: String by Delegates.observable("") { _, _, newValue ->
textView.text = newValue
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
text = "NewText"
}
}
In the above code snippet, We have initiated textView with a lazy delegate just for our safety so that there will be no null pointer exception while accessing it. Whenever there is a change in the “text” variable, the text view is updated with the new value. This is similar to the concept of Live Data.
Using Built-in delegated property Lazy along with Custom Delegate:
Let’s say we want to save a boolean shared preference in our MainActivity.kt file (Any activity class file). Let’s create a custom delegate to store a Boolean shared preference value:
//Delegating the boolean preference saving option
class BooleanPreference(
private val preferences: Lazy<SharedPreferences>,
private val name: String,
private val defaultValue: Boolean
) : ReadWriteProperty<Any, Boolean> {
@WorkerThread
override fun getValue(thisRef: Any, property: KProperty<*>): Boolean {
return preferences.value.getBoolean(name, defaultValue)
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: Boolean) {
preferences.value.edit().putBoolean(name, value).apply()
}
}
We can see that our custom delegate class takes three parameters, the preferences instance, the name(Key in this case), and the default value.
So, let’s create our shared preferences global instance in our Activity file.
private val prefs: Lazy<SharedPreferences> = lazy { // Lazy to prevent IO access to main thread.
this.applicationContext.getSharedPreferences(
PREFS_NAME, MODE_PRIVATE
)
}
companion object {
const val PREFS_NAME = "Preferences"
const val TOGGLE_PREFS = "toggle"
}
Let’s say we are handling the preference of a toggle here in our Activity class. We can just use our custom delegate as follows:
private var togglePreference by BooleanPreference(prefs, TOGGLE_PREFS, false)
Now, let’s say if we want to change the value in the onCreate method(or any click listener, in general):
We just use it as a simple variable assignment. Looks clean and concise!
That’s all about in this article.
Conclusion
In this article, We understood about Property Delegation concepts in Kotlin. We have discussed about What is Property Delegation, Default Delegated Properties, create Custom Property Delegation and how to use Property delegation with Android Development. Property delegation is a powerful technique, that allows you to write code that takes over control of other properties, and helps this logic to be easily shared amongst different classes. This allows for robust, reusable logic that looks and feels like regular property access.
Thanks for reading ! I hope you enjoyed and learned about Property Delegation concepts in Kotlin. 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 and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.
Hello Readers, CoolMonkTechie heartily welcomes you in this article.
In this article, We will learn about iOS App Security. Mobile applications are the center of most peoples’ technology usage. They deal with a lot of private and sensitive user data like your personal health information or banking information. Protecting this data as well as possible is heavily important and the topic of this article.
We’ll discuss concrete techniques for making our iOS apps more secure. Our best practices cover means for securely storing data as well as sending & receiving data over the network. We’ll see why it is so hard to get security right and how we can improve our app security by using services from Apple and other providers.
We will focus on three main topics related to iOS App Security:
Storing user data safely.
Secure data transportation.
How to use Apple’s new cryptographic APIs.
A famous quote about Security and Learning is :
” Learning how to live with insecurity is the only security. “
So Let’s begin.
Chances are that our app handles private data that we don’t want to end up in the wrong hands. Therefore, we need to make sure to store this data safely and make data transportation as secure as possible.
Best Practices for Storing User Data
If we are developing iOS apps lots of security features are already provided by the OS. All iOS devices with an A7 processor or later also have a coprocessor called the Secure Enclave. It powers iOS security features in a hardware-accelerated way.
Apple’s App Sandbox
All apps running on iOS run in a sandbox to make sure the app can only access data which is stored in the app’s unique home directory. If an app wants to access data outside of its home directory it needs to use services provided by iOS, like the ones available for accessing iCloud data or the photo album. Therefore, no other app can read or modify data from our app.
Apple’s App Sandbox is powered by UNIX’s user permissions and makes sure that apps get executed with a less privileged “mobile” user. Everything outside the app’s home directory is mounted read-only. All system files and resources are protected. The available APIs don’t allow apps to escalate privileges in order to modify other apps or iOS itself.
For performing specific privileged operations an app needs to declare special entitlements. These entitlements get signed together with the app and are not changeable. Examples of services that need special entitlements are HealthKit or audio input. Some entitlements are even restricted to be only used if Apple gives you access to them. This includes services like CarPlay. They are stronger protected because misusing them could have fatal consequences.
Next to entitlements giving us special rights, apps can make use of the iOS extensions system. The OS has many points to be used by app extensions. App extensions are single-purpose executables bundled with the app. They run in their own address space and get controlled by the OS.
Additionally, iOS has methods to prevent memory-related security bugs. Address space layout randomization (ASLR) randomizes the assigned memory regions for each app on every startup. This makes the exploitation of memory-corruption-bugs much less likely. Also, memory pages are marked as non-executable with ARM’s Execute Never (XN) feature to stop malicious code from being executed.
Data Protection API
All iOS versions since iOS 4 have a built-in security feature called Data Protection. It allows an app to encrypt and decrypt the files stored in their app directory. The encryption and decryption processes are automatic and hardware-accelerated. Data Protection is available for file and database APIs, including NSFileManager, CoreData, NSData, and SQLite.
The feature is enabled by default but can be configured on a per-file basis to increase security. Every file can be configured to use one of 4 available protection levels. By default, all files are encrypted until the first user authentication but it might make sense to increase the protection level for certain data.
The four available protection levels include:
No protection: The file is always accessible and not encrypted at all
Complete until first user authentication: This is enabled by default and decrypts the file after the user unlocks their device for the first time. Afterward, the file stays decrypted until the device gets rebooted. Locking the device doesn’t encrypt the data again.
Complete unless open: The file is encrypted until the app opens the file for the first time. The decryption stays alive even when the device gets locked by the user.
Complete: The file is only accessible when the device is unlocked.
We can specify the protection level when we create files like this:
But we are also able to change the protection level of existing files by setting the resource values:
try (fileURL as NSURL).setResourceValue(
URLFileProtection.complete,
forKey: .fileProtectionKey)
It is important to understand which protection levels fits our needs. By default, we should use the highest protection level possible. However, if we need access to files in the background while the phone is locked we can’t use complete data encryption for them.
Keychain
The keychain is our secure place to store small chunks of data. It is a hardware-accelerated secure data storage that encrypts all of its contents. It is used by the system to store data like passwords and certificates but we as an app developer have also access to this data storage.
Our app or app group has its own space in the keychain and no other app has access to it. This way, we don’t need to store encryption keys in our app and can rely on the system to provide the highest level of security.
The keychain is the secure key-value storage replacement for NSUserDefaults. NSUserDefaults are not encrypted at all and should be avoided for sensitive data.
For every keychain item, we can define specific access policies for accessibility and authentication. We can require user presence (requesting Face ID or Touch ID unlock) or that the biometric ID enrolment didn’t change since the item was added to the keychain.
As an additional feature of the iOS Keychain, we can decide if we want to store the information in the local keychain which is only available on this specific device, or in the iCloud Keychain which gets synchronized across all Apple devices. This gives us the ability to share the information between our iPhone, iPad and Mac counterparts.
Even though the Keychain Services API should be used whenever possible, its interface is not exactly nice to use in Swift. If we plan to use such functionality more often in our codebase consider wrapping it with a nicer Swift API.
let query: [String: Any] = [
kSecClass as String: kSecClassInternetPassword,
kSecAttrAccount as String: account,
kSecAttrServer as String: server,
kSecValueData as String: password
]
let status = SecItemAdd(query as CFDictionary, nil)
This code snippet stores some credentials to the keychain. A lot of casting is necessary and in the end, we call SecItemAdd which synchronously stores the credentials and returns either a success or error status code.
Best Practices for Secure Data Transportation
Next to storing the user data safely, we should make sure that the communication between our app and its remote counterparts is secured. This prevents attackers from collecting private data by sniffing the network traffic or by running malicious servers.
HTTPs
Most network communication is done over the HTTP protocol between a client and a server. By default, HTTP connections are not encrypted. It is easily possible for attackers to sniff data from our local network or to perform man-in-the-middle attacks.
Since iOS 9, there is a new feature called App Transport Security (ATS). It improves the security of network communication in our apps. ATS blocks insecure connections by default. It requires all HTTP connections to be performed using HTTPS secured with TLS.
ATS can be configured in many ways to loosen up these restrictions. We can, therefore, allow insecure HTTP connections for specific domains or change the minimum TLS version used for HTTPS.
If our app contains an in-app browser we should use the NSAllowsArbitraryLoadsInWebContent configuration which allows our users to browse the web normally and still makes sure that all other network connections in our app use the highest security standards.
SSL Pinning
By default, HTTPS connections are verified by the system: it inspects the server certificate and checks if it’s valid for this domain. This should make sure the connected server is not malicious. However, there are still ways for attackers to perform more complex man-in-middle attacks.
The system certificate trust validation checks if the certificate was signed by a root certificate of a trusted certificate authority. To circumvent this security mechanism attackers would need to explicitly trust another malicious certificate in the user’s device settings or compromise a certificate authority. The attacker could then perform a man-in-the-middle attack to read all messages sent between client and server.
To prevent these kinds of attacks an app can perform additional trust validation of server certificates. This is called SSL or Certificate Pinning. We can implement SSL Pinning by including a list of valid certificates (or its public keys or its hashes) in our app bundle. The app can, therefore, check if the certificate used by the server is on this list and only then communicate with the server.
Implementing this validation from scratch should be avoided since implementation mistakes are very likely and lead to even more security vulnerabilities. We can recommend using an Open Source framework like TrustKit.
Introducing SSL Pinning, unfortunately, introduces the risk of bricking our app. Since we hardcode the trusted certificates, the app itself needs to be updated if a server certificate expires. To avoid such a situation, pin the future certificates in the client app before releasing new server certificates.
Push Notifications
To send push notifications to our users, we need to use Apple’s APNS services. If we want to use end-to-end encryption or if we just don’t want to give Apple the (theoretical) chance to read your messages you can use UNNotificationServiceExtension extensions to modify the messages on the client-side.
This allows us to send either encrypted messages to our clients or use placeholders for sensitive data. The messages will simply be used as a wakeup call for the app. The app can then decrypt the message or respectively for placeholder messages fetch the necessary information from the local device and replace the placeholders with the sensitive data. The clear text message will then be shown on the user’s lock screen without any sensitive information being sent to Apple’s servers.
In the following example, we can see how easily we can change our message content in our notification service extension.
End-to-end encryption is the “holy grail” for secure data transportation. It allows us to encrypt messages in a way that only the sender and receiver can decrypt them and neither Apple or our servers can read the cleartext data.
End-to-end encryption is not easy to implement and requires a lot of experience in cryptographic processes. If our team doesn’t have the experience it is very advisable to consult a third party expert helping with the implementation of the encryption mechanism.
CloudKit
If our app doesn’t need a server , we can use Apple’s CloudKit. CloudKit allows us to store data in iCloud containers while using our Apple ID as the login mechanism for our app. This way, we don’t need to implement all of these services on our own.
CloudKit was made with security in mind. As an app developer, we don’t have access to concrete user data and the communication is encrypted by default.
All the communication between our app and the server can be done using Apple’s client-side CloudKit framework. If we have an additional Android or Web app, we can still use CloudKit using its JavaScript and Web services.
To make this even better: CloudKit is completely free of charge to a certain amount. We can reach millions of users without fearing costs for traffic, data storage or requests.
Best Practices of Using Apple’s cryptographic APIs
The iOS SDK includes APIs to handle common cryptographic tasks for us. As we said above it is generally a good idea to rely on proven crypto implementations and not reimplement them ourself.
Apples CryptoKit
Apples CryptoKit is a new API that was introduced in iOS 13 and provides lower-level APIs to perform cryptographic operations or implement security protocols.
CryptoKit is based on top of more lower-level APIs. They were available before but introduced additional risk factors since developers often used them in a wrong way.
CryptoKit also allows us to use the SecureEnclave to get cryptographically safe functions that are performant and optimized for the device’s hardware.
If we want to support older iOS versions, we can use those lower-level APIs or use well known open-source third-party libraries like CryptoSwift.
Hashing data
Hash functions are functions that convert data of arbitrary size to fixed-size values. Good hash functions should minimize duplication of output values (so-called collisions) and be very fast to compute. Swift includes hashing functionality in the Swift Standard Library, but these functions are heavily focused on being fast and have a relative high collision rate. This makes them a good fit for performance-critical operations, but not so much for security related operations.
Securely hashing data is simple using CryptoKit. We just need to call the hash function on one of our Structs and choose a hash algorithm. CryptoKit supports the most common ones from SHA512 to SHA256.
let data = ...
let dataDigest = SHA512.hash(data: data)
let hashString = dataDigest.description
Authenticating Data using Message authentication codes
A message authentication code (MAC) is used to authenticate the sender of a message and confirm the integrity of that message. It allows the receiver to verify the origins of messages and detect changes to the message’s content.
To use this kind of integrity checks we can, for example, use CryptoSwift’s hashed authentication codes, also known as HMACs. The HMAC struct is a generic type that can be used with all the hash functions included in CryptoSwift.
To create a message authentication code , we can use this simple code snippet.
let key = SymmetricKey(size: .bits256)
let authenticationCode = HMAC<SHA256>.authenticationCode(for: messageData, using: key)
We can verify the message by using:
let isValid = HMAC<SHA256>.isValidAuthenticationCode(
Data(authenticationCode),
authenticating: messageData,
using: key
)
As we can see, we need to create a symmetric key first and securely share it between the sender and receiver.
Encrypting Data using symmetric keys
Encrypting and decrypting data using a symmetric key is simple, too. We can use one of two available ciphers: ChaChaPoly (ChaCha20-Poly1305) or AES-GCM in CryptoSwift:
let encryptedData = try! ChaChaPoly.seal(data, using: key).combined
let sealedBox = try! ChaChaPoly.SealedBox(combined: encryptedData)
let decryptedData = try! ChaChaPoly.open(sealedBox, using: key)
We should always make sure not to hardcode these symmetric keys in our app though. We can generate a symmetric key at runtime and then store it safely in the keychain. That way, no one has access to our key to decrypt data.
Performing Key Agreement
In a lot of cases, we will need to perform some form of key exchange to exchange cryptographic keys over an insecure channel. With key agreement protocols like Elliptic-curve Diffie–Hellman (ECDH), we can derive a common secret.
1. Create private/public key-pairs for Alice and Bob
let alicePrivateKey = P256.KeyAgreement.PrivateKey()
let alicePublicKey = alicePrivateKey.publicKey
let bobPrivateKey = P256.KeyAgreement.PrivateKey()
let bobPublicKey = bobPrivateKey.publicKey
Both Alice and Bob create their own private/public key-pairs and share their public keys.
2. Deriving shared secret
let aliceSharedSecret = try! alicePrivateKey.sharedSecretFromKeyAgreement(with: bobPublicKey)
let bobSharedSecret = try! bobPrivateKey.sharedSecretFromKeyAgreement(with: alicePublicKey)
We were now able to derive a common secret by using the own private key together with the counterpart’s public key. aliceSharedSecret and bobSharedSecret are now the same.
3. The created secret number should not be used as an encryption key by itself. Instead it can be used to generate a much larger and more secure encryption key using HKDF or X9.63 key derivation.
let usedSalt = "Secure iOS App".data(using: .utf8)!
let symmetricKey = aliceSharedSecret.hkdfDerivedSymmetricKey(
using: SHA256.self,
salt: protocolSalt,
sharedInfo: Data(),
outputByteCount: 32
)
That’s generally how you could implement it! Also note that Apple has an implementation of Diffie-Hellman in iOS as part of Secure Transport.
Creating and Verifying Signatures
If we want to send messages and make sure that the sender is the person we thought it is we can use signatures. To do so, we need a private/public key pair first.
let signingKey = Curve25519.Signing.PrivateKey()
let signingPublicKey = signingKey.publicKey
Using the private key any form of data can be signed.
let data = ...
let signature = try! signingKey.signature(for: data)
This signature is then sent together with the actual data to the receiver which can use the public key to validate the signature.
let isSignatureValid = signingPublicKey.isValidSignature(signature, for: data)
That’s all about in this article.
Conclusion
In this article, We understood about Apple’s iOS security Best Practices. Creating secure and robust iOS applications is not easy. However, we can improve security in our iOS apps without much effort by sticking to best practices. Protecting user data must be a high priority and should never be ignored.
Thanks for reading ! I hope you enjoyed and learned about Apple’s iOS security Best Practices. 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 and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.
Hello Readers, CoolMonkTechie heartily welcomes you in this article.
In this article, We will learn about five major ways of making network request in Swift. Many iOS application are clients of a REST API. This article gives an overview of common ways to accomplish tasks associated with making HTTP requests and handling responses.
A famous quote about learning is :
” Change is the end result of all true learning. “
So Let’s begin.
First we need to understand that how to threadingrelates from network requests.
How does threading relate from network requests?
In iOS much of the code that runs in your application is triggered by an event on the main event loop. The main event loop is responsible for executing code to respond to things like user interaction (e.g triggering an @IBAction) or events in a view controller’s lifecycle (e.g. viewDidLoad). Code executed from the main event loop is run on the main thread. This is convenient for us because any updates to an application’s UI elements must happen on the main thread. We’ll want to keep this rule in mind when working with network requests.
iOS provides a couple of higher level libraries for concurrent programming: Grand Central Dispatch and NSOperationQueue. We’ll be able use either to ensure us that a piece of code does or does not run on the main thread.
We should never make a synchronous network request on the main thread since this will block thread and UI will appear frozen while our request is pending. We’ll rarely run into instances where we’ll need to make synchronous requests.
When we make an asynchronous request, any of the above libraries will execute the request on a background (i.e. not the main) thread. Some methods will allow us to specify the dispatch queue on which we want the response handler to run, others will provide no guarantees. If we need to update the UI in our response handler we must ensure that the code that manipulates the UI is run on the main thread. This can be tricky because we may call into a method that calls into another method that after a long stack of calls eventually updates a UI element.
One simple way to ensure a block of code is run on the main thread using Grand Central Dispatch is as follows:
DispatchQueue.main.async {
// This code will be executed on the main thread
}
Overview of popular network programming methods
There is a wide variety of ways to make HTTP requests in iOS with which we might at least want to be familiar. We will discuss the below 4 major ways of making network requests in network programming :
1. NSURLConnection
NSURLConnection is a lower level mechanism to make URL requests and is part of Apple’s Foundation Framework.
simplest way to make URL request.
provides synchronous and asynchronous requests via with completion handler blocks and delegates.
does not have much support for authentication or a session concept.
does not have an “operation” or “task” concept associated with requests so there’s no convenient way to handle queue of requests or to pause/resume.
does not handle parsing of common content types.
not much built in support for HTTP error codes / request parameters.
deprecated in iOS 9.
Example
Notice that we are forced to specify a operation queue on which the completion handler will run.
import UIKit
private let apiKey = "53eb9541b4374660d6f3c0001d6249ca:19:70900879"
private let resourceUrl = NSURL(string: "http://api.nytimes.com/svc/topstories/v1/home.json?api-key=\(apiKey)")!
class Story {
var headline: String?
var thumbnailUrl: String?
init(jsonResult: NSDictionary) {
...
}
class func fetchStories(successCallback: ([Story]) -> Void, error: ((NSError?) -> Void)?) {
let request = NSURLRequest(URL: resourceUrl)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response, data, requestError) -> Void in
if let requestError = requestError? {
error?(requestError)
} else {
if let data = data? {
let json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as NSDictionary
if let results = json["results"] as? NSArray {
var stories: [Story] = []
for result in results as [NSDictionary] {
stories.append(Story(jsonResult: result))
}
successCallback(stories)
}
} else {
// unexpected error happened
error?(nil)
}
}
}
}
}
2. URLSession
NSURLSession a higher level library that is part of Apple’s Foundation Framework.
built on top of NSURLConnection
better support for authentication and has a session concept
concept of “task” enables pausing/resuming requests
can perform requests while your app is in the background
does not handle parsing of common content types
not much built in support for HTTP error codes / request parameters
URLSession is now the preferred built-in method of performing network requests on iOS.
Example
class Movie {
// ...
class func fetchMovies(successCallBack: @escaping (NSDictionary) -> (), errorCallBack: ((Error?) -> ())?) {
let apiKey = "Put_Your_Client_Id_Here"
let url = URL(string: "https://api.themoviedb.org/3/movie/now_playing?api_key=\(apiKey)")!
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 10)
let session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main)
let task: URLSessionDataTask = session.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if let error = error {
errorCallBack?(error)
} else if let data = data,
let dataDictionary = try! JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary {
//print(dataDictionary)
successCallBack(dataDictionary)
}
}
task.resume()
}
// ...
}
3. Codable
Codable is Apple’s latest powerful contribution to efforts to better improve the built-in networking libraries available to iOS and Mac OS engineers. Codable is actually a typealias for Encodable and Decodable protocols that allows us to quickly decode and encode external representations (such as JSON strings) as native Structs in Swift.
Before Codable was introduced to the Swift language in Swift 4, many developers had to rely on third party frameworks or building their own JSON decoding code which required a lot of boilerplate code. However, with the introduction of Codable, it’s actually really easy to write 100% Swift networking code! Let’s give it a try!
Example
Let’s assume we are implementing a movie list viewing application that retrieves a list of movies from a server to show the user, with the following JSON response.
With the power of Codable, you can implement a native Struct object with the following code.
struct MovieResponse: Codable {
let totalFilms: Int
let films: [Film]
}
struct Film: Codable {
let id: Int
let imageURL, title: String
let score: Double
enum CodingKeys: String, CodingKey {
case id
case imageURL = "image_url"
case title, score
}
}
As we can see, the Struct looks almost exactly like we would want it to look if we were using it to drive a UITableViewDataSource or a custom film details View Controller.
The CodingKey is simply an enum that allows the JSONDecoder to perform an internal switch on each key of the JSON response in order to match each key to the property name of the Struct.
Can we guess why imageURL is the only case in the enum that has a declared raw value? If we’re thinking it’s related to Snake Case, we’re right! While Snake Case works in Swift, it’s not best practice, and Apple cleverly considered that an application might need properties to have different names compared to their network properties so an engineer can overwrite the property name by mapping a different variable name to each JSON parameter. If we wanted, we could make imageURL read “thumbnailURL” instead, as long as the encoding key is equal to “image_url”, the JSONDecoder will know that the JSON value for key “image_url” is set to thumbnailURL.
So how do we use it? Easy!!
Let’s go back to the example from URLSession, and instead of a general NSDictionary (which would require a lot more code on the consumption side like a MovieObject class with init(fromDict dict: NSDictionary) in order to be usable in your code base), let’s substitute it with our Codable compatible struct.
class Movie {
// ...
class func fetchMovies(successCallBack: @escaping ([Film]?) -> (), errorCallBack: ((Error?) -> ())?) {
let apiKey = "Put_Your_Client_Id_Here"
let url = URL(string: "https://api.themoviedb.org/3/movie/now_playing?api_key=\(apiKey)")!
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 10)
let session = URLSession(configuration: .default, delegate: nil, delegateQueue: OperationQueue.main)
let task: URLSessionDataTask = session.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if let error = error {
errorCallBack?(error)
} else if let data = data,
let filmResponse = try! JSONDecoder().decode(MovieResponse.self, from: data) {
//print(filmResponse.films)
successCallBack(filmResponse.films)
}
}
task.resume()
}
// ...
}
It might not look like much, but a significant amount of code is saved from the Movie class object, and when we consume this API call, on the other side we’ll get the Film objects we need to drive our UI, rather than a Dictionary you’d have to iterate over, verifying each value.
Codable can save us a significant amount of time in writing networking code, and it’s growing in popularity, so we highly recommend picking it up!!
4. AFNetworking
AFNetworking is the most popular library for and is the de facto gold standard for networking tasks in the iOS world. Chances are we will want to use this library if accessing an API and making network requests is a key part of your application.
built on top of NSURLSession.
built-in support for parsing common content-types.
great support for common HTTP operations including handling of request params, headers, error codes.
great integration with UIKit components making complex behavior like loading remote images asynchronously very easy.
Example
This code starts to look a little cleaner with AFNetworking. AFNetworking does some error handling for us and gives us a way to provide a failure handler. Also note that we no longer have to parse the JSON result ourselves since AFNetworking does this automatically based on the content type. Finally note that we were able to supply our GET parameters as a Swift dictionary. This is not particularly useful here, but becomes very nice to have if there is a large number of parameters.
private let params = ["api-key": "53eb9541b4374660d6f3c0001d6249ca:19:70900879"]
private let resourceUrl = "http://api.nytimes.com/svc/topstories/v1/home.json"
class Story {
var headline: String?
var thumbnailUrl: String?
init(jsonResult: NSDictionary) {
...
}
class func fetchStories(successCallback: ([Story]) -> Void, error: ((NSError?) -> Void)?) {
let manager = AFHTTPRequestOperationManager()
manager.GET(resourceUrl, parameters: params, success: { (operation ,responseObject) -> Void in
if let results = responseObject["results"] as? NSArray {
var stories: [Story] = []
for result in results as [NSDictionary] {
stories.append(Story(jsonResult: result))
}
successCallback(stories)
}
}, failure: { (operation, requestError) -> Void in
if let errorCallback = error? {
errorCallback(requestError)
}
})
}
}
5. AlamoFire
AlamoFire is another networking library by the same author as AFNetworking. It is written in Swift.
Swift only
many of the same features as AFNetworking
easy to use/read syntax for making common requests
no integration with UIKit
Example
This example demonstrates that how to use Alamofire library using Swift Code in iOS.
import UIKit
import Alamofire
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
var jsonArray:NSMutableArray?
var newArray: Array<String> = []
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "https://rocky-meadow-1164.herokuapp.com/todo") .responseJSON { response in
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result) // result of response serialization
if let JSON = response.result.value {
self.jsonArray = JSON as? NSMutableArray
for item in self.jsonArray! {
print(item["name"]!)
let string = item["name"]!
print("String is \(string!)")
self.newArray.append(string! as! String)
}
print("New array is \(self.newArray)")
self.tableView.reloadData()
}
}
// Do any additional setup after loading the view, typically from a nib.
}
}
That’s all about in this article.
Conclusion
In this article, We understood about 5 Popular Ways Of Making Network Requests In Swift. We have discussed the common ways to accomplish tasks associated with making HTTP requests and handling responses in Swift.
Thanks for reading ! I hope you enjoyed and learned about the Overview of popular network programming methods in Swift. 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 and support us in any way possible. Also like and share the article with others for spread valuable knowledge.
If you have any comments, questions, or think I missed something, feel free to leave them below in the comment box.