<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Bugfender</title>
    <description>The latest articles on Forem by Bugfender (@bugfenderapp).</description>
    <link>https://forem.com/bugfenderapp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F12285%2F68f3bdb9-79fe-49db-a0aa-b63660cb35b2.png</url>
      <title>Forem: Bugfender</title>
      <link>https://forem.com/bugfenderapp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bugfenderapp"/>
    <language>en</language>
    <item>
      <title>Android Geofencing: How to Set Up and Troubleshoot Location Triggers</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Thu, 29 May 2025 05:40:44 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/android-geofencing-how-to-set-up-and-troubleshoot-location-triggers-5187</link>
      <guid>https://forem.com/bugfenderapp/android-geofencing-how-to-set-up-and-troubleshoot-location-triggers-5187</guid>
      <description>&lt;p&gt;You’re probably familiar with Android Geofencing already, but in case this is your first rodeo, it’s a location-based system that can receive access to a mobile device’s location data, and trigger all kinds of functions when users pass in and out of specific areas. The ads our users see, the content they can stream, the offers they receive via push notifications… all can be triggered by geofencing.&lt;/p&gt;

&lt;p&gt;Geofencing and location updates can be tricky, particularly for those who are still &lt;a href="https://bugfender.com/guides/android-app-development-guide/" rel="noopener noreferrer"&gt;starting out in Android app development&lt;/a&gt;. So in this article we’re going to unpack it for you, looking specifically at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to set up Android Geofencing.&lt;/li&gt;
&lt;li&gt;How to create an Android Geofence API.&lt;/li&gt;
&lt;li&gt;How to handle geofence transitions.&lt;/li&gt;
&lt;li&gt;How to deal with edge cases and unexpected problems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And loads of other stuff around geofence monitoring. By the end of the article, you should have a sound working knowledge of geofencing, which you can take into your day-to-day work as an Android developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;First, can you give me a bit more context?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Why sure.&lt;/p&gt;

&lt;p&gt;Android Geofencing allows a mobile app to define a circular, virtual boundary with the three parameters of latitude, longitude and radius. This. in turn, allows the creation of a &lt;a href="https://dev.to/bugfenderapp/android-push-notifications-step-by-step-guide-2l6"&gt;notification&lt;/a&gt; when the &lt;a href="https://dev.to/bugfenderapp/the-best-resources-to-learn-android-development-jgm"&gt;mobile&lt;/a&gt; device enters, exits or stays within the geofence area defined by these parameters.&lt;/p&gt;

&lt;p&gt;Geofencing is activated through the &lt;code&gt;GeofencingClient&lt;/code&gt; API. The Google Play Service provides location tracking, and Google Maps provides geofence visualization. The location of the Android device is established through GPS, cellular networks and WiFi.&lt;/p&gt;

&lt;p&gt;A key point to note here is that Google Maps does not work with the geofencing app system directly. Instead, it indicates the device’s location on a map, as the following code demonstrates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              // Initialize Geofencing Client

var geofencingClient: GeofencingClient = LocationServices.getGeofencingClient(this)

                   // Define a geofence

val geofence = Geofence.Builder()
            .setRequestId("MyGeofence")
            .setCircularRegion(37.7749, -122.4194, 500)
            .setExpirationDuration(Geofence.NEVER_EXPIRE)
            .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
            .build()



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Now, how do we create Geofencing in an Android application?
&lt;/h2&gt;

&lt;p&gt;Ok, let’s get into it.&lt;/p&gt;

&lt;p&gt;As we mentioned earlier, we can use Google Play services to create geofencing technology in an Android mobile app. But how do we go about implementing it?&lt;/p&gt;

&lt;p&gt;Well to get started, we’ll need to add Google Play service dependencies in the &lt;code&gt;build.gradle&lt;/code&gt; file provided via Android Studio, and sync the project. Google Maps is optional, so if we need to visualize a device’s location information, or indicate where a geofenced area is on a map, we’ll need to go ahead and add Google Maps specifically. Here’s some code to show you how:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
dependencies {
    implementation 'com.google.android.gms:play-services-location:21.0.1' 
    implementation 'com.google.android.gms:play-services-maps:18.1.0'   
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s add location services and background permission in the &lt;code&gt;AndroidManifest.xml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/&amp;gt;
&amp;lt;uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/&amp;gt;
&amp;lt;uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Runtime permission is also required on Android OS 6.0 API onwards.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val myRequest= registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -&amp;gt;
                if (permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true) {
                    Log.d("MyGeofence", "permission granted")
                } else {
                    Log.e("MyGeofence", "permission denied")
                }
            }
        myRequest.launch(
            arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
        )

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great, now we’re cooking! Let’s initialize the Geofencing Client API and define the Geofence object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Initialize Geofencing Client

var gClient: GeofencingClient = LocationServices.getGeofencingClient(this)              



     // Define a geofence

val geofence = Geofence.Builder()
            .setRequestId("MyGeofence")
            .setCircularRegion(37.7749, -122.4194, 500)
            .setExpirationDuration(Geofence.NEVER_EXPIRE)
            .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
            .build()


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, we’re nearly done. But next, we need to define location-sensitive requests and pending intent. This is required to trigger an event when the geofencing Android app enters or exits a geofenced location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     // Create Geofencing Request

 val gRequest = GeofencingRequest.Builder()
            .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
            .addGeofence(geofence)
            .build()

      // Create Pending Intent 

        val intent = Intent(this, MyGeofenceBroadcastReceiver::class.java)
        val pIntent = PendingIntent.getBroadcast(
            this, 0, intent , PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
        )

        // Add Geofence

       gClient.addGeofences(gRequest , pIntent )
            .addOnSuccessListener {
                Log.d("MyGeofence", "successfully added")
            }
            .addOnFailureListener { e -&amp;gt;
                Log.e("MyGeofence", "Failed to add", e)
            } 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, we need to create a broadcast receiver class to receive pending intent, and notify the user when they enter or exit the geofenced area.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyGeofenceBroadcastReceiver: BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val myGeofencingEvent = GeofencingEvent.fromIntent(intent)
        if (myGeofencingEvent.hasError()) {
            Log.e("MyGeofenceReceiver", "error: ${geofencingEvent.errorCode}")
            return
        }

        when (myGeofencingEvent.geofenceTransition) {
            Geofence.GEOFENCE_TRANSITION_ENTER -&amp;gt; Log.d("MyGeofenceReceiver", "Entered!")
            Geofence.GEOFENCE_TRANSITION_EXIT -&amp;gt; Log.d("MyGeofenceReceiver", "Exited!")
            else -&amp;gt; Log.d("MyGeofenceReceiver", "Unknown")
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ok, awesome. Now, let’s create a geofence transition in our app
&lt;/h2&gt;

&lt;p&gt;When we talk about a geofence transition, we’re talking about when we define a circle with a certain radius on a map. It triggers when the mobile device enters, exits or stays within the boundary of the circle. This is something that Android app developers often struggle with, but really it’s pretty simple.&lt;/p&gt;

&lt;p&gt;The geofencing API helps to create transitions, and triggers can be handled through the &lt;code&gt;BroadcastReceiver&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyGBR: BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val myGf = GeofencingEvent.fromIntent(intent)

        when (myGf.geofenceTransition) {
            Geofence.GEOFENCE_TRANSITION_ENTER -&amp;gt; Log.d("MyGBR", "Entered!")
            Geofence.GEOFENCE_TRANSITION_EXIT -&amp;gt; Log.d("MyGBR", "Exited!")
            Geofence.GEOFENCE_TRANSITION_DWELL -&amp;gt; Log.d("MyGBR", "Dwell!")
            else -&amp;gt; Log.d("MyGBR", "Unknown")
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we need to register &lt;code&gt;BroadcastReceiver&lt;/code&gt; class in &lt;code&gt;AndroidManifest.xml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;receiver android:name=".MyGBR"
    android:exported="false"&amp;gt;
    &amp;lt;intent-filter&amp;gt;
        &amp;lt;action android:name="com.google.android.gms.location.GeofenceTransition"/&amp;gt;
    &amp;lt;/intent-filter&amp;gt;
&amp;lt;/receiver&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ok, let’s look at how to create an Android geofence API
&lt;/h2&gt;

&lt;p&gt;Here, we will create a geofence API with the help of Node.js and Firebase (remember that Firebase is used to store data). We will need to use the Restful API in our Android application later.&lt;/p&gt;

&lt;p&gt;First, let’s create a directory and install node.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
mkdir geofence-api
cd geofence-api
npm init -y


npm install express firebase-admin cors body-parser

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it’s time to create a Firebase Store project and download the &lt;code&gt;serviceAccountKey.json&lt;/code&gt; file. Then, we can incorporate this file into the project directory, and create a &lt;code&gt;server.js&lt;/code&gt; file for backend code.&lt;/p&gt;

&lt;p&gt;To get things going, let’s create a Restful API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require("express");
const admin = require("firebase-admin");
const bodyParser = require("body-parser");
const cors = require("cors");

const app = express();
app.use(cors());
app.use(bodyParser.json());

admin.initializeApp({
  credential: admin.credential.cert(require("./serviceAccountKey.json")),
});

const db = admin.firestore();
const geofenceCollection = db.collection("geofences");

// Add a new geofence
app.post("/create-geofence", async (req, res) =&amp;gt; {
  try {
    const { id, latitude, longitude, radius } = req.body;
    await geofenceCollection.doc(id).set({
      latitude,
      longitude,
      radius,
    });
    res.status(200).json({ message: "Geofence created successfully!" });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Get all geofences
app.get("/geofences", async (req, res) =&amp;gt; {
  try {
    const snapshot = await geofenceCollection.get();
    const geofences = snapshot.docs.map(doc =&amp;gt; ({ id: doc.id, ...doc.data() }));
    res.status(200).json(geofences);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Remove a geofence
app.delete("/delete-geofence/:id", async (req, res) =&amp;gt; {
  try {
    const { id } = req.params;
    await geofenceCollection.doc(id).delete();
    res.status(200).json({ message: "Geofence deleted successfully!" });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Start server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () =&amp;gt; console.log(`Server running on port ${PORT}`));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice, clean code right? Note that the endpoint of the API will be &lt;code&gt;http://your-server-ip:5000/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Android geofencing using Google Maps
&lt;/h2&gt;

&lt;p&gt;Next, we need to implement Google Maps in our app to test the geofencing function. This is a really important part of the process, as geofencing can throw up lots of edge cases.&lt;/p&gt;

&lt;p&gt;Let’s initialize the map in Kotlin code; the &lt;code&gt;SupportFragmentMagaer&lt;/code&gt; class will provide an instance of &lt;code&gt;MapFragment&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
class MyMapActivity : AppCompatActivity(), OnMapReadyCallback {

    private lateinit var gfClient: GeofencingClient
    private lateinit var gMap: GoogleMap

    private val gfLatLng = LatLng(37.7749, -122.4194)
    private val gfRadius = 500f // 500 meters
    private val gfId = "MyMapActivity"

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

        // Initialize Geofencing Client
        gfClient = LocationServices.getGeofencingClient(this)

        // Load the Google Map
        val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
        mapFragment.getMapAsync(this)

        // check permissions
        checkPermission()
    }

    override fun onMapReady(map: GoogleMap) {
        gMap = map
        gMap.uiSettings.isZoomControlsEnabled = true

        // Add marker and circle for geofence
        gMap.addMarker(MarkerOptions().position(gfLatLng).title("Geofence Location"))
        gMap.addCircle(
            CircleOptions()
                .center(gfLatLng)
                .radius(gfRadius.toDouble())
                .strokeColor(0xFFFF0000.toInt()) // Red outline
                .fillColor(0x22FF0000) // Transparent fill
        )

        gMap.moveCamera(CameraUpdateFactory.newLatLngZoom(gfLatLng, 15f))

        // Set up geofencing
        createGeofence()
    }

    private fun createGeofence() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            Log.e("MyMapActivity", "Location permission not granted")
            return
        }

        val geofence = Geofence.Builder()
            .setRequestId(gfId)
            .setCircularRegion(gfLatLng.latitude, gfLatLng.longitude, gfRadius)
            .setExpirationDuration(Geofence.NEVER_EXPIRE)
            .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
            .build()

        val gfRequest = GeofencingRequest.Builder()
            .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
            .addGeofence(geofence)
            .build()

        val intent = Intent(this, MyGBR::class.java)
        val pIntent = PendingIntent.getBroadcast(
            this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
        )

        gfClient.addGeofences(gfRequest, pIntent)
            .addOnSuccessListener {
                Log.d("MyMapActivity", "Geofence added successfully")
            }
            .addOnFailureListener { e -&amp;gt;
                Log.e("MyMapActivity", "Failed to add geofence", e)
            }
    }

    private fun checkPermission() {
        val requestPermissionLauncher =
            registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -&amp;gt;
                if (permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true) {
                    Log.d("MyMapActivity", "Location permission granted")
                } else {
                    Log.e("MyMapActivity", "Location permission denied")
                }
            }
        requestPermissionLauncher.launch(
            arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
        )
    }
}


class MyGBR: BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val myGf = GeofencingEvent.fromIntent(intent)

        when (myGf.geofenceTransition) {
            Geofence.GEOFENCE_TRANSITION_ENTER -&amp;gt; Log.d("MyGBR", "Entered!")
            Geofence.GEOFENCE_TRANSITION_EXIT -&amp;gt; Log.d("MyGBR", "Exited!")
            Geofence.GEOFENCE_TRANSITION_DWELL -&amp;gt; Log.d("MyGBR", "Dwell!")
            else -&amp;gt; Log.d("MyGBR", "Unknown")
        }
    }
}



&amp;lt;receiver android:name=".MyGBR"
    android:exported="false"&amp;gt;
    &amp;lt;intent-filter&amp;gt;
        &amp;lt;action android:name="com.google.android.gms.location.GeofenceTransition"/&amp;gt;
    &amp;lt;/intent-filter&amp;gt;
&amp;lt;/receiver&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the fun bit; we can open the emulator and run the code.&lt;/p&gt;

&lt;p&gt;Click on the ‘More’ section with the three-dot icon, then move to the location information section and enter the above latitude and longitude. Remember to &lt;a href="https://bugfender.com/remote-logging/" rel="noopener noreferrer"&gt;check the logcat&lt;/a&gt; after entering the geofenced area, and again when exiting.&lt;/p&gt;

&lt;h3&gt;
  
  
  What if Android geofencing does not trigger?
&lt;/h3&gt;

&lt;p&gt;If moving in or out of the geofenced area is not triggering an event, you’ll need to troubleshoot the &lt;a href="https://dev.to/bugfenderapp/mastering-exception-handling-in-kotlin-a-comprehensive-guide-88l"&gt;error&lt;/a&gt;. This will likely have arisen from one or more of the following issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whether device location permission granted or not.&lt;/li&gt;
&lt;li&gt;Whether the latest Google Play service installed or not (if not, be sure to update).&lt;/li&gt;
&lt;li&gt;Whether GPS is enabled with an option for high location accuracy.&lt;/li&gt;
&lt;li&gt;Whether the &lt;code&gt;PendingIntent&lt;/code&gt; is properly used with correct flags.&lt;/li&gt;
&lt;li&gt;Whether the radius is correct (if not, increase and then test again).&lt;/li&gt;
&lt;li&gt;Whether geofence permissions have expired (check geo fence expiry and transition type).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, be sure to ceck logcat messages and &lt;a href="https://bugfender.com/crash-reporting/" rel="noopener noreferrer"&gt;errors&lt;/a&gt;, and debug each line with a mock location.&lt;/p&gt;

&lt;p&gt;If everything is working fine and set up correctly, restart the device and try again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Android Geofencing FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Android Geofencing and how does it work?
&lt;/h3&gt;

&lt;p&gt;Android Geofencing uses latitude, longitude, and radius to define a virtual area. When a device enters or exits this area, it triggers events through the GeofencingClient API.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I set up Android Geofencing in an app?
&lt;/h3&gt;

&lt;p&gt;To set up Android Geofencing, add location permissions, initialize the GeofencingClient, create a geofence, and use a BroadcastReceiver to handle transitions.&lt;/p&gt;

&lt;h3&gt;
  
  
  What permissions are needed for Android Geofencing?
&lt;/h3&gt;

&lt;p&gt;You need ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, and ACCESS_BACKGROUND_LOCATION permissions, plus runtime handling for Android 6.0 and above.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I visualize a geofence on Google Maps?
&lt;/h3&gt;

&lt;p&gt;To show a geofence on the map, draw a marker and circle using the Google Maps API. This gives users a visual of the geofenced area.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do geofence transitions work in Android?
&lt;/h3&gt;

&lt;p&gt;Geofence transitions are triggered when the user enters, exits, or dwells in the set area. You handle these using the GeofencingEvent inside a BroadcastReceiver.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a PendingIntent in geofencing and why is it used?
&lt;/h3&gt;

&lt;p&gt;A PendingIntent lets the geofencing service send events to your app, even when it’s not running. It links transition triggers to your logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I build a backend geofence API using Node.js and Firebase?
&lt;/h3&gt;

&lt;p&gt;You can build a backend using Express and Firebase to store and manage geofence data. These APIs connect with the app to create or remove geofences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is my Android geofence not triggering?
&lt;/h3&gt;

&lt;p&gt;Check permissions, GPS status, radius size, &lt;a href="https://dev.to/bugfenderapp/google-play-store-policy-changes-2024-4cib-temp-slug-1888313"&gt;Google Play&lt;/a&gt; Services updates, and your PendingIntent setup. Use logcat for deeper debugging.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s the best geofence radius for Android apps?
&lt;/h3&gt;

&lt;p&gt;Use a radius between 100 to 500 meters. It balances accuracy and performance across most devices and network conditions.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can I test Android geofencing in the emulator?
&lt;/h3&gt;

&lt;p&gt;Use the &lt;a href="https://dev.to/bugfenderapp/android-emulator-setup-guide-for-app-testing-4018"&gt;emulator’s location settings to send test&lt;/a&gt; coordinates. Monitor the logcat to confirm whether the geofence was entered or exited.&lt;/p&gt;

&lt;h2&gt;
  
  
  And we’re done!
&lt;/h2&gt;

&lt;p&gt;Well, nearly. No coding article would be complete without some take-home points, so here are some key things to remember when testing the geofencing function of an Android app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The radius should be defined between 100 and 500 meters for better performance.&lt;/li&gt;
&lt;li&gt;High Accuracy mode must be selected for fast location-tracking.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PendingIntent.FLAG_MUTABLE&lt;/code&gt; must be used for Android 12+ device testing.&lt;/li&gt;
&lt;li&gt;Test a mock location, to ensure it is working properly in the emulator, before testing the real app.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we &lt;a href="https://bugfender.com/platforms/android/" rel="noopener noreferrer"&gt;are still encountering errors&lt;/a&gt;, we can register geofences using &lt;code&gt;GeofencingClient&lt;/code&gt; and check the event in the &lt;code&gt;BroadcastReceiver&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Any other questions? Just write to us. We’re always happy to chat Android Geofencing, or any other Android coding topic. So whatever questions you’ve got, hit us up.&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
      <category>android</category>
      <category>java</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>How to Use Android Vitals to Improve App Stability and Google Play Ranking</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Tue, 20 May 2025 08:25:47 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/how-to-use-android-vitals-to-improve-app-stability-and-google-play-ranking-210l</link>
      <guid>https://forem.com/bugfenderapp/how-to-use-android-vitals-to-improve-app-stability-and-google-play-ranking-210l</guid>
      <description>&lt;p&gt;If you want to run a health-check on &lt;a href="https://bugfender.com/guides/android-app-development-guide/" rel="noopener noreferrer"&gt;your Android app&lt;/a&gt;, Android Vitals is a great piece of kit.&lt;/p&gt;

&lt;p&gt;Developed by Google and available on the Google Play console, this performance and stability checking tool helps us fix issues proactively, improve the technical quality of our apps, and avoid uninstalls. Crucially, it will also improve our Google Play ranking, because Google Play ranks app quality using Android Vitals data.&lt;/p&gt;

&lt;p&gt;And the really great news? We don’t need to write any code to use Android Vitals; Google automatically collects data from our app and shows the relevant metric in the Play Console. However, if we want to &lt;em&gt;really&lt;/em&gt; maximize Android Vitals, we need to do a bit more digging.&lt;/p&gt;

&lt;p&gt;So over the next few hundred words, we’re going to open up Android Vitals, looking at stuff like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How we set up Android Studio.&lt;/li&gt;
&lt;li&gt;How we can maximize the benefits of Google Play.&lt;/li&gt;
&lt;li&gt;How we deal with edge cases and common technical issues.&lt;/li&gt;
&lt;li&gt;How can we achieve &lt;a href="https://bugfender.com/blog/effective-tips-to-increase-your-app-store-rating/" rel="noopener noreferrer"&gt;app store&lt;/a&gt; optimization by monitoring core vitals proactively.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The article should be ideal for any developer who is new to Android Vitals and wants a working knowledge. It’s a topic that you can get to grips with quickly, so by the end of this article you should be ready to fly.&lt;/p&gt;

&lt;h2&gt;
  
  
  But first, some core concepts to understand
&lt;/h2&gt;

&lt;p&gt;As an Android Vitals rookie, there are some acronyms you’ll need to grasp. Here are three that every Android developer should know.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Application Not Responding (ANR) rate&lt;/strong&gt; shows the percentage of sessions where the app freezes.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;LMK (low memory kill) rate&lt;/strong&gt; shows the percentage of foreground service.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;crash rate&lt;/strong&gt; shows the percentage of user-perceived &lt;a href="https://bugfender.com/crash-reporting/" rel="noopener noreferrer"&gt;crashes&lt;/a&gt; of active users on a daily basis.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And there’s some other core stuff that will come up when we get our Android Vitals reports.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start-up times.&lt;/strong&gt; When the app initially starts and calls the &lt;code&gt;onCreate()&lt;/code&gt; method, it has three metric data points: &lt;strong&gt;cold start&lt;/strong&gt; (when the app is relaunched after running in the background for a while), &lt;strong&gt;warm start&lt;/strong&gt; (when the app is brought to the foreground from the background), and &lt;strong&gt;hot start&lt;/strong&gt; (when the app’s activity is already in the foreground).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rendering UI.&lt;/strong&gt; This refers to the rate at which a frame is generated from our app, and displayed upon the screen. If our apps have a UI refresh rate below 60 FPS, this is termed excessive slow frames. If a frame takes more than 700 ms to render, this is called excessive frozen frames.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Battery Usage:&lt;/strong&gt; This provides a metric for how much battery life is being drained in different sessions. The key sessions are app wakeup, wake locks (when the app carries on running when the screen is off), background WiFi scans and background network usage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission:&lt;/strong&gt; This shows how many permissions we are denying the user. If we are taking unnecessary permissions, this indicates bad UX design and may impact our app store optimization.&lt;/p&gt;

&lt;p&gt;Now, I now what you’re wondering: &lt;em&gt;how can I find all that data&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Well all we need to do is log into the Play Console and select the apps for which we want to receive Android Vitals data. Here’s a screenshot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fduwsor1sgqxzboty2hy5.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fduwsor1sgqxzboty2hy5.webp" alt="Android Vitals Dashboard" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ok, now let’s look at setting up Android Vitals
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do we set up Android Vitals using Android Studio?
&lt;/h3&gt;

&lt;p&gt;As mentioned, we don’t need to write any code in our app to set up Android Vitals. Google Play does the work for us, and all we’ve got to do is follow this path.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select the app that we want to monitor in our Play console.&lt;/li&gt;
&lt;li&gt;Navigate to &lt;strong&gt;“Monitor and improve &amp;gt; Android Vitals &amp;gt; Overview.”&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple! Once we upload the file to the Play Store, Google will start collecting data about its performance and quality, and the dashboard means we can monitor it easily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Now let’s see how Android Vitals works in Google Play
&lt;/h3&gt;

&lt;p&gt;Once Android Vitals has processed the data, it will show in the Play Console in different metrics. Let’s go point by point.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Data Collect:&lt;/strong&gt; When the app runs on an Android device, Google will collect the performance data automatically and store the data in its database. The data is stored in different metrics, like devices and regions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Process:&lt;/strong&gt; Google Play processes raw data into specific Android Vitals metrics. These metrics are grouped by device, device OS, region, country, and app version. The groupings help us to identify the issues properly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor Dashboard:&lt;/strong&gt; We can easily view the data in the Android Vitals dashboard to identify app performance and stability.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And that’s the basics done! If you still have any doubts, check out the official Android app developer video.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: More advanced concepts on Android Vitals
&lt;/h2&gt;

&lt;p&gt;Ok, now we’re hitting the big stuff: complex debugging, deep-checking of performance issues and analysis of Play Console reports. These advanced concepts will help us improve app stability and, just as importantly, reduce crashes and ANR.&lt;/p&gt;

&lt;p&gt;Once we’ve optimized the crashes, Google will automatically improve our ranking in the Play Store, and our app will become virally popular around the world (ok, we can’t control the last bit, but you get my point).&lt;/p&gt;

&lt;p&gt;So let’s look at some ways we can improve our ranking.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Debug ANR (Application Not Responding):&lt;/strong&gt; When the main UI thread is blocked for more than 5 seconds, the ANR dialog appears. How do we fix this? Well first, we need to analyze when the main thread blocks the UI from the Play Console report, then fix the code and upload the new build in the Play Console accordingly (heads-up: most cases of ANR occur during network operation, query execution and complex calculation, and they prevent the user from providing input in the app).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Battery drain and wake lock:&lt;/strong&gt; As goes without saying, we need to be careful that the app does not consume much battery life. Thankfully, the &lt;strong&gt;Battery Historian&lt;/strong&gt; tool provided by Android Studio helps us cut the code into pieces, execute it in different time frames and analyze partial wake locks and background processes. If we have a wake lock issue, we can optimize it using the &lt;code&gt;WakefulBroadcastReceiver&lt;/code&gt; or &lt;code&gt;JobScheduler&lt;/code&gt; tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background Network Usage:&lt;/strong&gt; We can check network usage using &lt;code&gt;Network Profiler&lt;/code&gt; in Android Studio, or once again we can use &lt;code&gt;JobScheduler&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native code and JNI Issues:&lt;/strong&gt; When dealing with Java Native Interface (JNI) issues, we need to debug crashes from C++/NDK code. Google Play Console native crash reports help to optimize the issue, and we can use &lt;code&gt;ndk-stack&lt;/code&gt; to symbolize the crash logs from the native library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI render and frame drops:&lt;/strong&gt; Poor UI performance leads to frame drops – simple. But there’s plenty we can do here. After analyzing a UI report from the rendering section of the Android vitals dashboard, we can use &lt;code&gt;GPU Profiler&lt;/code&gt; to detect UI overflow. We have to analyze &lt;code&gt;Choreographer logs&lt;/code&gt; to measure the frame rendering time.7&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As general points of good practice, it’s advisable to use SVG images instead of PNG or JPEG images (SVG images reduce &lt;a href="https://dev.to/bugfenderapp/how-proper-lifecycle-management-can-prevent-memory-leaks-46pg"&gt;out-of-memory errors&lt;/a&gt;), and handled runtime permissions properly. Specifically, we need to be careful that users cannot skip the permission dialog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally, let’s look at different metrics and edge cases
&lt;/h2&gt;

&lt;p&gt;One of the great things about Android Vitals is that we can use it to measure different metrics and plan for edge cases. This will help us carry out deep debugging and fix issues, while metrics data helps to categorize issues and priorities.&lt;/p&gt;

&lt;p&gt;Here are some possible edge cases that we should anticipate and fix proactively, to ensure our apps are error-free and maintain that precious Play Store ranking.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slow networks or repeated network calls cause the ANR dialog.&lt;/li&gt;
&lt;li&gt;Large bitmap &lt;a href="https://dev.to/bugfenderapp/imageview-in-android-everything-you-need-to-know-1n48"&gt;image&lt;/a&gt; processing in low-memory devices causes out-of-memory errors.&lt;/li&gt;
&lt;li&gt;App start-up will be delayed because lots of operations are on the &lt;code&gt;onCreate()&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;The app will hang when too many SDKs initialize at app launch.&lt;/li&gt;
&lt;li&gt;If the app wakes up on the device too frequently, it will drain too much battery.&lt;/li&gt;
&lt;li&gt;Continuous location updating in the background will cause high battery consumption.&lt;/li&gt;
&lt;li&gt;Complex UI or animation requires too much time to render frames.&lt;/li&gt;
&lt;li&gt;Nested &lt;code&gt;RecyclerViews&lt;/code&gt; cause slow performance in the app.&lt;/li&gt;
&lt;li&gt;An API request with retry options, or an API running on the main thread, will cause the ANR dialog.&lt;/li&gt;
&lt;li&gt;Downloading large images or videos blocks the UI.&lt;/li&gt;
&lt;li&gt;If Android 6.0 permissions are denied, this can affect app functionality and lead to increased crash frequency.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Android Vitals FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is Android Vitals and why is it important for app developers?
&lt;/h3&gt;

&lt;p&gt;Android Vitals is a Google Play tool that tracks app performance, crashes, and battery usage. Monitoring it helps developers fix issues and &lt;a href="https://bugfender.com/platforms/android/" rel="noopener noreferrer"&gt;improve app stability&lt;/a&gt;, which can boost Google Play rankings.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can I access Android Vitals in the Google Play Console?
&lt;/h3&gt;

&lt;p&gt;To access Android Vitals, go to the Play Console, select your app, and navigate to &lt;strong&gt;Monitor and improve &amp;gt; Android Vitals &amp;gt; Overview&lt;/strong&gt;. You’ll see metrics like crash rate, ANR rate, and battery usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to add code to use Android Vitals in my app?
&lt;/h3&gt;

&lt;p&gt;No, Android Vitals collects data automatically. You don’t need to add code—just upload your app to the Play Store and Google handles the rest through the Play Console.&lt;/p&gt;

&lt;h3&gt;
  
  
  What does ANR rate mean in Android Vitals?
&lt;/h3&gt;

&lt;p&gt;The ANR (Application Not Responding) rate shows how often your app’s UI thread is blocked for more than 5 seconds. High ANR rates hurt user experience and affect app rankings.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can I fix battery drain issues highlighted in Android Vitals?
&lt;/h3&gt;

&lt;p&gt;To fix battery drain, use tools like Battery Historian or JobScheduler. Look out for wake locks, background WiFi scans, and frequent background activity—all of which can drain battery fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  What causes slow UI rendering in Android apps?
&lt;/h3&gt;

&lt;p&gt;Slow UI rendering happens when frames take too long to render. Common causes include complex layouts, animations, or nested RecyclerViews. Use GPU Profiler and Choreographer logs to spot these issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does Android Vitals affect my app’s ranking on Google Play?
&lt;/h3&gt;

&lt;p&gt;Google uses Android Vitals data to rank apps. Apps with fewer crashes, better battery use, and smoother UI are more likely to appear higher in Play Store search and recommendations.&lt;/p&gt;

&lt;h2&gt;
  
  
  And we’re done!
&lt;/h2&gt;

&lt;p&gt;Well, almost. Before we go, let’s summarize what we have learned today, from app creation to app publishing in the Play Store.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We need to regularly follow up on the Android Vitals report and continuously fix crashes for a better user experience and optimal Google Play ranking.&lt;/li&gt;
&lt;li&gt;During app development, we need to remember the above edge cases and develop the app accordingly.&lt;/li&gt;
&lt;li&gt;We should think about the user experience at all times. User permissions provide a prime example; even a slight error with permissions can have a major impact on the overall look and feel of our app.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Any other questions? Just write to us. We’re always happy to chat about Android Vitals or any other Android topic. So whatever questions you’ve got, we’re hear to chat.&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

</description>
      <category>android</category>
      <category>java</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Kotlin Extension Functions: Add Functionality Without Modifying Code</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Fri, 16 May 2025 08:24:51 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/kotlin-extension-functions-add-functionality-without-modifying-code-3ckk</link>
      <guid>https://forem.com/bugfenderapp/kotlin-extension-functions-add-functionality-without-modifying-code-3ckk</guid>
      <description>&lt;p&gt;Imagine you own a car. It’s reliable, runs smoothly and gets you where you need to go. But one day, you realize you need a GPS navigation system for better routes. What do you do?&lt;/p&gt;

&lt;p&gt;Would you redesign the entire car just to integrate GPS, or would you simply install a GPS device on the dashboard? Of course, the smarter choice is to add the GPS instead of modifying the car’s built-in system.&lt;/p&gt;

&lt;p&gt;This is exactly how a Kotlin Extension Function works. Instead of modifying the code of an existing class or interface, or relying on its inheritance, developers can add new functionality externally – just like upgrading your car without altering its core structure.&lt;/p&gt;

&lt;p&gt;And here’s the really cool part about extension functions: &lt;a href="https://dev.to/bugfenderapp/kotlin-vs-java-a-comprehensive-comparison-3n6f"&gt;whereas in Java&lt;/a&gt; they look like regular functions, in Kotlin, they appear as if they are &lt;em&gt;directly part of the inner class itself.&lt;/em&gt; This makes them more intuitive, and it’s great for static method coding (where we call a class directly by name, without creating an object).&lt;/p&gt;

&lt;p&gt;Today, we’re going to dive into the whole topic of extension functions usage in Kotlin. It should be ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://bugfender.com/guides/android-app-development-guide/" rel="noopener noreferrer"&gt;Developers taking their first steps in the Kotlin&lt;/a&gt; programming language.&lt;/li&gt;
&lt;li&gt;Those who want to see how static function coding can improve their daily lives as devs.&lt;/li&gt;
&lt;li&gt;Those who need to see the Kotlin class methodology in practice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good to go? Let’s get stuck into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  First off: how do we create a Kotlin function?
&lt;/h2&gt;

&lt;p&gt;In Java, if you want to add a new function to an existing class (such as &lt;code&gt;String&lt;/code&gt; or &lt;code&gt;List&lt;/code&gt;), you typically extend the class through inheritance or create a separate utility function, both of which can make the code clunkier and harder to maintain. In Kotlin? Well, to show you the difference, let’s run some code.&lt;/p&gt;

&lt;p&gt;For example, let’s create a function that performs an operation on a &lt;code&gt;String&lt;/code&gt;. In Java code, a developer might do it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;String myString = someOperation(input);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But with Kotlin Extension Functions, the source code is way simpler and more natural:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val escaped = input.someOperation()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;input&lt;/code&gt; is a &lt;code&gt;String&lt;/code&gt;, and we are calling &lt;code&gt;someOperation()&lt;/code&gt; on it, just like a built-in method of &lt;em&gt;String&lt;/em&gt; class.&lt;/p&gt;

&lt;p&gt;Not only is this easier to read, but Integrated Development Environments (IDEs) will be able to offer the extension method as an autocomplete option, as if it were a standard method on the &lt;em&gt;String&lt;/em&gt; class.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;An important thing to note&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As we’ve noted, extension functions don’t actually modify the classes they extend. As a Kotlin developer, when we define an extension, we aren’t adding new members to the original class itself, we’re simply allowing new functions to be called using dot notation on the relevant Kotlin variable.&lt;/p&gt;

&lt;p&gt;One key thing to remember is that extension functions are resolved at compile time. Kotlin determines which extension function to call, based on the type of the variable, before the program runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Excited to learn more?
&lt;/h2&gt;

&lt;p&gt;Great: we’re excited to teach you! Let’s dive in and explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writing our own extension functions.&lt;/strong&gt; Syntax and fundamentals of creating extensions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leveraging nullability in extension functions.&lt;/strong&gt; Handling nullable types effectively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generics in extension functions.&lt;/strong&gt; Making extensions more flexible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extension properties.&lt;/strong&gt; Adding properties without modifying the class.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infix with extension functions.&lt;/strong&gt; The cherry on the top – Enhancing readability and elegance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Companion object extensions.&lt;/strong&gt; Extend the companion class object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced extension function ideas.&lt;/strong&gt; Useful functions you can implement right away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using extension functions in Java (interoperability).&lt;/strong&gt; Calling Kotlin extensions from Java.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When and how to use extension functions.&lt;/strong&gt; Best practices and real-world use cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organizing extension functions.&lt;/strong&gt; Keeping your code clean and maintainable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Writing our own Extension Functions
&lt;/h2&gt;

&lt;p&gt;Now, this is the moment we’ve all been waiting for, right? We’ve talked enough about theory, now let’s get into the coding! &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjgr7bha1n1hjuxtxwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjgr7bha1n1hjuxtxwv.png" alt="🚀" width="72" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the Anatomy of an Extension Function
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun ClassName.functionName(parameters): ReturnType {
    // Function body
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing an extension function is just like writing any regular function in Kotlin. The only difference? We specify the class that we are extending before the function name; this is how we declare an extension!&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Creating a Full URL from a Path
&lt;/h3&gt;

&lt;p&gt;Imagine you have an image path, and you always need to append a BASE_URL before using it in your app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Base URL
val BASE_URL = "&amp;lt;https://some.base.url&amp;gt;"

// Extension function to convert a relative path into a full URL
fun String.toFullUrl() = BASE_URL + this

fun main() {
    val image = "/images/my_image.jpg"

    val url = image.toFullUrl()

    println(url)
}

// OUTPUT
// &amp;lt;https://some.base.url/images/my_image.jpg&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;toFullUrl()&lt;/code&gt; function extends the &lt;strong&gt;String class&lt;/strong&gt; seamlessly, without modifying its internal implementation or structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  But why use an extension function?
&lt;/h3&gt;

&lt;p&gt;Instead of writing &lt;code&gt;BASE_URL + imagePath&lt;/code&gt; everywhere, an extension function keeps it reusable and clean! &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjgr7bha1n1hjuxtxwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjgr7bha1n1hjuxtxwv.png" alt="🚀" width="72" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging nullability in Extension Functions
&lt;/h2&gt;

&lt;p&gt;When working with nullable numbers, comparison operations can get tricky due to the need for explicit null checks. Let’s see how we can simplify this using extension functions!&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Comparing Nullable Numbers
&lt;/h3&gt;

&lt;p&gt;Imagine you have two nullable numbers that need to be compared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val numA: Int? = 5
val numB: Int? = null

val isNumAGreaterThanNumB = numA &amp;gt; numB // ERROR!

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mbi5qg8py33nu8clrup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mbi5qg8py33nu8clrup.png" alt="🚨" width="72" height="72"&gt;&lt;/a&gt; Oops! The IDE &lt;a href="https://dev.to/bugfenderapp/mastering-exception-handling-in-kotlin-a-comprehensive-guide-88l"&gt;throws errors&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type mismatch: Required &lt;code&gt;Int&lt;/code&gt;, but found &lt;code&gt;Int?&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Operator call issue: &lt;code&gt;'numA.compareTo(numB)'&lt;/code&gt; is not allowed on a nullable receiver&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Quick Fix: Using &lt;code&gt;?:&lt;/code&gt; (Elvis Operator)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val isNumAGreaterThanNumB = (numA ?: 0) &amp;gt; (numB ?: 0)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This instance works, but writing &lt;code&gt;?: 0&lt;/code&gt; everywhere is a waste of your time. Instead, it can be improved with an extension function.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Better Solution: Using an Extension Function
&lt;/h3&gt;

&lt;p&gt;Instead of adding null checks manually every time, let’s create a reusable extension function that handles nullability for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun Number?.isGreaterThan(number: Number?): Boolean {
    if (this == null) return false
    if (number == null) return true
    return this.toDouble() &amp;gt; number.toDouble()
}

fun main() {
    val numA: Int? = 5
    val numB: Int? = null
    println(numA.isGreaterThan(numB)) // true
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it – no more manual null checks! &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp02smn7o2y1tl0r7pkby.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp02smn7o2y1tl0r7pkby.png" alt="🎉" width="72" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This simple extension function removes boilerplate code, improves readability and ensures our comparisons always work, regardless of null values or numeric types.&lt;/p&gt;

&lt;h3&gt;
  
  
  But wait, there’s more!
&lt;/h3&gt;

&lt;p&gt;We’ve mastered handling nullability, but what if we want our extension functions to be even more flexible?&lt;/p&gt;

&lt;p&gt;Let’s dive into the power of Generics in Extension Functions!&lt;/p&gt;

&lt;h2&gt;
  
  
  Generics in Extension Functions! &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjgr7bha1n1hjuxtxwv.png" alt="🚀" width="72" height="72"&gt;
&lt;/h2&gt;

&lt;p&gt;Generics allow us to write a single extension function that works with multiple types.&lt;/p&gt;

&lt;p&gt;For example, instead of writing different functions for &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;Double&lt;/code&gt;, and &lt;code&gt;String&lt;/code&gt; class, we can create a &lt;a href="https://dev.to/bugfenderapp/understanding-kotlin-generics-a-complete-guide-for-developers-2dal"&gt;generic extension function that works with any type&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: A Generic &lt;a href="https://bugfender.com/remote-logging/" rel="noopener noreferrer"&gt;Logging&lt;/a&gt;Function
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun &amp;lt;T&amp;gt; T.log() {
    println("Value: $this (Type: ${this::class.simpleName})")
}

fun main() {
    val number = 42
    val text = "Hello, Kotlin!"
    val pi = 3.14159

    number.log()
    text.log()
    pi.log()
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Output:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Value: 42 (Type: Int)
Value: Hello, Kotlin! (Type: String)
Value: 3.14159 (Type: Double)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;&amp;lt;T&amp;gt;&lt;/code&gt; makes the function work with any type without needing separate functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extension Properties
&lt;/h2&gt;

&lt;p&gt;We’ve explored extension functions, but Kotlin has another powerful feature, called Extension Properties. Just like functions, you can add properties to a class without modifying its original definition.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do Extension Properties work?
&lt;/h3&gt;

&lt;p&gt;Let’s say you have a &lt;code&gt;Person&lt;/code&gt; class, and you want to add an extension property that returns the full name without changing the class itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Person(val firstName: String, val lastName: String)

// Extension property to get the full name
val Person.fullName: String
    get() = "$firstName $lastName"

fun main() {
    val person = Person("Jhon", "Doe")
    println(person.fullName) // Output: Jhon Doe
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Limitations of Extension Properties
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cannot Store Values.&lt;/strong&gt; Extension properties don’t have a backing field, so they can only return computed values, not stored data. This means mutable (&lt;code&gt;var&lt;/code&gt;) extension properties are not allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Private Access.&lt;/strong&gt; They can’t access private properties or methods of the class they extend, only public members.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Field Addition.&lt;/strong&gt; They don’t actually add new properties to a class, just a way to compute values dynamically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Naming Conflicts.&lt;/strong&gt; If an extension property has the same name as a class property, the class property takes priority, which can lead to confusion.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Infix with Extension Functions: The cherry on top! &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1pvi2coh0heannr9wr2.png" alt="🍒" width="72" height="72"&gt;
&lt;/h2&gt;

&lt;p&gt;In Kotlin, functions marked with the &lt;code&gt;infix&lt;/code&gt; keyword can be called without using dots or parentheses. This makes function calls look more natural and readable, especially when designing DSL-style code.&lt;/p&gt;

&lt;p&gt;However, not all functions can use infix notation. Instead they must meet the following conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They must be member functions or extension functions.&lt;/li&gt;
&lt;li&gt;They must have exactly one parameter.&lt;/li&gt;
&lt;li&gt;The parameter must not be &lt;code&gt;vararg&lt;/code&gt; and must not have a default value.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Understanding Infix Expressions
&lt;/h3&gt;

&lt;p&gt;Normally, when calling a function, we use dot notation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun Int.add(number: Int): Int {
    return this + number
}

val num = 10
val newNum = num.add(5)
println(newNum) // Output: 15

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding the &lt;code&gt;infix&lt;/code&gt; keyword, we can call the function without the dot and parentheses, making the syntax cleaner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;infix fun Int.add(number: Int): Int {
    return this + number
}

val num = 10
val newNum = num add 5 // Now called using infix notation
println(newNum) // Output: 15

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach improves readability and makes function calls feel more natural.&lt;/p&gt;

&lt;h2&gt;
  
  
  Companion Object Extensions
&lt;/h2&gt;

&lt;p&gt;What if you want to &lt;strong&gt;extend&lt;/strong&gt; the functionality of a companion object? Kotlin allows this through &lt;strong&gt;companion object extensions&lt;/strong&gt;. These work just like regular extension functions, but they specifically extend the companion object of a class.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk6m2g2hztm7rmfksysv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk6m2g2hztm7rmfksysv7.png" alt="💡" width="72" height="72"&gt;&lt;/a&gt;In Kotlin, a &lt;strong&gt;companion object&lt;/strong&gt; is a special type of object that belongs to a class but acts like a static member (similar to &lt;code&gt;static&lt;/code&gt; in Java). It allows you to define functions and properties that can be accessed without creating an instance of the class.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyClass {
    companion object { } // This is the companion object
}

// Define an Extension Function for the Companion Object
fun MyClass.Companion.printCompanion() { 
    println("MyClass companion") 
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;This function &lt;strong&gt;extends the companion object&lt;/strong&gt; of &lt;code&gt;MyClass&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It can be called using just the &lt;strong&gt;class name&lt;/strong&gt; , without needing an instance of &lt;code&gt;MyClass&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Usage:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun main() {
    MyClass.printCompanion() // Calling the function directly on the class
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Output:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyClass companion

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advanced Extension Function ideas
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Network Connectivity Check (Android)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun Context.isOnline(): Boolean {
    val connMgr = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val networkInfo: NetworkInfo? = connMgr.activeNetworkInfo
    return networkInfo?.isConnected == true
}

// Usage
val isOnline = applicationContext.isOnline()
// or
val isOnline = context.isOnline()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Converting Any Object to JSON (Using Gson)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun Any.toJson(): String = Gson().toJson(this)

// Usage
data class User(val name: String, val age: Int)

val user = User("Jhon", 25)
val json = user.toJson() // Output: {"name":"Jhon","age":25}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Converting JSON String to Any Object (Using Gson)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inline fun &amp;lt;reified T&amp;gt; String.fromJson(): T = Gson().fromJson(this, T::class.java)

// Usage
val jsonString = """{"name":"Jhon","age":25}"""
val user: User = jsonString.fromJson()
println(user.name) // Jhon

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deep Copy Using (Using Gson)
&lt;/h3&gt;

&lt;p&gt;This function converts an object to JSON and then deserializes it back into a new object, ensuring a deep copy is created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inline fun &amp;lt;reified T&amp;gt; T.jsonCopy(): T {
    val json = Gson().toJson(this)
    return Gson().fromJson(json, T::class.java)
}

// Usage
data class User(val name: String, val age: Int)
val user = User("Jhon", 25)
val copiedUser: User = user.jsonCopy()

println("Original: $user")
println("Copied: $copiedUser")
println("Are objects the same? ${user === copiedUser}") // false (different instances)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prevent Double Clicks on a View
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun View.setSingleClickListener(delay: Long = 500, action: (View) -&amp;gt; Unit) {
    this.setOnClickListener(object : View.OnClickListener {
        private var lastClickTime = 0L

        override fun onClick(v: View) {
            val currentTime = System.currentTimeMillis()
            if (currentTime - lastClickTime &amp;gt;= delay) {
                lastClickTime = currentTime
                action(v)
            }
        }
    })
}

// Usage
button.setSingleClickListener {
    println("Button clicked!")
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since you can create functions based on your needs, the possibilities are endless.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.android.com/kotlin/ktx/extensions-list" rel="noopener noreferrer"&gt;Android-KTX libraries&lt;/a&gt; frequently use extension functions to simplify development. Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;List&amp;lt;T&amp;gt;.getOrNull()&lt;/code&gt; – Safe element retrieval.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;List&amp;lt;T&amp;gt;.lastIndex&lt;/code&gt; – Gets the last valid index.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Canvas.withClip()&lt;/code&gt; – Simplifies clipping operations.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Bitmap.toDrawable()&lt;/code&gt; – Converts a &lt;code&gt;Bitmap&lt;/code&gt; to a &lt;code&gt;Drawable&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Extension functions reduce boilerplate and improve code readability, making Kotlin even more powerful &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjgr7bha1n1hjuxtxwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjgr7bha1n1hjuxtxwv.png" alt="🚀" width="72" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Using extension functions in Java (Interoperability)
&lt;/h2&gt;

&lt;p&gt;Since Kotlin is built on top of Java, it ensures seamless interoperability between both languages. However, Kotlin extension functions don’t directly translate into member functions in Java. Instead, they act as static utility functions, requiring explicit calls from Java class.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Accessing an Extension Function in Java class
&lt;/h3&gt;

&lt;p&gt;Let’s say we define an extension function in StringExt.kt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Extension function to convert a relative path into a full URL
fun String.toFullUrl() = BASE_URL + this

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To call this function from Java, we refer to the Kotlin file name followed by &lt;code&gt;Kt&lt;/code&gt;, as Kotlin compiles extension functions into static methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;String image = "/images/my_image.jpg";
String url = StringExtKt.toFullUrl(image);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although extension functions behave like utility functions in Java, they remain a powerful tool for keeping Kotlin code concise and readable.&lt;/p&gt;

&lt;h2&gt;
  
  
  When and How to Use Extension Functions
&lt;/h2&gt;

&lt;p&gt;Extension functions are powerful, but they should be used thoughtfully. Here are some best practices and real-world use cases:&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use Extension Functions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enhancing Existing Classes.&lt;/strong&gt; Add functionality to third-party or built-in classes without modifying their source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improving Code Readability.&lt;/strong&gt; Replace repetitive utility functions with cleaner, more natural syntax.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reducing Boilerplate Code.&lt;/strong&gt; Avoid redundant code by defining reusable extensions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to Avoid Extension Functions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;If it makes code hard to understand.&lt;/strong&gt; Overuse of extensions can make it unclear where a function is defined.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If it conflicts with class members.&lt;/strong&gt; If an extension function has the same name as a class function, the member function takes precedence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you need private access.&lt;/strong&gt; Extension functions can’t access private members of the class they extend.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Organizing Extension Functions
&lt;/h2&gt;

&lt;p&gt;To keep your code clean and maintainable, extension functions should be structured properly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices for Organizing Extensions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Group by context.&lt;/strong&gt; Store extension functions in files related to their purpose.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;StringExt.kt&lt;/code&gt; → String-related extensions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ViewExt.kt&lt;/code&gt; → Android &lt;code&gt;View&lt;/code&gt; extensions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NetworkExt.kt&lt;/code&gt; → Network-related extensions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Use meaningful file names.&lt;/strong&gt; Avoid dumping all extensions into one file. Use descriptive names based on functionality.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Keep extensions modular.&lt;/strong&gt; Avoid making extensions too large or complex. Each extension should be focused on a single responsibility.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Document important extensions.&lt;/strong&gt; If an extension function does something non-obvious, add documentation to avoid confusion.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;By following these practices, you ensure that extension functions remain an asset rather than a burden, keeping your Kotlin codebase clean, structured and easy to navigate. &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjgr7bha1n1hjuxtxwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjgr7bha1n1hjuxtxwv.png" alt="🚀" width="72" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  To Recap
&lt;/h2&gt;

&lt;p&gt;Kotlin’s extension functions provide a clean and efficient way to enhance existing classes without inheritance or modification. They allow better code organization, reusability, and readability, making &lt;a href="https://dev.to/bugfenderapp/jetpack-compose-tutorial-for-android-developers-build-modern-uis-faster-18g1"&gt;development faster&lt;/a&gt; and more intuitive.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We explored how extension functions work under the hood, their syntax, and real-world use cases.&lt;/li&gt;
&lt;li&gt;Extension properties help add computed properties, while infix notation makes function calls more expressive.&lt;/li&gt;
&lt;li&gt;Advanced extension functions demonstrate their power in &lt;a href="https://bugfender.com/platforms/android/" rel="noopener noreferrer"&gt;Android&lt;/a&gt;, JSON serialization, and utility operations.&lt;/li&gt;
&lt;li&gt;Interoperability with Java ensures Kotlin extension functions remain useful in mixed-language projects.&lt;/li&gt;
&lt;li&gt;Best practices for structuring and using extension functions help maintain a scalable and organized codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By leveraging extension functions effectively, you can write concise, elegant, and highly maintainable Kotlin code. &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjgr7bha1n1hjuxtxwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fjgr7bha1n1hjuxtxwv.png" alt="🚀" width="72" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;May your coding adventures in Kotlin be productive and enjoyable. Happy coding!&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>iOS vs Android Error Monitoring: Key Differences Explained</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Mon, 12 May 2025 09:43:51 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/ios-vs-android-error-monitoring-key-differences-explained-4a5o</link>
      <guid>https://forem.com/bugfenderapp/ios-vs-android-error-monitoring-key-differences-explained-4a5o</guid>
      <description>&lt;p&gt;When it comes to error monitoring in mobile apps, iOS and Android take distinct approaches due to their unique architectures and tools. Here’s a quick breakdown of the key differences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;iOS&lt;/strong&gt; : Relies on &lt;a href="https://developer.apple.com/testflight/" rel="noopener noreferrer"&gt;TestFlight&lt;/a&gt; and App Store logs for error reporting. Debugging requires dSYM files for symbolication, and app reviews can take up to 7 days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android&lt;/strong&gt; : Uses &lt;a href="https://play.google.com/console/about/vitals/" rel="noopener noreferrer"&gt;Android Vitals&lt;/a&gt; for &lt;a href="https://bugfender.com/crash-reporting/" rel="noopener noreferrer"&gt;crash reporting&lt;/a&gt;. Debugging involves &lt;a href="https://www.guardsquare.com/manual/home" rel="noopener noreferrer"&gt;ProGuard&lt;/a&gt; mapping files, and updates can be deployed same-day without lengthy reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Programming Languages&lt;/strong&gt; : Swift (iOS) uses explicit error handling (&lt;code&gt;throws&lt;/code&gt;), while Kotlin (Android) offers more flexibility and optional runtime exception handling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Quick Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;iOS&lt;/th&gt;
&lt;th&gt;Android&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Initial Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Higher (device variety)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Review Time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~7 days&lt;/td&gt;
&lt;td&gt;Same-day possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Debug Files&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;dSYM files required&lt;/td&gt;
&lt;td&gt;ProGuard mapping needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Crash Analysis&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Symbolication required&lt;/td&gt;
&lt;td&gt;Multiple trace types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Test Environment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited devices&lt;/td&gt;
&lt;td&gt;Multiple configurations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Understanding these differences helps developers build stable, user-friendly apps. Tools like &lt;a href="https://bugfender.com/" rel="noopener noreferrer"&gt;Bugfender&lt;/a&gt; simplify cross-platform error monitoring by providing &lt;a href="https://bugfender.com/remote-logging/" rel="noopener noreferrer"&gt;unified logging&lt;/a&gt;, automated debugging, and secure data handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mobile crash reporting and debugging best practices
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Platform Differences in Error Monitoring
&lt;/h2&gt;

&lt;p&gt;iOS and Android handle error monitoring differently due to their distinct architectures and environments. Understanding these differences is crucial for implementing effective monitoring strategies. Below, we explore the native tools and language-specific error handling methods that define these platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-in Error Reporting Tools
&lt;/h3&gt;

&lt;p&gt;iOS developers rely on tools like TestFlight and App Store logs, while Android developers use the Google Play Console’s Android Vitals. iOS app distribution comes with a $100 yearly fee, which also covers testing on devices. On the other hand, Android’s Google Play Console provides &lt;a href="https://bugfender.com/blog/introducing-crash-reporting/" rel="noopener noreferrer"&gt;flexible crash reporting&lt;/a&gt; and faster deployment, skipping the lengthy review process typical for iOS.&lt;/p&gt;

&lt;p&gt;Here’s a quick comparison of error reporting features across the two platforms:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;iOS&lt;/th&gt;
&lt;th&gt;Android&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initial Cost&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Higher (due to device variety)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Review Time&lt;/td&gt;
&lt;td&gt;~7 days&lt;/td&gt;
&lt;td&gt;Same-day possible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test Environment&lt;/td&gt;
&lt;td&gt;Limited devices&lt;/td&gt;
&lt;td&gt;Multiple configurations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics&lt;/td&gt;
&lt;td&gt;TestFlight + App Store&lt;/td&gt;
&lt;td&gt;Android Vitals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License Fee&lt;/td&gt;
&lt;td&gt;$100/year&lt;/td&gt;
&lt;td&gt;$25 one-time&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Swift vs Kotlin Error Handling
&lt;/h3&gt;

&lt;p&gt;The programming languages used for iOS and Android – Swift and Kotlin – also influence error management approaches, shaping the way monitoring is implemented.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Swift Error Handling&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Requires explicit error throwing with &lt;code&gt;throws&lt;/code&gt; and integrates with optionals using &lt;code&gt;try?&lt;/code&gt; or &lt;code&gt;try!&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Runtime exceptions cannot be caught.&lt;/li&gt;
&lt;li&gt;The compiler does not enforce error type checking.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://dev.to/bugfenderapp/mastering-exception-handling-in-kotlin-a-comprehensive-guide-88l"&gt;&lt;strong&gt;Kotlin Error Handling&lt;/strong&gt;&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Does not use checked exceptions.&lt;/li&gt;
&lt;li&gt;Supports optional &lt;code&gt;@Throws&lt;/code&gt; for Java interoperability.&lt;/li&gt;
&lt;li&gt;Offers a more flexible approach to error handling.&lt;/li&gt;
&lt;li&gt;Runtime exception handling is optional.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These language differences call for platform-specific monitoring strategies tailored to their unique error handling mechanisms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Crash Data Collection and Analysis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Standard Crash Metrics
&lt;/h3&gt;

&lt;p&gt;Both iOS and Android automatically collect crash data to help developers identify and fix issues. Here’s a breakdown of the key metrics captured:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric Type&lt;/th&gt;
&lt;th&gt;Data Points Collected&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Device Info&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OS version, device model, memory status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;System State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Battery level, storage space, network status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stack traces, thread details, crash timestamp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;App State&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Active views/activities, app version, build ID&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Platform-Specific Crash Data
&lt;/h3&gt;

&lt;p&gt;Although iOS and Android share common crash reporting features, their processes and specific details vary significantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;iOS Crash Reporting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On iOS, debugging relies heavily on symbolication, a process that uses dSYM (debug symbol) files to translate memory addresses into readable information. Each app build generates a unique dSYM file with a UUID that must match the crash report for accurate debugging. iOS crash reports typically include the type of crash or exception, thread stack traces, and a list of loaded images with their UUIDs &lt;a href="https://dev.to/bugfenderapp/how-to-read-and-analyze-ios-crash-reports-a-developers-guide-41nn"&gt;&lt;sup&gt;[1]&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Android Crash Analysis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Android compiles crash data using several methods:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Exception Stack Traces&lt;/strong&gt; : Offer detailed insights into failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ANR (Application Not Responding) Traces&lt;/strong&gt;: Track app freezes or unresponsiveness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NDK Tombstones&lt;/strong&gt; : Log crashes within native code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;According to a 2015 Crittercism study, Apple had a slightly higher crash rate (2.2% on iOS 8) compared to Android (2.0% on Lollipop) &lt;a href="https://innoppl.com/blog/android-fares-slightly-better-ios-app-crash-rates" rel="noopener noreferrer"&gt;&lt;sup&gt;[2]&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform-Specific Challenges&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each platform presents unique challenges for developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Android&lt;/strong&gt; : ProGuard obfuscation can make stack traces unreadable without the correct mapping files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iOS&lt;/strong&gt; : Proper handling of dSYM files is essential for effective symbolication and debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a quick comparison of the two platforms:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;iOS&lt;/th&gt;
&lt;th&gt;Android&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Debug Files&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires dSYM files&lt;/td&gt;
&lt;td&gt;Needs ProGuard mapping files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Crash Analysis&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Relies on symbolication&lt;/td&gt;
&lt;td&gt;Uses multiple trace types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data Detail Level&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Focused on single app data&lt;/td&gt;
&lt;td&gt;Handles multi-process data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These differences highlight the complexities of crash analysis across platforms. Up next, we’ll explore how Bugfender offers a streamlined solution for cross-platform error monitoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://bugfender.com/" rel="noopener noreferrer"&gt;Bugfender&lt;/a&gt;‘s Cross-Platform Monitoring
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzishogkxypus9lwc7d0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzishogkxypus9lwc7d0.jpg" alt="Bugfender" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bugfender offers a unified solution for monitoring iOS and Android apps. With over 30,000 apps using its services, it provides a reliable way to track errors across platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Bugfender Features
&lt;/h3&gt;

&lt;p&gt;Bugfender does more than just crash reporting – it gives developers detailed insights into how their apps behave. Its standout features include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature Category&lt;/th&gt;
&lt;th&gt;iOS Capabilities&lt;/th&gt;
&lt;th&gt;Android Capabilities&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Remote Logging&lt;/td&gt;
&lt;td&gt;Real-time log capture, offline buffering&lt;/td&gt;
&lt;td&gt;Real-time log capture, offline buffering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error Context&lt;/td&gt;
&lt;td&gt;Stack traces, device info, user actions&lt;/td&gt;
&lt;td&gt;Stack traces, ANR traces, user interactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance Impact&lt;/td&gt;
&lt;td&gt;Lightweight SDK, battery-efficient&lt;/td&gt;
&lt;td&gt;Minimal memory footprint, battery-optimized&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A great example of Bugfender’s impact comes from &lt;a href="https://www.se.com/us/en/" rel="noopener noreferrer"&gt;Schneider Electric&lt;/a&gt;. By using Bugfender, they improved their bug resolution process, enabling teams across the globe to share logs and feedback instantly. This helped streamline workflows and speed up development &lt;a href="https://bugfender.com/resources/case-studies/schneider-electric/" rel="noopener noreferrer"&gt;&lt;sup&gt;[3]&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tailored Solutions for iOS and Android
&lt;/h3&gt;

&lt;p&gt;Bugfender addresses platform-specific challenges with ease. For iOS, it automatically handles symbolication, while for Android, it simplifies debugging with ProGuard mapping.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Bugfender allows me to have remote logs, and investigate really rare bugs and fix them”, says Volodymyt Shevchenko from AppQ&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The platform captures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Crash stack traces with automatic symbolication&lt;/li&gt;
&lt;li&gt;Detailed error context, including user actions&lt;/li&gt;
&lt;li&gt;Device-specific details for accurate diagnostics&lt;/li&gt;
&lt;li&gt;User interaction traces leading up to issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These features work seamlessly within Bugfender’s secure environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Setup and Robust Security
&lt;/h3&gt;

&lt;p&gt;Bugfender can be integrated into your app in under five minutes. It also meets high security standards:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Certified under ISO 27001 for enterprise-level security&lt;/li&gt;
&lt;li&gt;GDPR compliant with data processing agreements available&lt;/li&gt;
&lt;li&gt;TLS 1.3 encryption for data in transit and encryption at rest&lt;/li&gt;
&lt;li&gt;EU-based data storage with a 30-day retention period for premium plans&lt;/li&gt;
&lt;li&gt;Two-factor authentication and detailed audit logs&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“Bugfender is very easy to use and offers great support. Its ability to help find silent bugs is invaluable”, says Tudor Watson from Frog Yellow&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Error Monitoring Guidelines
&lt;/h2&gt;

&lt;p&gt;To make error monitoring more effective across platforms, consider these practical steps for improving detection and resolution processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Early Crash Detection
&lt;/h3&gt;

&lt;p&gt;Detecting crashes early is essential for keeping your app stable. For instance, the Google Home app reduced null pointer exception crashes by 30% after refining its monitoring strategies &lt;a href="https://developer.android.com/topic/performance/vitals/crash" rel="noopener noreferrer"&gt;&lt;sup&gt;[4]&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how you can enhance early crash detection:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Detection Phase&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;iOS Implementation&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Android Implementation&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Development&lt;/td&gt;
&lt;td&gt;Enable &lt;a href="https://dev.to/bugfenderapp/nslog-and-you-learning-how-and-when-to-use-it-1n20-temp-slug-5022455"&gt;NSLog logging&lt;/a&gt; and UI event tracking&lt;/td&gt;
&lt;td&gt;Utilize Android Vitals for monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;Capture stack traces with symbolication&lt;/td&gt;
&lt;td&gt;Analyze crash stack traces thoroughly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production&lt;/td&gt;
&lt;td&gt;Monitor crash rates as experienced by users&lt;/td&gt;
&lt;td&gt;Track user-perceived crash rates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://gymnadz.com/" rel="noopener noreferrer"&gt;GymNadz&lt;/a&gt; experienced faster issue detection through real-time monitoring. Martin Kovachev shared:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The ability to look under the hood in realtime gives us a much better and faster way to detect and fix issues for production apps!”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Besides early detection, understanding the context of errors can speed up troubleshooting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Error Context Data
&lt;/h3&gt;

&lt;p&gt;CARFIT leveraged detailed error context logs during testing to uncover issues more effectively.&lt;/p&gt;

&lt;p&gt;Key types of contextual data to monitor include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Data Type&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Impact&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;User Actions&lt;/td&gt;
&lt;td&gt;Tracks steps leading to crashes&lt;/td&gt;
&lt;td&gt;Simplifies reproducing issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device State&lt;/td&gt;
&lt;td&gt;Captures system conditions&lt;/td&gt;
&lt;td&gt;Pinpoints device-specific problems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Version&lt;/td&gt;
&lt;td&gt;Identifies version-specific issues&lt;/td&gt;
&lt;td&gt;Allows targeted fixes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Bugfender’s approach highlights the importance of context:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Bugfender logs all bugs on all devices and sends the results in seconds – enabling you to find and fix bugs before your users even get an error message.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Equally important is ensuring the security of your error data, which brings us to essential security practices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Security Requirements
&lt;/h3&gt;

&lt;p&gt;Combine error detection and context analysis with robust security measures to protect sensitive data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Security Practices&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use TLS for secure data transmission&lt;/li&gt;
&lt;li&gt;Encrypt data at rest&lt;/li&gt;
&lt;li&gt;Enable two-factor authentication&lt;/li&gt;
&lt;li&gt;Comply with GDPR and other relevant standards&lt;/li&gt;
&lt;li&gt;Utilize ISO 27001-certified data centers&lt;/li&gt;
&lt;li&gt;Maintain audit logs for account activities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The team behind &lt;a href="https://sixages.com/" rel="noopener noreferrer"&gt;Six Ages&lt;/a&gt; demonstrated the benefits of secure monitoring by identifying and fixing a bug before their QA team even reported it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We understand that &lt;a href="https://bugfender.com/security" rel="noopener noreferrer"&gt;protecting your customer’s data&lt;/a&gt; is vital for you. It’s just as important for us.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For apps handling sensitive information, consider private instances that comply with PCI and HIPAA standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;As discussed earlier, challenges like platform-specific log access, error context, and integration can be addressed with a unified monitoring solution.&lt;/p&gt;

&lt;p&gt;Real-world examples from top-performing teams show how unified logging can speed up development processes.&lt;/p&gt;

&lt;p&gt;Here’s a quick comparison of how platforms differ and how a unified solution bridges the gap:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;iOS&lt;/th&gt;
&lt;th&gt;Android&lt;/th&gt;
&lt;th&gt;Cross-Platform Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Log Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Device-restricted&lt;/td&gt;
&lt;td&gt;More accessible&lt;/td&gt;
&lt;td&gt;Remote access for all devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Error Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stack traces with symbolication&lt;/td&gt;
&lt;td&gt;Detailed crash stack traces&lt;/td&gt;
&lt;td&gt;Unified error context view&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Swift-specific&lt;/td&gt;
&lt;td&gt;Kotlin/Java implementation&lt;/td&gt;
&lt;td&gt;Single SDK integration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These differences lead to better troubleshooting and improved app stability. For instance, Disney successfully resolved a production bug, demonstrating the effectiveness of unified monitoring&lt;a href="https://bugfender.com" rel="noopener noreferrer"&gt;&lt;sup&gt;.&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Bugfender is very easy to use and offers great support. Its ability to help find silent bugs is invaluable.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What are the biggest challenges developers face when managing crash data on iOS and Android, and how can they address them?
&lt;/h3&gt;

&lt;p&gt;Managing crash data on iOS and Android comes with unique challenges due to the differences in their ecosystems. Developers must navigate platform-specific tools, frameworks, and error-handling methods to effectively diagnose issues.&lt;/p&gt;

&lt;p&gt;On iOS, challenges often involve working with &lt;strong&gt;Swift&lt;/strong&gt; and managing symbolicated crash reports, which require uploading &lt;strong&gt;.dSYM files&lt;/strong&gt; to interpret stack traces. For Android, handling crashes in &lt;strong&gt;Kotlin&lt;/strong&gt; or &lt;strong&gt;Java&lt;/strong&gt; can be complicated by diverse device configurations and OS versions. To address these challenges, developers should use robust crash reporting tools that capture detailed context, such as user actions, device specs, and logs, to streamline debugging and improve app reliability.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does Bugfender improve error monitoring for iOS and Android apps, and what features make debugging easier?
&lt;/h3&gt;

&lt;p&gt;Bugfender simplifies error monitoring for iOS and Android by collecting detailed app logs to help you quickly identify and resolve issues. With &lt;strong&gt;remote logging&lt;/strong&gt; , you can capture errors, crashes, and exceptions directly from user devices, even if they don’t report problems themselves.&lt;/p&gt;

&lt;p&gt;Key features include &lt;strong&gt;real-time error alerts&lt;/strong&gt; , automatic crash stack trace symbolication, and detailed context with logs and user actions. Bugfender also takes a device-focused approach, making it easier to track and troubleshoot issues specific to individual users. These tools ensure a smoother, more efficient debugging process for developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is it important for developers to understand the differences in error handling between Swift and Kotlin, and how does this impact error monitoring?
&lt;/h3&gt;

&lt;p&gt;Understanding how Swift and Kotlin handle errors is essential because each language has its own distinct approach, which directly influences how developers monitor and manage errors. Swift requires methods that can throw errors to be marked with the &lt;code&gt;throws&lt;/code&gt; keyword, and developers use &lt;code&gt;do-catch&lt;/code&gt; blocks to handle these errors. However, Swift does not mandate specifying the types of errors a method can throw, which can sometimes lead to broader or less targeted error handling.&lt;/p&gt;

&lt;p&gt;Kotlin, on the other hand, does not require methods to declare exceptions they might throw. While this offers more flexibility, it can result in unchecked exceptions that may be missed if not carefully managed. These differences mean developers need tailored strategies to ensure all potential errors are effectively logged and monitored, especially when using robust tools like Bugfender for remote logging and debugging. By understanding these platform-specific nuances, developers can optimize their error tracking workflows and maintain high app performance.&lt;/p&gt;

</description>
      <category>android</category>
      <category>engineering</category>
      <category>ios</category>
      <category>qa</category>
    </item>
    <item>
      <title>How to Use MapKit in iOS with SwiftUI</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Wed, 07 May 2025 12:14:12 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/how-to-use-mapkit-in-ios-with-swiftui-130l</link>
      <guid>https://forem.com/bugfenderapp/how-to-use-mapkit-in-ios-with-swiftui-130l</guid>
      <description>&lt;p&gt;In this article we’ll learn about Apple’s  &lt;strong&gt;MapKit framework&lt;/strong&gt;.  &lt;strong&gt;MapKit&lt;/strong&gt;  is the native way to include  &lt;strong&gt;map views&lt;/strong&gt;  in &lt;a href="https://bugfender.com/guides/ios-app-development/" rel="noopener noreferrer"&gt;our  &lt;strong&gt;iOS application&lt;/strong&gt;&lt;/a&gt; to display  &lt;strong&gt;map&lt;/strong&gt;  or  &lt;strong&gt;satellite imagery&lt;/strong&gt;. It is quite useful for any  &lt;strong&gt;map-centric view&lt;/strong&gt;  and to show  &lt;strong&gt;coordinates&lt;/strong&gt; , annotate on them with a  &lt;strong&gt;custom annotation&lt;/strong&gt; , or just generally view world locations or specific  &lt;strong&gt;map features&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article will be very hands-on and will include several examples on how things are done using  &lt;strong&gt;MapKit&lt;/strong&gt;  and the  &lt;strong&gt;mapkit api&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  MapView
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Showing a Map
&lt;/h2&gt;

&lt;p&gt;Let’s start with the simplest possible thing, showing a  &lt;strong&gt;map&lt;/strong&gt;  on our  &lt;strong&gt;app&lt;/strong&gt;. The simplest way of doing so is just adding a  &lt;strong&gt;Map view&lt;/strong&gt;  to our  &lt;strong&gt;SwiftUI view&lt;/strong&gt;. A simple  &lt;strong&gt;SwiftUI view&lt;/strong&gt;  with a  &lt;strong&gt;map&lt;/strong&gt;  can look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
port SwiftUI
import MapKit

struct ContentView: View {
    var body: some View {
        VStack {
            Map()
        }
        .padding()
    }
}

#Preview {
    ContentView()
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s as simple as it gets, and when we run our  &lt;strong&gt;application&lt;/strong&gt;  we’ll get a full screen  &lt;strong&gt;MapKit view&lt;/strong&gt;  with our  &lt;strong&gt;location service&lt;/strong&gt;  defaulted to our current country.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing the location of the map
&lt;/h2&gt;

&lt;p&gt;Just showing a  &lt;strong&gt;map&lt;/strong&gt;  centered in our country is cool, but what if we wanted to change the location that is being shown to us?&lt;/p&gt;

&lt;p&gt;While this could seem trivial, the only way to change a  &lt;strong&gt;Map’s location&lt;/strong&gt;  programmatically is by injecting that location into the  &lt;strong&gt;Map’s&lt;/strong&gt;  initialization, using the  &lt;strong&gt;MapKit library&lt;/strong&gt;. So if we wanted to start our  &lt;strong&gt;map&lt;/strong&gt;  centered in London, this is what we would need to have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Map(initialPosition: MapCameraPosition.region(
    MKCoordinateRegion(
        center: CLLocationCoordinate2D(latitude: 51.5072, longitude: 0.1276),
        span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)
    )))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What if we wanted to have several different positions and change the map between them? Maybe based on  &lt;strong&gt;specific locations&lt;/strong&gt;  such as Tokyo or Washington DC?&lt;/p&gt;

&lt;p&gt;For that to happen, we would need to make the initial position a &lt;code&gt;@State&lt;/code&gt; property, and we could change its state for it to be reflected in the  &lt;strong&gt;interactive map&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As an example, let’s have 3 &lt;a href="https://dev.to/bugfenderapp/swiftui-buttons-simplified-a-step-by-step-guide-jop-temp-slug-3372944"&gt;buttons&lt;/a&gt; at the bottom of our  &lt;strong&gt;map view&lt;/strong&gt; , and each one linked to  &lt;strong&gt;real-world location data&lt;/strong&gt;  for three major cities.&lt;/p&gt;

&lt;p&gt;By default, we’ll be opening the  &lt;strong&gt;Map&lt;/strong&gt;  centered in London.&lt;/p&gt;

&lt;p&gt;Here’s the  &lt;strong&gt;code&lt;/strong&gt;  example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import SwiftUI
import MapKit

struct ContentView: View {

    @State private var position = MapCameraPosition.region(
        MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: 51.5072, longitude: 0.1276),
            span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)
        )
    )

    var body: some View {
        VStack {
            Map(position: $position)
            HStack(spacing: 50) {
                Button("Washington") {
                    position = MapCameraPosition.region(
                        MKCoordinateRegion(
                            center: CLLocationCoordinate2D(latitude: 47.7511, longitude: -120.7401),
                            span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)
                        )
                    )
                }

                HStack(spacing: 50) {
                    Button("London") {
                        position = MapCameraPosition.region(
                            MKCoordinateRegion(
                                center: CLLocationCoordinate2D(latitude: 51.5072, longitude: 0.1276),
                                span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)
                            ))
                    }

                    Button("Tokyo") {
                        position = MapCameraPosition.region(
                            MKCoordinateRegion(
                                center: CLLocationCoordinate2D(latitude: 35.6897, longitude: 139.6922),
                                span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)
                            )
                        )
                    }
                }
            }
        }}
}

#Preview {
    ContentView()
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can just press any of the bottom buttons and see our  &lt;strong&gt;map view&lt;/strong&gt;  taking us there in &lt;a href="https://dev.to/bugfenderapp/implementing-real-time-communication-in-ios-with-websockets-3gk2-temp-slug-7169851"&gt;real time&lt;/a&gt;, just like a  &lt;strong&gt;web app&lt;/strong&gt; would with a live  &lt;strong&gt;interactive map&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Placing Annotations on our Map
&lt;/h2&gt;

&lt;p&gt;Now that we know how to make the  &lt;strong&gt;map&lt;/strong&gt;  move to our bidding, the next logical step is to learn how to place  &lt;strong&gt;annotations&lt;/strong&gt; on it. Let’s say we really like the London Eye, and we’d like to add a  &lt;strong&gt;custom annotation&lt;/strong&gt;  at its  &lt;strong&gt;specific coordinate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;First, we would need the London Eye’s location, which is lat:  &lt;strong&gt;51.503399&lt;/strong&gt; , long:  &lt;strong&gt;-0.119519&lt;/strong&gt; , and we would add it using a  &lt;strong&gt;marker&lt;/strong&gt;. A  &lt;strong&gt;Marker&lt;/strong&gt;  for London Eye could look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Marker("London eye", coordinate: CLLocationCoordinate2D(latitude: 51.503399, longitude: -0.119519))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s update our  &lt;strong&gt;content&lt;/strong&gt;  to include this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import SwiftUI
import MapKit

struct ContentView: View {

    var annotations: [IdentifiableLocation] = [
        IdentifiableLocation(id: ObjectIdentifier(MyClass(name: "London eye")), location: CLLocationCoordinate2D(latitude: 51.503399, longitude: -0.119519))
    ]

    @State private var region = MKCoordinateRegion(
        center: CLLocationCoordinate2D(latitude: 51.5072, longitude: 0.1276),
        span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)
    )

    var body: some View {
        Map(coordinateRegion: $region,
            annotationItems: annotations)
        { place in
            MapPin(coordinate: place.location,
                   tint: Color.purple)
        }
    }
}

#Preview {
    ContentView()
}

struct IdentifiableLocation: Identifiable {
    var id: ObjectIdentifier
    var location: CLLocationCoordinate2D
}

class MyClass {
    var name: String
    init(name: String) {
        self.name = name
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our  &lt;strong&gt;map view&lt;/strong&gt;  now displays the  &lt;strong&gt;map item&lt;/strong&gt;  pointing at London Eye with a simple pin:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqsehlv528goizus7llyh.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqsehlv528goizus7llyh.webp" alt="Mapkit" width="472" height="1024"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other basic types of annotations that  &lt;strong&gt;MapKit&lt;/strong&gt;  supports include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MapMarker&lt;/strong&gt; , a simpler alternative to the Pin&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MapAnnotation&lt;/strong&gt; , for designing fully  &lt;strong&gt;custom annotations&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting tap coordinates
&lt;/h2&gt;

&lt;p&gt;An important feature of  &lt;strong&gt;interactive maps&lt;/strong&gt;  is getting the  &lt;strong&gt;location data&lt;/strong&gt;  where users tap.&lt;/p&gt;

&lt;p&gt;We can use a &lt;code&gt;onTapGesture&lt;/code&gt; on a  &lt;strong&gt;Map view&lt;/strong&gt;  like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Map().onTapGesture() { position in
    print(position)
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But to get  &lt;strong&gt;longitude&lt;/strong&gt;  and  &lt;strong&gt;latitude coordinates&lt;/strong&gt;  instead of simple X,Y screen positions, we should use a &lt;code&gt;MapReader&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
import SwiftUI
import MapKit

struct ContentView: View {

    var body: some View {
        MapReader { proxy in
            Map().onTapGesture() { position in
                if let coordinate = proxy.convert(position, from: .local) {
                    print(coordinate)
                }
           }
        }
    }
}

#Preview {
    ContentView()
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will now receive output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
CLLocationCoordinate2D(latitude: 38.1225410362762, longitude: -8.471839602579648)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This can be very useful in  &lt;strong&gt;developer apps&lt;/strong&gt;  when saving  &lt;strong&gt;specific locations&lt;/strong&gt;  or building  &lt;strong&gt;offline maps&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Map Styles
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;MapKit&lt;/strong&gt;  allows us to switch between different styles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard&lt;/strong&gt;  (default street maps)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Imagery&lt;/strong&gt;  (satellite view)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid&lt;/strong&gt;  (satellite + roads)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Switching  &lt;strong&gt;map styles&lt;/strong&gt;  is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Map()
    .mapStyle(.hybrid(elevation: .realistic, showsTraffic: true))
    .mapStyle(.standard)
    .mapStyle(.imagery)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The  &lt;strong&gt;imagery map&lt;/strong&gt;  is particularly beautiful when you want to showcase  &lt;strong&gt;satellite imagery&lt;/strong&gt;  in your  &lt;strong&gt;ios application&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Map interaction modes
&lt;/h2&gt;

&lt;p&gt;Another important aspect of building with  &lt;strong&gt;MapKit&lt;/strong&gt;  is setting interaction modes. These define how users can interact with the  &lt;strong&gt;map content&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Map(interactionModes: [.all, .pan, .pitch, .rotate, .zoom])

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interaction modes can heavily impact your  &lt;strong&gt;app’s&lt;/strong&gt;  functionality when designing with  &lt;strong&gt;mapkit library&lt;/strong&gt;  and  &lt;strong&gt;offline maps&lt;/strong&gt;  use cases in mind.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summing up
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;MapKit&lt;/strong&gt;  is a wonderful tool for building powerful, intuitive maps in your  &lt;strong&gt;ios applications&lt;/strong&gt;  or  &lt;strong&gt;web apps&lt;/strong&gt;. We have seen how to use  &lt;strong&gt;MapKit framework&lt;/strong&gt; , how to manipulate the user’s  &lt;strong&gt;map view&lt;/strong&gt; , how to add  &lt;strong&gt;annotations&lt;/strong&gt; , how to use  &lt;strong&gt;routes&lt;/strong&gt;  and directions, and even how to change between different  &lt;strong&gt;map features&lt;/strong&gt;  like  &lt;strong&gt;satellite imagery&lt;/strong&gt;  and hybrid styles.&lt;/p&gt;

&lt;p&gt;Hopefully this article gives you the inspiration and technical skill to incorporate  &lt;strong&gt;MapKit&lt;/strong&gt;  into your own projects, build  &lt;strong&gt;new features&lt;/strong&gt; , and make your  &lt;strong&gt;content&lt;/strong&gt;  rich and &lt;a href="https://dev.to/bugfenderapp/creating-smooth-and-engaging-ui-with-swiftui-animations-594b"&gt;interactive&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>switui</category>
    </item>
    <item>
      <title>7 Best Practices for Remote App Debugging in 2025</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Fri, 02 May 2025 07:19:32 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/7-best-practices-for-remote-app-debugging-in-2025-2ned</link>
      <guid>https://forem.com/bugfenderapp/7-best-practices-for-remote-app-debugging-in-2025-2ned</guid>
      <description>&lt;p&gt;&lt;strong&gt;Remote debugging saves time but comes with challenges like security risks and performance issues.&lt;/strong&gt; In 2025, developers use advanced tools and strategies to debug apps running in distributed systems, microservices, and modern frameworks like &lt;a href="https://flutter.dev/" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt;, React, and &lt;a href="https://developer.apple.com/documentation/swiftui/" rel="noopener noreferrer"&gt;SwiftUI&lt;/a&gt;. Here’s a quick summary of the best practices to make remote debugging faster and more secure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secure Remote Access&lt;/strong&gt; : Use two-factor authentication, SSH tunneling, and TLS 1.3 to protect debugging sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-Time Error Tracking&lt;/strong&gt; : Monitor crashes and performance issues live with tools that capture stack traces and user impact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Device-Based Log Collection&lt;/strong&gt; : Gather logs for network requests, user actions, and performance metrics using structured and encrypted logging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaborative Debugging&lt;/strong&gt; : Use tools that allow multi-user access, live code editing, and secure data sharing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework-Specific Tools&lt;/strong&gt; : Leverage built-in debugging tools like Flutter DevTools, &lt;a href="https://reactnative.dev/" rel="noopener noreferrer"&gt;React Native&lt;/a&gt; DevTools, and &lt;a href="https://developer.android.com/develop/ui/compose/documentation" rel="noopener noreferrer"&gt;Jetpack Compose&lt;/a&gt; Hot Reload.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Monitoring&lt;/strong&gt; : Track app speed, network latency, and resource usage to ensure smooth user experiences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Reporting Systems&lt;/strong&gt; : Create templates for users to report bugs, including screenshots, steps to reproduce, and system details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quick Comparison of Remote vs. Local Debugging&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Remote Debugging&lt;/th&gt;
&lt;th&gt;Local Debugging&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infrastructure Costs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lower (no physical devices needed)&lt;/td&gt;
&lt;td&gt;Higher (requires local devices)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Debug across distributed systems&lt;/td&gt;
&lt;td&gt;Limited to available devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security Risks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Higher (requires strict protocols)&lt;/td&gt;
&lt;td&gt;Lower (local environment only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Connection Dependency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires stable internet&lt;/td&gt;
&lt;td&gt;Not dependent on connectivity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaway&lt;/strong&gt; : Implementing these practices can reduce debugging time by up to 30% and improve app stability. Start by securing access, using live error tracking, and leveraging framework-specific tools for efficient debugging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remote Debugging Complete Crash Course | Visual Studio …
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1. Set Up Secure Remote Access
&lt;/h2&gt;

&lt;p&gt;In today’s world of distributed systems, securing remote debugging is more critical than ever, especially with the rise in cyber threats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authentication and Access Control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start by enforcing strong authentication methods. Use tools like Google Authenticator or FIDO U2F Security Keys to add an extra layer of security. Here are some key practices to follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Two-Factor Authentication (2FA)&lt;/strong&gt;: Add an additional security step to user accounts to protect against unauthorized access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH Key Management&lt;/strong&gt; : Keep private keys secure, rotate them periodically, and treat them with the same care as passwords.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network Security&lt;/strong&gt; : Use network segmentation to isolate debugging environments and reduce the impact of potential breaches. Modern VPNs like WireGuard offer notable advantages, such as:FeatureBenefitAdvanced cryptographyStronger securitySimple architectureEasier to maintainBetter performanceFaster debugging sessions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Data Protection Measures&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Protect &lt;a href="https://bugfender.com/blog/category/debugging/" rel="noopener noreferrer"&gt;debugging data&lt;/a&gt; by using secure communication protocols like TLS 1.3 (or TLS 1.2 if compatibility is needed). For added security, tunnel JDWP traffic through SSH.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring and Compliance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Track and secure your debugging activities with these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintain detailed logs of all debugging sessions.&lt;/li&gt;
&lt;li&gt;Use automated monitoring tools to flag unusual activity.&lt;/li&gt;
&lt;li&gt;Conduct regular security audits and ensure compliance with industry standards like GDPR and ISO 27001.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For enterprise setups, consider using a dedicated SSH bastion host. This acts as a centralized access point, making it easier to monitor and manage security while protecting your debugging infrastructure. These measures lay the groundwork for integrating additional tools and team workflows effectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Use Device-Based Log Collection
&lt;/h2&gt;

&lt;p&gt;Device logs give you real-time insights into how your app behaves, even without physical access. Here’s how to set up effective and secure log collection.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Key Log Categories&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A &lt;a href="https://bugfender.com/remote-logging/" rel="noopener noreferrer"&gt;remote logging SDK&lt;/a&gt; can help capture essential log types, such as network requests and user actions. Here’s a breakdown:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Log Category&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Key Data Points&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network Requests&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monitor API interactions&lt;/td&gt;
&lt;td&gt;Response codes, timing, payload size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;User Actions&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Track user behavior&lt;/td&gt;
&lt;td&gt;Screen views, button taps, gestures&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance Metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Assess app performance&lt;/td&gt;
&lt;td&gt;Memory usage, frame rates, load times&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;System States&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Understand device context&lt;/td&gt;
&lt;td&gt;OS version, device model, storage space&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Advanced Logging Techniques&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Use structured logging with meaningful tags to simplify debugging. For example, tag network-related logs with “API” to easily filter and diagnose connection issues. This is especially useful for tracking down intermittent problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Securing Your Logs&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To protect sensitive data, follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;TLS 1.3 encryption&lt;/strong&gt; for secure log transfers.&lt;/li&gt;
&lt;li&gt;Apply strict access controls to limit who can view log data.&lt;/li&gt;
&lt;li&gt;Store passwords securely using key derivation functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Framework-Specific Tools&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you’re working with Flutter, the DevTools logging view is a powerful resource. It can capture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dart runtime events&lt;/li&gt;
&lt;li&gt;Framework-level activities&lt;/li&gt;
&lt;li&gt;App-specific logs&lt;/li&gt;
&lt;li&gt;Garbage collection metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Log Management Tips&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To make the most of your logs, consider these practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Define Log Levels&lt;/strong&gt; : Use categories like DEBUG, INFO, WARNING, and ERROR to prioritize issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set Retention Policies&lt;/strong&gt; : Keep logs only as long as necessary based on their importance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Context&lt;/strong&gt; : Include details like device type and session data to make debugging easier.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Set Up Live Error Tracking
&lt;/h2&gt;

&lt;p&gt;Monitoring errors as they happen is crucial for maintaining app stability, especially in distributed development environments. With teams often spread across various locations, keeping an eye on issues in real time ensures your app runs smoothly and delivers a solid user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Key Components of an Error Tracking System&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;An effective error tracking setup should include the following:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Key Metrics&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://bugfender.com/crash-reporting/" rel="noopener noreferrer"&gt;Crash Reporting&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Logs crashes and exceptions&lt;/td&gt;
&lt;td&gt;Stack traces, affected users, crash rates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error Context&lt;/td&gt;
&lt;td&gt;Provides detailed context&lt;/td&gt;
&lt;td&gt;User actions, device state, network status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance Monitoring&lt;/td&gt;
&lt;td&gt;Tracks app stability&lt;/td&gt;
&lt;td&gt;Response times, memory usage, CPU load&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User Impact Analysis&lt;/td&gt;
&lt;td&gt;Evaluates error severity&lt;/td&gt;
&lt;td&gt;Users affected, session data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;How to Implement&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Set up a secure error tracking system that collects meaningful data while respecting user privacy. Make sure to capture details like stack traces, device specs, user actions, and network conditions at the time of errors. This rich data helps you assess the impact on users and debug efficiently.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Value in Action&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Error tracking tools can dramatically improve your app development process. Alexis Caruana from &lt;a href="https://www.se.com/us/en/" rel="noopener noreferrer"&gt;Schneider Electric&lt;/a&gt; shared their experience:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Bugfender has significantly improved our bug resolution process. The ability to share logs and feedback instantly between our globally dispersed teams has streamlined our workflow and accelerated our development timelines.” &lt;a href="https://bugfender.com/remote-logging" rel="noopener noreferrer"&gt;&lt;sup&gt;[4]&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Keeping Data Secure&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When setting up error tracking, prioritize security with these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encrypt data transfers&lt;/strong&gt; using TLS 1.3 for maximum protection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control access&lt;/strong&gt; to error logs to ensure only authorized personnel can view them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set retention policies&lt;/strong&gt; that align with legal and compliance requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anonymize sensitive data&lt;/strong&gt; to safeguard user privacy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By securing your error tracking system, you can safely integrate it into your team’s workflow without compromising data integrity.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Best Practices for Distributed Teams&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For teams spread out across different locations, clear protocols are essential. Here are some tips to stay on top of errors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure notifications for critical errors affecting multiple users.&lt;/li&gt;
&lt;li&gt;Use automated grouping to spot recurring issues.&lt;/li&gt;
&lt;li&gt;Add custom tags to categorize errors effectively.&lt;/li&gt;
&lt;li&gt;Monitor severe problems in real time to address them quickly.&lt;/li&gt;
&lt;li&gt;Develop workflows to prioritize and resolve errors based on their impact.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“Bugfender is crucial for our product. Without it, we would be running blind. Its real-time logging and feedback features are indispensable for testing with our beta users and resolving issues on production.” &lt;a href="https://bugfender.com/remote-logging" rel="noopener noreferrer"&gt;&lt;sup&gt;[4]&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Enable Team Debug Coordination
&lt;/h2&gt;

&lt;p&gt;Remote debugging works best when the entire team is on the same page. Building on secure access and error tracking, coordination ensures smoother collaboration and faster resolutions.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Real-Time Collaboration Tools&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Debugging today demands tools that allow teams to work together seamlessly and provide instant feedback. Here are some key features to look for:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Multi-user Access&lt;/td&gt;
&lt;td&gt;Allows multiple users to access apps or windows simultaneously&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live Code Editing&lt;/td&gt;
&lt;td&gt;Enables collaborative debugging in real-time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secure Data Sharing&lt;/td&gt;
&lt;td&gt;Ensures sensitive data stays protected during sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integrated Communication&lt;/td&gt;
&lt;td&gt;Facilitates contextual discussions within the debugging platform&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Improving Debug Communication&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Having great tools is only part of the equation. Clear communication practices are just as important for resolving issues quickly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Centralize tracking and standardize procedures&lt;/strong&gt; : This reduces confusion and speeds up bug fixes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Securely share debugging data&lt;/strong&gt; : Protect sensitive information when collaborating. As Microsoft Learn advises: “You should only share your code, content, and applications with people you trust” &lt;a href="https://learn.microsoft.com/en-us/visualstudio/liveshare/reference/security" rel="noopener noreferrer"&gt;&lt;sup&gt;[5]&lt;/sup&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Success Stories&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Real-world examples highlight the power of coordinated debugging. &lt;a href="https://www.netflix.com/" rel="noopener noreferrer"&gt;Netflix&lt;/a&gt;, for instance, processes over 500 billion events daily through its analytics platform. This setup allows the team to detect and resolve streaming issues almost immediately. As a result, their average bug resolution time has dropped from days to just hours.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Security Measures&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;When running remote debugging sessions, keep these security steps in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use end-to-end encryption for all shared sessions.&lt;/li&gt;
&lt;li&gt;Assign unique identifiers to each session.&lt;/li&gt;
&lt;li&gt;Apply strict access controls to debugging data.&lt;/li&gt;
&lt;li&gt;Maintain detailed logs of all activities during debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Tips for Remote Debugging Sessions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To get the most out of your remote debugging efforts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schedule regular sync meetings to avoid overlapping work.&lt;/li&gt;
&lt;li&gt;Document all decisions in your issue tracker for clarity and accountability.&lt;/li&gt;
&lt;li&gt;Use screen-sharing tools that allow multi-user control.&lt;/li&gt;
&lt;li&gt;Enable cross-user copy/paste functionality to streamline code sharing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Apply Framework-Specific Debug Tools
&lt;/h2&gt;

&lt;p&gt;Using tools tailored to specific frameworks can make remote debugging more effective by leveraging each technology’s unique features.&lt;/p&gt;

&lt;p&gt;Frameworks today come with built-in debugging tools designed to tackle their specific challenges. Here’s a quick look at some popular frameworks and their key debugging features:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Key Features&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Flutter&lt;/td&gt;
&lt;td&gt;DevTools, Hot Reload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React Native&lt;/td&gt;
&lt;td&gt;DevTools, &lt;a href="https://reactnative.dev/docs/hermes" rel="noopener noreferrer"&gt;Hermes Engine&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jetpack Compose&lt;/td&gt;
&lt;td&gt;Hot Reload, Layout Inspector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SwiftUI&lt;/td&gt;
&lt;td&gt;Xcode Previews, Time Profiler&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For example, the March 2025 release of Compose Hot Reload (v1.0.0-alpha02) introduced faster UI inspection for desktop apps. While it’s still in beta, keep in mind that some migration tweaks might be necessary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation Guidelines
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Flutter&lt;/strong&gt;
To unlock Flutter’s debugging capabilities, run &lt;code&gt;flutter upgrade&lt;/code&gt;. You’ll gain access to tools for performance profiling, memory analysis, and network inspection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React Native&lt;/strong&gt;
React Native’s DevTools works seamlessly with Hermes-enabled apps. It allows real-time inspection of components and monitoring of network activity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jetpack Compose&lt;/strong&gt;
Add the &lt;code&gt;org.jetbrains.compose.hot-reload&lt;/code&gt; Gradle plugin to your build script to enable near-instant UI updates. This feature has already shown its usefulness in projects like Suhyeon Kim’s “Would You Rather Game” in March 2025.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Security Measures for Debugging
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Turn off debug tools in production environments.&lt;/li&gt;
&lt;li&gt;Use conditional imports to separate debugging features from production code.&lt;/li&gt;
&lt;li&gt;Ensure secure data transmission during debugging sessions.&lt;/li&gt;
&lt;li&gt;Keep logs of debugging activities for accountability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tips for Better Integration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use the same debugging tools across your team to maintain consistency.&lt;/li&gt;
&lt;li&gt;Automate issue reporting to save time.&lt;/li&gt;
&lt;li&gt;Schedule regular performance profiling to catch issues early.&lt;/li&gt;
&lt;li&gt;Create clear protocols for remote debugging to keep everyone aligned.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Track App Speed and Network Status
&lt;/h2&gt;

&lt;p&gt;Did you know that improving load times by just one second can increase conversion rates by 27% and reduce abandonment by 70%? &lt;a href="https://medium.com/@flutterwtf/mobile-app-performance-how-to-improve-and-test-it-0d4f0a9db3c7" rel="noopener noreferrer"&gt;&lt;sup&gt;[6]&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Performance Metrics
&lt;/h3&gt;

&lt;p&gt;To deliver a smooth user experience, keep an eye on these metrics:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric Type&lt;/th&gt;
&lt;th&gt;Target Value&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cold Launch&lt;/td&gt;
&lt;td&gt;2–4 seconds&lt;/td&gt;
&lt;td&gt;Time for initial app start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Warm Launch&lt;/td&gt;
&lt;td&gt;2–3 seconds&lt;/td&gt;
&lt;td&gt;Time to resume from background&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hot Launch&lt;/td&gt;
&lt;td&gt;1–1.5 seconds&lt;/td&gt;
&lt;td&gt;Time for memory-cached start&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI Response&lt;/td&gt;
&lt;td&gt;Under 100ms&lt;/td&gt;
&lt;td&gt;Feedback from user interactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frame Render&lt;/td&gt;
&lt;td&gt;17ms per frame&lt;/td&gt;
&lt;td&gt;Ensures smooth animations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network Response&lt;/td&gt;
&lt;td&gt;Under 1 second&lt;/td&gt;
&lt;td&gt;Speed for API requests&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Network Performance Monitoring
&lt;/h3&gt;

&lt;p&gt;AI is transforming network monitoring by detecting issues and providing real-time diagnostics &lt;a href="https://uptrace.dev/tools/network-monitoring-tools" rel="noopener noreferrer"&gt;&lt;sup&gt;[7]&lt;/sup&gt;&lt;/a&gt;. For example, SPI Health and Safety used &lt;a href="https://dev.to/bugfenderapp/how-ai-is-revolutionizing-the-development-process-264j"&gt;AI-driven tools&lt;/a&gt; to improve response times significantly &lt;a href="https://obkio.com/blog/best-remote-network-monitoring-software" rel="noopener noreferrer"&gt;&lt;sup&gt;[8]&lt;/sup&gt;&lt;/a&gt;. These insights enable faster, more precise performance fixes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Set Up Performance Baselines&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Monitor core indicators like CPU usage, memory consumption, and network latency. Aim for client-side response times under 1 second and ensure images are compressed to under 100KB &lt;a href="https://medium.com/@flutterwtf/mobile-app-performance-how-to-improve-and-test-it-0d4f0a9db3c7" rel="noopener noreferrer"&gt;&lt;sup&gt;[9]&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Configure Network Analysis&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Enable detailed logging for network events and set up automatic retries for connection timeouts. Addressing bottlenecks isn’t just about speed – it’s about creating a seamless experience.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Optimize Resource Usage&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keep tabs on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Battery usage patterns&lt;/li&gt;
&lt;li&gt;Memory allocation and potential leaks&lt;/li&gt;
&lt;li&gt;Network bandwidth consumption&lt;/li&gt;
&lt;li&gt;Cache performance metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best Practices for Remote Monitoring
&lt;/h3&gt;

&lt;p&gt;To maintain top performance, combine these steps with automated alerts and regular profiling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up alerts for when performance thresholds are exceeded&lt;/li&gt;
&lt;li&gt;Use compression to reduce network payload sizes&lt;/li&gt;
&lt;li&gt;Conduct periodic performance profiling&lt;/li&gt;
&lt;li&gt;Allow remote access to metrics for team debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Create Clear User Report Systems
&lt;/h2&gt;

&lt;p&gt;In addition to secure access, live error tracking, and coordinated team debugging, having a clear user reporting system is key to effective remote troubleshooting. These systems can help organizations save up to $125,000 annually by speeding up bug detection and resolution &lt;a href="https://marker.io/blog/bug-report" rel="noopener noreferrer"&gt;&lt;sup&gt;[10]&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Elements of Effective User Reports
&lt;/h3&gt;

&lt;p&gt;A detailed bug report should include the following components:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visual Proof&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Screenshots or recordings&lt;/td&gt;
&lt;td&gt;Speeds up issue verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Environment Details&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Device, OS, app version&lt;/td&gt;
&lt;td&gt;Helps replicate the issue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Console Logs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;System messages and errors&lt;/td&gt;
&lt;td&gt;Adds technical context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Steps to Reproduce&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Exact user actions&lt;/td&gt;
&lt;td&gt;Ensures the issue is reproducible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Expected vs. Actual&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Comparison of outcomes&lt;/td&gt;
&lt;td&gt;Makes the problem clearer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To make these reports even more effective, integrate them with &lt;a href="https://bugfender.com/in-app-user-feedback/" rel="noopener noreferrer"&gt;in-app feedback&lt;/a&gt; tools to gather additional context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using In-App Feedback
&lt;/h3&gt;

&lt;p&gt;Incorporating in-app feedback systems can reduce negative reviews by up to 80%. For example, Schneider Electric implemented a reporting tool that allowed instant log sharing across global teams, &lt;a href="https://bugfender.com/resources/case-studies/schneider-electric/" rel="noopener noreferrer"&gt;drastically speeding up their bug resolution process&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tips for Managing User Reports
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use Standardized Templates&lt;/strong&gt; : Predefined templates can cut debugging time by 45%. Include fields for environment details, reproduction steps, and expected results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate Assignments&lt;/strong&gt; : Automatically route bug reports to the right teams, such as UI/UX, backend, or security teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrate with Development Tools&lt;/strong&gt; : Link your reporting system to project management tools to streamline the debugging process between users, testers, and developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Real-World Example
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“Bugfender helped us to fix a production level bug in our app that we just couldn’t replicate ourselves.” – John Jordan, Disney&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Keeping Reports Secure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Encrypt all data during transmission.&lt;/li&gt;
&lt;li&gt;Use secure authentication and access controls.&lt;/li&gt;
&lt;li&gt;Follow GDPR guidelines for log storage and data protection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Remote Debugging Benefits and Limitations
&lt;/h2&gt;

&lt;p&gt;Balancing the pros and cons of remote and local debugging is key to optimizing your development workflow. Here’s a breakdown of how the two approaches stack up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost-Benefit Analysis
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Remote Debugging&lt;/th&gt;
&lt;th&gt;Local Debugging&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infrastructure Costs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lower hardware requirements and reduced expenses&lt;/td&gt;
&lt;td&gt;Higher initial investment in testing devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time Efficiency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Speeds up time to market by around 20%&lt;/td&gt;
&lt;td&gt;Faster iterations but limited by device access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource Usage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minimal reliance on local resources&lt;/td&gt;
&lt;td&gt;Heavy use of local machine resources&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can test across many devices, regardless of location&lt;/td&gt;
&lt;td&gt;Restricted by available physical devices&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Performance Considerations
&lt;/h3&gt;

&lt;p&gt;These advantages come with some technical challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connection Stability&lt;/strong&gt; : Remote debugging relies on a stable, low-latency internet connection. High latency or network disruptions can significantly impact the debugging process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Risks&lt;/strong&gt; : According to NIST, &lt;strong&gt;42% of vulnerabilities&lt;/strong&gt; in open-source software arise from poor resource management during debugging. To counter these risks, teams should enforce:

&lt;ul&gt;
&lt;li&gt;Strong authentication protocols&lt;/li&gt;
&lt;li&gt;Encrypted connections&lt;/li&gt;
&lt;li&gt;Careful handling of sensitive data to avoid accidental exposure during logging&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;These precautions align with secure practices outlined in earlier sections, ensuring a safer debugging environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-World Impact
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;“Remote debugging doesn’t always deal with a remote machine, we often need it when debugging into Kubernetes or Docker.” – Shai Almog&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This insight underscores how remote debugging has expanded beyond traditional setups. Forrester Research found that companies using remote debugging tools can reduce their time to market by up to &lt;strong&gt;20%&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Limitations
&lt;/h3&gt;

&lt;p&gt;Despite its advantages, remote debugging has some notable drawbacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Susceptibility to network latency and outages&lt;/li&gt;
&lt;li&gt;Security vulnerabilities, particularly with JDWP&lt;/li&gt;
&lt;li&gt;Dependence on reliable connectivity for effective operation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best Practices for Mitigation
&lt;/h3&gt;

&lt;p&gt;To address these challenges, consider the following measures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;SSH tunneling&lt;/strong&gt; for secure connections&lt;/li&gt;
&lt;li&gt;Apply &lt;strong&gt;minimal privilege access controls&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Keep debugging environments &lt;strong&gt;isolated from production systems&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Maintain detailed logs of all debugging sessions for accountability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Remote debugging has become a core aspect of modern app development, directly influencing both development efficiency and the quality of applications. By following best practices, teams can address challenges in distributed environments while improving outcomes.&lt;/p&gt;

&lt;p&gt;Incorporating &lt;em&gt;secure access&lt;/em&gt;, &lt;em&gt;detailed logging&lt;/em&gt;, &lt;em&gt;live error tracking&lt;/em&gt;, &lt;em&gt;collaborative debugging&lt;/em&gt;, &lt;em&gt;framework-specific tools&lt;/em&gt;, &lt;em&gt;performance monitoring&lt;/em&gt;, and &lt;em&gt;user reporting&lt;/em&gt; leads to clear advantages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Development Impact&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allows developers to interact directly with applications in real-world settings.&lt;/li&gt;
&lt;li&gt;Facilitates collaboration among distributed teams in real time.&lt;/li&gt;
&lt;li&gt;Provides structured monitoring for better insights.&lt;/li&gt;
&lt;li&gt;Simplifies resolving issues in complex systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Performance Benefits&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Speeds up identifying and fixing problems.&lt;/li&gt;
&lt;li&gt;Improves application stability with robust error handling.&lt;/li&gt;
&lt;li&gt;Detects potential issues early.&lt;/li&gt;
&lt;li&gt;Enhances user experience through ongoing monitoring.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“At the same time, multi-client remote debugging allows for a more realistic debugging scenario as typical servers establish multiple asynchronous connections with clients.” – Tom Granot, Chief Growth Officer at Stealth&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By focusing on consistent logging, strong error management, and secure communication channels, teams can produce higher-quality applications while navigating the challenges of remote work &lt;a href="https://anmolsehgal.medium.com/mastering-remote-debugging-4284b8251d24" rel="noopener noreferrer"&gt;&lt;sup&gt;[11]&lt;/sup&gt;&lt;/a&gt;. As remote debugging tools continue to evolve, they play an increasingly important role in supporting distributed development and ensuring reliable applications.&lt;/p&gt;

&lt;p&gt;Adopting these practices not only strengthens your development workflow but also enhances application performance in today’s distributed environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What security measures should you take when setting up remote debugging for distributed systems?
&lt;/h3&gt;

&lt;p&gt;When configuring remote debugging for distributed systems, it’s essential to prioritize security to protect both your application and sensitive data. Use &lt;strong&gt;secure connections&lt;/strong&gt; like SSH tunnels or VPNs to safeguard communication, and implement &lt;strong&gt;strict access controls&lt;/strong&gt; to ensure only authorized personnel can access the debugging environment.&lt;/p&gt;

&lt;p&gt;Additionally, configure &lt;strong&gt;firewall rules&lt;/strong&gt; to restrict unnecessary access, keep your debugging tools &lt;strong&gt;up-to-date&lt;/strong&gt; with the latest patches, and disable remote debugging when it’s not actively in use. These practices help maintain a secure and efficient debugging process in distributed environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the best ways for developers to collaborate effectively during remote debugging sessions?
&lt;/h3&gt;

&lt;p&gt;To collaborate effectively during remote debugging sessions, developers can focus on clear communication, shared tools, and streamlined workflows. Use &lt;strong&gt;dedicated communication tools&lt;/strong&gt; like Slack or Zoom for real-time discussions, and establish clear guidelines for response times and preferred methods of communication. Maintain a &lt;strong&gt;centralized knowledge repository&lt;/strong&gt; (e.g., Notion or Google Docs) to document processes, share insights, and avoid knowledge silos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Version control systems&lt;/strong&gt; like GitHub or GitLab are essential for managing code and ensuring consistency, while collaborative debugging tools can help teams reproduce and resolve issues together. Additionally, plan tasks around time zones, set overlapping work hours for live collaboration, and use asynchronous methods when needed. These practices foster teamwork, improve efficiency, and ensure smoother remote debugging sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  What are the best tools for debugging apps remotely in 2025, and how do they improve the process for popular frameworks?
&lt;/h3&gt;

&lt;p&gt;For remote debugging in 2025, leveraging framework-specific tools can significantly enhance efficiency and accuracy. &lt;strong&gt;Jetpack Compose&lt;/strong&gt; developers can use tools like Android Studio’s Composable Preview, Live Edit, and Layout Inspector to visualize UI changes in real-time and troubleshoot layout issues. Similarly, &lt;strong&gt;SwiftUI&lt;/strong&gt; benefits from Xcode’s Previews and Debug Navigator for streamlined debugging workflows.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;Flutter&lt;/strong&gt; , the DevTools suite offers powerful features like the Widget Inspector for analyzing UI hierarchies and the Performance View for monitoring app performance under different conditions. &lt;strong&gt;React&lt;/strong&gt; and &lt;strong&gt;Angular&lt;/strong&gt; developers can rely on browser-based debugging tools like &lt;a href="https://bugfender.com/blog/category/javascript/react/" rel="noopener noreferrer"&gt;React Developer Tools&lt;/a&gt; and &lt;a href="https://bugfender.com/blog/category/javascript/angular/" rel="noopener noreferrer"&gt;Angular DevTools&lt;/a&gt; to inspect component trees and track state changes effectively.&lt;/p&gt;

&lt;p&gt;These tools are designed to simplify the debugging process, reduce errors, and improve collaboration in distributed development teams, ensuring apps perform optimally across platforms.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>engineering</category>
    </item>
    <item>
      <title>How to Read and Analyze iOS Crash Reports: A Developer’s Guide</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Tue, 29 Apr 2025 09:55:37 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/how-to-read-and-analyze-ios-crash-reports-a-developers-guide-41nn</link>
      <guid>https://forem.com/bugfenderapp/how-to-read-and-analyze-ios-crash-reports-a-developers-guide-41nn</guid>
      <description>&lt;p&gt;The crash-proof app doesn’t exist. It never has, and it probably never will. Because apps can crash for all kinds of reasons, some of them impossible to foresee. No matter how well we build them, crashes are going to happen to our apps.&lt;/p&gt;

&lt;p&gt;So, &lt;a href="https://bugfender.com/guides/ios-app-development/" rel="noopener noreferrer"&gt;as devs&lt;/a&gt;, we need to know how to react to a crash when it happens. And in this context, understanding &lt;a href="https://dev.to/bugfenderapp/best-mobile-crash-reporting-tools-for-2025-features-pros-and-cons-4khc"&gt;crash reports&lt;/a&gt; is crucial. They provide the clues we need to put the pieces together.&lt;/p&gt;

&lt;p&gt;In this article, we’re going to look specifically at understanding iOS crash reports, so you can find the root cause and track it, and ultimately fix problems faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick summary of what you’ll learn today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What crashes actually are, with examples.&lt;/li&gt;
&lt;li&gt;How to use crash reporting and the crash &lt;a href="https://dev.to/bugfenderapp/swift-logging-techniques-a-complete-guide-to-ios-logging-46gg"&gt;log&lt;/a&gt; to figure out the root cause.&lt;/li&gt;
&lt;li&gt;How stack traces and other crash data can make our lives (waaaayyyy) easier.&lt;/li&gt;
&lt;li&gt;How to avoid crashes through proactive use of crash reporting and logging tools.&lt;/li&gt;
&lt;li&gt;How you can find the right iOS crash reporting tool for you, and go beyond the simple crash report to achieve proactive defense.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before we go on, a quick note to say that yes, we offer a crash reporting tool at Bugfender. But we’ll try to be as objective as we can.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ok so first, what &lt;em&gt;are&lt;/em&gt; crashes?
&lt;/h2&gt;

&lt;p&gt;An &lt;a href="https://dev.to/bugfenderapp/how-to-fix-crashing-apps-on-an-iphone-or-android-4djb-temp-slug-8333259"&gt;app crash&lt;/a&gt; is any force termination of our app that happens due to an unhandled exception. Those exceptions can take many forms. Here are just a few:&lt;/p&gt;

&lt;h3&gt;
  
  
  Index out of range
&lt;/h3&gt;

&lt;p&gt;This is a particularly common exception type, and occurs when we try to access an index, on any collection, that does not exist. You can easily reproduce an index out of range instance, with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let array = ["1"]
print(array[2])

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Force unwrapping
&lt;/h3&gt;

&lt;p&gt;Any time we force unwrap, there is the possibility of creating an app crash. That’s why, in general, it is advisable to use safer coding practices. As an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var optionalValue: String? = nil
print(optionalValue!)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dividing by zero
&lt;/h3&gt;

&lt;p&gt;Another common termination reason is the failure to put any checks into our code, and when we come to a division and the divisor is 0:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func divideTenBy(divisor: Int) -&amp;gt; Int {
    return 10 / divisor;
}
...

divideTenBy(0)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Invalid casting of types
&lt;/h3&gt;

&lt;p&gt;When we force out a cast to an unrelated type, we also get a crash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    let a = 10

    print(a as! String)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We could go on here, because, as you’ll know, there are lots of ways to crash an app, and even if we avoid any of these mistakes, our users can trigger a crash by exposing unexpected edge cases.&lt;/p&gt;

&lt;p&gt;So now, let’s look at what action we can take, starting with the stack trace, the most important analytics data at your disposal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading a stack trace
&lt;/h2&gt;

&lt;p&gt;A stack trace is the most crucial crash report we can obtain, because it tells us exactly what was happening when the crash unfolded.&lt;/p&gt;

&lt;p&gt;Specifically, it shows us the method calls that our iOS app was processing when everything went wrong. With stack traces, we can see who was calling who at the moment of the crash, and pinpoint where the execution stopped.&lt;/p&gt;

&lt;p&gt;For example, if you run the previously given example of the invalid casting type and run an app with it, you will get something akin to:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://bugfender.com/wp-content/uploads/2025/04/Screenshot_2025-02-13_at_13.38.09.webp" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7c2rudn45ks075yz06oc.webp" width="520" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ViewModel.init shows exactly where we had our crash. By looking specifically at this, we can determine what was causing the unhandled exception.&lt;/p&gt;

&lt;p&gt;Additional tip: while running your iOS app, you can check, or log, your stack trace at any time simply by using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for symbol: String in Thread.callStackSymbols {
    print(symbol)
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finding device crash logs
&lt;/h2&gt;

&lt;p&gt;Now let’s take things a stage further. If you have physical access to an iOS device that has crashed, you can access the &lt;a href="https://bugfender.com/remote-logging/" rel="noopener noreferrer"&gt;logs on the device&lt;/a&gt; directly to get priceless debug information.&lt;/p&gt;

&lt;p&gt;To do this, open up Xcode, then, on the Window Menu, open Devices and Simulators:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsm3lu4gzhnnshadvru6.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsm3lu4gzhnnshadvru6.webp" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we can choose a device and look at the device’s logs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj50ti4eb6mj607m16qf7.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj50ti4eb6mj607m16qf7.webp" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we’ve got access to the stack trace, just like we saw earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Symbolication
&lt;/h2&gt;

&lt;p&gt;Symbolication is the translation of a crash’s information from memory addresses into a more humanly readable format. As an example how would you prefer to look at a crash report, A, or B?&lt;/p&gt;

&lt;p&gt;A.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread 0 Crashed:
0 App 0x0000000103478abc 0x103470000 + 35516
1 App 0x0000000103478f32 0x103470000 + 36658

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;B.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Thread 0 Crashed:
0 App 0x0000000103478abc -[ViewController viewDidLoad] + 40
1 App 0x0000000103478f32 myScreen + 22

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is obviously B, since we can tell where things went wrong in that, while A is just an unreadable stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does it work?
&lt;/h3&gt;

&lt;p&gt;For us to be able to symbolicate crashes, we need dSYM (Debug Symbol) files that are generated in each build of our app. You can download these dSYM files from App Store Connect by going:&lt;/p&gt;

&lt;p&gt;Your App &amp;gt; TestFlight tab &amp;gt; Choose the build you’d like to have dSYMs for &amp;gt; Build Metadata tab &amp;gt; Download dSYMs button&lt;/p&gt;

&lt;p&gt;Once you have your dSYM, you are able to symbolicate it to be more readable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Symbolicating a dSYM
&lt;/h3&gt;

&lt;p&gt;Xcode is the preferred way to symbolicate crash reports because it uses all available dSYM files on your Mac at once.&lt;/p&gt;

&lt;p&gt;To symbolicate in Xcode, click the Device Logs button in the Devices and Simulators Window, that you can access from the Windows menu button on top, then you just need drag and drop the crash report file into the &lt;a href="https://dev.to/bugfenderapp/complete-guide-to-swiftui-lists-for-developers-2b2k"&gt;list&lt;/a&gt; of device logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoiding crashes
&lt;/h2&gt;

&lt;p&gt;Now as we said at the top, there will always be an issue somewhere in your apps, and it’s nigh-on impossible to make them bug-free. However, there &lt;em&gt;is&lt;/em&gt; a way we can avoid many common issues, and it’s known as defensive coding.&lt;/p&gt;

&lt;p&gt;We will now go back through all the crashes we showed earlier, and using simple safe coding practices, we will make changes to fix the issues shown:&lt;/p&gt;

&lt;h3&gt;
  
  
  Index out of range
&lt;/h3&gt;

&lt;p&gt;Checking the index beforehand is the best way to avoid an inexistent index. In our case, if we wanted to still be able to access the index 2, here’s how we’d do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let array = ["1"]

if(array.count &amp;gt;= 3) {
   print(array[2])
} else {
...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Force unwrapping
&lt;/h3&gt;

&lt;p&gt;There are many ways to avoid force-unwrapping, from guards to &lt;em&gt;if lets&lt;/em&gt;, to default values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var optionalValue: String? = nil

guard let value = optionalValue else { return }

print(value)

---//---

if let value = optionalValue { 
    print(value) 
}

---//---

print(optionalValue ?? "")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dividing by zero
&lt;/h3&gt;

&lt;p&gt;One of the approaches we can take is to check whether the divisor is 0. If it is, just return 0.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func divideTenBy(divisor: Int) -&amp;gt; Int {

    return divisor &amp;gt; 0 ? (10 / divisor) : 0;
}
...

divideTenBy(0)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Invalid Casting of types
&lt;/h3&gt;

&lt;p&gt;We should always check for the type &lt;em&gt;with is&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    let a = 10
    if(a is String) {
        print(a as! String)
    }


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Crash and error reporting tools
&lt;/h2&gt;

&lt;p&gt;Ok, so we´ve got a basic grasp of how to catch errors in our apps. But the best way to understand the issues that our apps face in our users’ devices is to have a platform that provides total visibility over all issues that show up.&lt;/p&gt;

&lt;p&gt;As we write this piece in 2025, there are two primary crash reporting tools that provide this kind of service, and one is Bugfender. Below, we’ll give you a quick (and we hope, fair) overview of each:&lt;/p&gt;

&lt;h3&gt;
  
  
  Bugfender
&lt;/h3&gt;

&lt;p&gt;One of the key USPs of Bugfender is that it supports both crashes and general device logs. In other words, the program logs &lt;em&gt;everything&lt;/em&gt;, even when the app is running fine. As well as being a great crash analytics tool, it allows you to carry out proactive, round-the-clock &lt;a href="https://dev.to/bugfenderapp/optimize-your-ios-app-perfomance-using-metrickit-41cg"&gt;performance monitoring&lt;/a&gt; of your app quality and app performance.&lt;/p&gt;

&lt;p&gt;You can configure the priority of all different kinds of performance issues, and also configure how you get alerts of said issues. With the logs displayed alongside the crashes, you get a truly complete crash report, so you can truly understand what was happening on the device at the time of the crash.&lt;/p&gt;

&lt;p&gt;If you’d like to incorporate Bugfender into your app, we’ve recently made an article on how you can do so using SwiftUI and also integrate Alamofire. you can read the piece &lt;a href="https://dev.to/bugfenderapp/using-alamofire-and-integrating-it-with-bugfender-9an"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Firebase Crashlytics
&lt;/h3&gt;

&lt;p&gt;Firebase Crashlytics is a pure crash reporter, and it has lots of real plus-points, notably the fact that it automatically collects crash information alongside application specific information about the device.&lt;/p&gt;

&lt;p&gt;The main downside when comparing to Bugfender is that Crashlytics will not capture logs by default, nor does it automatically support non-crashing issues. You can manually log any errors that don’t lead to crashes, and it’s really simple, but you can’t add logs to Crashlytics.&lt;/p&gt;

&lt;p&gt;So if you’re purely interested in the crash details, this is a great option. If you want a more proactive product, we’d recommend ours,but in reality, both will give you a valuable crash report.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summing up
&lt;/h2&gt;

&lt;p&gt;it is near impossible to have an app without crash occurrences, especially in the early stages of the build. However there &lt;em&gt;are&lt;/em&gt; ways to avoid crashes, using defensive coding, and also ways to figure out where the issues lie.&lt;/p&gt;

&lt;p&gt;What’s more, there are platforms we can use to help us track those issues and have an overview of how often/when they occur to help us mitigate them and fix them as fast as possible.&lt;/p&gt;

&lt;p&gt;Hopefully this article has helped you in any way understanding iOS crashes. If you want any more info on how to analyze a crash report, and compare the different crash report tools on the market, we’d be happy to help.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>switui</category>
    </item>
    <item>
      <title>Android Emulator Setup Guide for App Testing</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Tue, 22 Apr 2025 12:01:02 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/android-emulator-setup-guide-for-app-testing-4018</link>
      <guid>https://forem.com/bugfenderapp/android-emulator-setup-guide-for-app-testing-4018</guid>
      <description>&lt;p&gt;The Android emulator is an Android virtual device that is used to test and debug &lt;a href="https://bugfender.com/guides/android-app-development-guide/" rel="noopener noreferrer"&gt;Android code&lt;/a&gt;. It looks and behaves the same as a physical Android device, and while it has some limitations (more of which later), it provides real time and cost savings for developers.&lt;/p&gt;

&lt;p&gt;Unlike the iPhone, which is limited to one manufacturer and a specific number of iOS versions, there are thousands of Android device types out there. Android themselves say there are over 24,000 (yep, that’s not a mis-print) different device types worldwide.&lt;/p&gt;

&lt;p&gt;As devs, we simply can’t have physical access to all these devices when building Android app. However, with Android Emulators, we can easily install the APK/AAB file and test the app code on loads of different types, without a physical device. In fact, a single emulator allows us to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test multiple instances and customize resources for better performance.&lt;/li&gt;
&lt;li&gt;Easily change device configuration and API level and test the code in various API labels.&lt;/li&gt;
&lt;li&gt;Import hardware profiles to test our app.&lt;/li&gt;
&lt;li&gt;Debug the code and execute the automation code script.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, the Bugfender team thought we’d do an article on this awesome tech. We’re going to show you how to set up an Android Emulator, so you can bring this vital technology into your Android developer toolkit, and give you loads of hints and tips to customize the emulator to your specific needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who this article is for
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Rookie Android devs and small software companies who want to make their Android application testing more efficient via virtual machine technology.&lt;/li&gt;
&lt;li&gt;Those with a geographically diverse audience whose app will be used on loads of different devices.&lt;/li&gt;
&lt;li&gt;Those who are building Google Play games, which will naturally throw up all kinds of edge cases.&lt;/li&gt;
&lt;li&gt;Those who are getting used to Android O and want a way to test for all the challenges it provides.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ready to go? Then let’s get into the weeds. And, just to be double-clear, we’re talking about the official Android Studio Emulator here (we’ll mention some more niche alternatives further down).&lt;/p&gt;

&lt;h2&gt;
  
  
  Ok, first, let’s start with the tools we’ll need
&lt;/h2&gt;

&lt;p&gt;In Android Studio, we can use lots of tools to test the app with Android Emulators. The tools are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Android Virtual Device Manager (AVD Manager)&lt;/strong&gt;, a command-line tool that allows us to create the virtual device in Android Studio. AVD Manager provides an option to customize the virtual Android device, such as RAM size , storage and API version.&lt;/li&gt;
&lt;li&gt;With the help of a &lt;strong&gt;command&lt;/strong&gt; , we can install the APK file in the emulator and test it. This helps us to pull and push the code from Git using the Git command.&lt;/li&gt;
&lt;li&gt;It’s also important to use &lt;strong&gt;Logcat&lt;/strong&gt; , another command-line tool, in tandem with Android Studio. This helps us to analyze the real-time system logs and debugging messages from the emulator, and &lt;a href="https://bugfender.com/platforms/android/" rel="noopener noreferrer"&gt;find out the exact cause of an app crash&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Now let’s create an Android Emulator
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Step 1: Android Studio and Java setup&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;First, download Android Studio and complete the installation process, which is very intuitive. You need to install Java on your machine to run Android Studio, and don’t forget to set HOME_PATH on your computer.&lt;/p&gt;

&lt;p&gt;If you are using Linux or Mac, then you need to install through the command prompt. You should not forget to select the Android SDK and emulator components during installation. Android Studio will launch after installation.&lt;/p&gt;

&lt;p&gt;Now, move to the &lt;code&gt;Tools&lt;/code&gt; menu and click SDK manager. Take a look at the image below for reference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkf9nelsivdhxb0j7rx6c.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkf9nelsivdhxb0j7rx6c.webp" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create the emulator
&lt;/h3&gt;

&lt;p&gt;A window will open from the right side, as you can see in the image below. There is a plus &lt;a href="https://dev.to/bugfenderapp/jetpack-compose-button-tutorial-from-basics-to-advanced-features-356l"&gt;button&lt;/a&gt;, and when you click it, the hardware window will open.&lt;/p&gt;

&lt;p&gt;Select the device you want to use, and click the &lt;code&gt;Next&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukt9q2pd0k9hxve7oa4h.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukt9q2pd0k9hxve7oa4h.webp" alt="Android Emulator Setup" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbdhugm8nwx6fywt4rbts.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbdhugm8nwx6fywt4rbts.webp" alt="Android Emulator Setup" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Select system image
&lt;/h3&gt;

&lt;p&gt;Now, it’s time to choose the Android version you want. One of the awesome things about Android Emulator is that there are loads of pre-configured devices (like Pixel), and you can add your own if it’s not built in.&lt;/p&gt;

&lt;p&gt;If the specific Android version you’re looking for is not pre-installed, then click the button beside it. You need to download and select the version before proceeding to the next window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqk0yztr67jcjip7eexj7.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqk0yztr67jcjip7eexj7.webp" alt="Android Emulator Setup" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Verify configuration
&lt;/h3&gt;

&lt;p&gt;Here you need to provide a meaningful name for the virtual device. Remember, you can change the device orientation at this step, which is very handy.&lt;/p&gt;

&lt;p&gt;After selecting the device, click the &lt;code&gt;Finish&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhlnlxrdoge0uqnzc2gf0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhlnlxrdoge0uqnzc2gf0.webp" alt="Android Emulator Setup" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced configuration and tips
&lt;/h2&gt;

&lt;p&gt;Ok, so we’ve learned how to create a virtual Android device, which is a great start. Now, it’s time to customize it as per our requirements.&lt;/p&gt;

&lt;p&gt;You can customize several things, like hardware, software, device boot, and system image. Ready to take a look?&lt;/p&gt;

&lt;h3&gt;
  
  
  Hardware customization
&lt;/h3&gt;

&lt;p&gt;You can change the hardware via the &lt;code&gt;new hardware profile&lt;/code&gt; or &lt;code&gt;import hardware profile&lt;/code&gt; section. You can set device type, screen size, screen resolution, and memory size. There are lots of screen sizes available.&lt;/p&gt;

&lt;p&gt;You can also use the previously created custom hardware. In fact, you can customize whether the Android device will open in portrait mode or landscape mode. There are several resolution devices available.&lt;/p&gt;

&lt;p&gt;For the sensor section, there are various options you can adopt and adapt. Accelerometers, GPS… all the nuts and bolts that will define the look, feel and function of your app (quick FYI: we typically use the back camera to take a picture on a virtual device).&lt;/p&gt;

&lt;h3&gt;
  
  
  System image
&lt;/h3&gt;

&lt;p&gt;You need to keep three things in mind during the installation of the system image.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Google API system image is used when you need maps and Firebase tests on an emulator.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://dev.to/bugfenderapp/google-play-store-policy-changes-2024-4cib-temp-slug-1888313"&gt;Google Play&lt;/a&gt; system image helps us to install the apps from the Play Store.&lt;/li&gt;
&lt;li&gt;The x86 image is used for faster performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are two types of processors you can choose to install. Either Intel (HAXM) processors or AMD Hypervisor processors, which allow you to run a Windows hypervisor platform. Note that AMD processors are faster than HAXM.&lt;/p&gt;

&lt;h3&gt;
  
  
  RAM and storage customization
&lt;/h3&gt;

&lt;p&gt;By default, we get 2048 MB of RAM and storage out of the box during device creation. But to make things even better, we can customize the size as per our requirements. Remember that you’ll need at least 4 GB of RAM to run the apps on a virtual device smoothly.&lt;/p&gt;

&lt;h3&gt;
  
  
  And just quickly, some pro tips
&lt;/h3&gt;

&lt;p&gt;Through trial and (a lot of!) error, we’ve discovered these little nuggets while using Android Emulator at Bugfender.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Intel HAXM or AMD Hypervisor for better performance.&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;Quick Boot&lt;/strong&gt; to save the startup time of the device.&lt;/li&gt;
&lt;li&gt;Use the foldable device profile to test dual apps.&lt;/li&gt;
&lt;li&gt;Update the Android SDK, Emulator, and Android Studio regularly for better performance (this may seem obvious, but it’s easy to forget when you’re right in the middle of a testing sprint).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to work with different Android versions
&lt;/h2&gt;

&lt;p&gt;Now, it gets complicated. To make our test as thorough as possible, it’s vital to test our app in different Android versions (remember that some of your users will be using different software editions).&lt;/p&gt;

&lt;p&gt;To do this, we need to install different Android APIs from the SDK manager. The benefit of &lt;a href="https://bugfender.com/blog/top-10-tips-for-mobile-app-testing/" rel="noopener noreferrer"&gt;testing the app&lt;/a&gt; in various APIs is fewer bugs; we don’t need to create virtual devices separately to use various APIs.&lt;/p&gt;

&lt;p&gt;Here’s how to switch APIs on a single device:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigate to the Android Studio menu.&lt;/li&gt;
&lt;li&gt;Select SDK device manager from the tools menu, and choose which API you want to use to test the app.&lt;/li&gt;
&lt;li&gt;Click the ****button if the API is not found.&lt;/li&gt;
&lt;li&gt;After clicking next, choose a hardware profile. After that, select &lt;strong&gt;System Image&lt;/strong&gt; and click &lt;strong&gt;Finish&lt;/strong&gt; to complete the process of device creation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need to repeat the same process each time we need to change the API and test it. On the other hand, we can handle the API label in the &lt;code&gt;build.gradle&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to test with Android Emulators
&lt;/h2&gt;

&lt;p&gt;This is the bit you’ve been waiting for, right?&lt;/p&gt;

&lt;p&gt;After clicking the &lt;code&gt;Finish&lt;/code&gt; button, we need to revisit the device manager section again. Here, we can find a list with the emulator name along with the API level. It’s important to choose one of them, and click it.&lt;/p&gt;

&lt;p&gt;On the right-hand side, a &lt;strong&gt;&lt;code&gt;Start&lt;/code&gt; button&lt;/strong&gt; will appear. When we click on the &lt;code&gt;Play&lt;/code&gt; button, the emulator will start to launch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb84xw335uik30ubk8j0n.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb84xw335uik30ubk8j0n.webp" alt="Android Emulator Setup" width="800" height="416"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we have to wait a few minutes to boot the device. After booting, the device will get ready for operation. Sometimes it takes 5-10 minutes to get ready, so don’t fret if you think it’s dragging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And finally, it’s time to run our app in the emulator!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To do this, simply click the &lt;strong&gt;Run&lt;/strong&gt; button and select the virtual device. The app will appear on the screen after successfully installing on the device.&lt;/p&gt;

&lt;p&gt;And that’s it! Your Android Emulator is ready to go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some stuff to try
&lt;/h3&gt;

&lt;p&gt;With Android Emulator set up, there’s loads of stuff we can do right away. Exciting, right? Here’s some stuff you should definitely play around with.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Emulator supports various system images for advanced testing, as well as &lt;strong&gt;quick boot&lt;/strong&gt; and snapshots for faster startup. Even if your current app project doesn’t require these, it’s worth trying them out to see how they look and feel.&lt;/li&gt;
&lt;li&gt;Think of all the different edge cases your audience may throw up. Change the RAM size, SSD size and CPU core.&lt;/li&gt;
&lt;li&gt;Test the app on various network types, like 2G, 3G, 4G, and 5G. Remember, your users will be running your app in all different locations: homes, offices, planes, trains… you get the point.&lt;/li&gt;
&lt;li&gt;Look at the pre-configured devices that Emulator provides. Their Pixel and and Emulator provides pre-configured popular devices like Pixel.&lt;/li&gt;
&lt;li&gt;Take screenshots and create video recordings, and it’s great for a bug fix.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Different Emulators to use
&lt;/h2&gt;

&lt;p&gt;Remember when we mentioned at the top that the official Android Studio emulator has some limitations? Well, it’s time to unpack that in a bit more detail.&lt;/p&gt;

&lt;p&gt;There are some minor drawbacks to Android’s official emulator. GPU capacity is a bit limited (which is not great for graphics-intensive apps) and there can be issues around speed and storage. So, we’d recommend using the official emulator in tandem with other, more niche products, to ensure your tests are as good as possible.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud-based emulators can be great. We’d recommend &lt;strong&gt;Bluestacks or NoxPlayer.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Genymotion&lt;/strong&gt; is another Cloud-based emulator, and it’s also great for advanced-level testing.&lt;/li&gt;
&lt;li&gt;If you’ve built a gaming app, we’d recommend &lt;strong&gt;LDPLayer.&lt;/strong&gt; One of the key USPs is that you can download all kinds of Android games for free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase Test Lab&lt;/strong&gt; is great for &lt;a href="https://bugfender.com/blog/top-devices-testing-android-apps/" rel="noopener noreferrer"&gt;the breadth of devices&lt;/a&gt; and operating systems it provides. We can switch between Android Sdk versions with a simple bit of code, like so: &lt;code&gt;android { compileSdkVersion 33 defaultConfig { minSdkVersion 21 targetSdkVersion 33 } }&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn’t about choosing the best Android emulator; there are loads of blog posts out there for that. Instead, we’d urge you to use a blend of emulators for your specific needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Annnddd we’d done!
&lt;/h2&gt;

&lt;p&gt;Fun, right? You should now have a good working knowledge of how to set up an Android Emulator, how to use it and how to add different tools to your kit.&lt;/p&gt;

&lt;p&gt;Remember, once you’ve installed the Android Emulator, be sure to play around with it as much as you can. There’s loads you can do with it, so don’t be afraid to get creative.&lt;/p&gt;

&lt;p&gt;And if you have any questions about Android Emulator or any other aspect of &lt;a href="https://bugfender.com/blog/top-devices-testing-android-apps/" rel="noopener noreferrer"&gt;Android app testing&lt;/a&gt;, don’t be afraid to reach out to us. Happy Coding!&lt;/p&gt;

</description>
      <category>android</category>
      <category>debugging</category>
      <category>qa</category>
    </item>
    <item>
      <title>Jetpack Compose Fragments: How to Migrate and Integrate in Android Apps</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Thu, 10 Apr 2025 07:37:03 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/jetpack-compose-fragments-how-to-migrate-and-integrate-in-android-apps-22p0</link>
      <guid>https://forem.com/bugfenderapp/jetpack-compose-fragments-how-to-migrate-and-integrate-in-android-apps-22p0</guid>
      <description>&lt;p&gt;Imagine you’re a toolmaker. You’ve got an old-school toolshed with loads of ancient equipment: hammers, pickaxes, all the stuff people used hundreds of years ago. And then one day you walk into your toolshed to find laser cutters, 3D printers and loads of other digital tools at your disposal. Life would suddenly seem a lot simpler, right?&lt;/p&gt;

&lt;p&gt;Well, that’s kind of how it’s been for &lt;a href="https://dev.to/bugfenderapp/jetpack-compose-tutorial-for-android-developers-build-modern-uis-faster-18g1"&gt;developers moving from old-school XML coding to Jetpack Compose&lt;/a&gt;, the modern toolkit made by Google to &lt;a href="https://bugfender.com/guides/android-app-development-guide/" rel="noopener noreferrer"&gt;build UI for Android apps&lt;/a&gt;. There are loads of benefits to Jetpack Compose that we could mention, but here are the most common ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We don’t need to write code in an XML file separately; we simply need to write code in the Kotlin class.&lt;/li&gt;
&lt;li&gt;This means we can create a solid product that’s fully compatible and adoptable with existing code content, so we don’t need to take care of it via a separate route.&lt;/li&gt;
&lt;li&gt;What’s more, the data will automatically refresh when its state has been changed, which saves a lot of time.&lt;/li&gt;
&lt;li&gt;And Jetpack Compose provides a ready-to-use collection of layouts to get you started.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Best of all, Jetpack Compose allows us to denote UI behaviors with the &lt;code&gt;@Composable&lt;/code&gt; annotation, which means we can easily implement each new feature with minimal code content. For example, here’s a &lt;a href="https://dev.to/bugfenderapp/jetpack-compose-button-tutorial-from-basics-to-advanced-features-356l"&gt;button we’ve built using Jetpack&lt;/a&gt; Compose. Look at how nice and simple the code is in this window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
@Composable
fun ButtonExample(onClick: () -&amp;gt; Unit) {
    Button(onClick = { Log.d("Button : ", "button clicked.") }) {
        Text("Click Button")
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;But there’s one really important thing to know with Jetpack Compose. It doesn’t use fragments natively.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Composable&lt;/code&gt; (and &lt;code&gt;Navigation&lt;/code&gt;) are designed to replace &lt;code&gt;Fragments&lt;/code&gt;. For new apps, with no legacy code, this is fine. But if we’re migrating old projects, which contain fragments, this may seem like a hassle.&lt;/p&gt;

&lt;p&gt;So, today, we’re going to show you how to handle fragments in Jetpack Compose, and all the stuff that comes with them, with plenty of code samples.&lt;/p&gt;

&lt;p&gt;The article is going to be useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entry-level Android developers who want some simple explainer content around Jetpack UI.&lt;/li&gt;
&lt;li&gt;Advanced developers who have a specific fragment they want to port over to Jetpack Compose and don’t have a method for it.&lt;/li&gt;
&lt;li&gt;Developers who are just looking for advanced coding topics to sharpen their game and add new tools to their kit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready? Let’s dive in. (Remember: if you have questions about anything you read here, we’re happy to provide support).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Part 1: The basics of Jetpack Compose fragments&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do we create a Jetpack Compose fragment?
&lt;/h3&gt;

&lt;p&gt;Jetpack Compose is fully declarative. This means that we can simply call the function of the Jetpack Compose UI, and this will transform our data to a UI hierarchy automatically.&lt;/p&gt;

&lt;p&gt;However, this also means that if we are migrating fragments from an old project, we are obliged to declare &lt;code&gt;ComposeView&lt;/code&gt; inside the fragment itself.&lt;/p&gt;

&lt;p&gt;Now, how do we do this? Well, there’s a workaround here. You can embed Jetpack Compose inside a fragment using &lt;code&gt;ComposeView&lt;/code&gt;, as shown in the example below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
class ExampleFragmentWithoutXml : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            setContent {
                Text("Hello Its a basic text Compose!")
            }
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Now let’s take a closer look without Jetpack Compose
&lt;/h3&gt;

&lt;p&gt;In this case, we need to put the &lt;code&gt;ComposeView&lt;/code&gt; in our XML layout file. Here’s what the code looks like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;LinearLayout xmlns:android="&amp;lt;http://schemas.android.com/apk/res/android&amp;gt;"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"&amp;gt;

  &amp;lt;androidx.compose.ui.platform.ComposeView
      android:id="@+id/compose_view"
      android:layout_width="match_parent"
      android:layout_height="match_parent" /&amp;gt;
&amp;lt;/LinearLayout&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we’ve done this, we have to inflate the XML layout file in our Kotlin code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class ExampleFragmentUsingXml : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        val view = inflater.inflate(R.layout.fragment_example, container, false)
        val composeView = view.findViewById&amp;lt;ComposeView&amp;gt;(R.id.compose_view)
        composeView.apply {
            setContent {
               Text("Hello Anupam")
            }
        }
        return view
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we’re done! Nice and straightforward so far, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 2: A more advanced view
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Now, how do we create a composable?
&lt;/h3&gt;

&lt;p&gt;As mentioned, a Composable in Jetpack Compose is a function annotated with &lt;code&gt;@Composable&lt;/code&gt; that defines a piece of UI. When we mark a function with the &lt;code&gt;@Composable&lt;/code&gt; annotation, this means the function will be used to build UI elements.&lt;/p&gt;

&lt;p&gt;So &lt;a href="https://dev.to/bugfenderapp/kotlin-apply-and-other-kotlin-scope-functions-3k6l"&gt;let’s create a basic Composable function&lt;/a&gt; that displays text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Composable
fun MyComposeText(name: String) {
    Text(text = "Hi, $name!")
}

// To update UI

@Composable
fun showCmposeText() {  
    MyComposeText("I am Anupam") // call compose function
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;Text&lt;/code&gt; is the inbuilt compose function that will be used to display text values. We previously used this in our XML file with the &lt;code&gt;&amp;lt;TextView&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;To update the UI dynamically, we need to use the composable functions like &lt;code&gt;remember&lt;/code&gt; and &lt;code&gt;mutableStateOf&lt;/code&gt;, like the code below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
@Composable
fun ShowButtonClickCountInText() {
    var i by remember { mutableStateOf(0) } // State variable
    Button(onClick = { i++ }) { 
        Text("Count is : $i") 
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ok, so how do we deal with dependencies?
&lt;/h3&gt;

&lt;p&gt;A dependency tag is used to implement libraries, and it’s declared in the &lt;code&gt;build.gradle.kts&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;For any Android projects, dependencies are managed through &lt;code&gt;Gradle&lt;/code&gt;. As we know, compose is part of the Jetpack library, so we need to implement the required library in the Gradle file.&lt;/p&gt;

&lt;p&gt;Before adding the library, we need to ensure that the correct build file ( &lt;strong&gt;Module: App&lt;/strong&gt; ) is open. Within this file, we need to place the link under the &lt;code&gt;dependency&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;In addition, to enable compose functionality, we need to set &lt;code&gt;compose=true&lt;/code&gt; under the &lt;code&gt;buildFeatures&lt;/code&gt; tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;android {
    buildFeatures {
        compose = true // Enable Jetpack Compose
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you found any type of conflict, such as &lt;strong&gt;“version”&lt;/strong&gt; or “ &lt;strong&gt;can’t resolve&lt;/strong&gt; ” &lt;strong&gt;,&lt;/strong&gt; then use &lt;code&gt;strictly&lt;/code&gt; to enforce a specific version, like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    implementation("androidx.compose.ui:ui") {
        version { strictly("1.6.0") }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we want to update the compose dependency with the latest version, then we need to update the project-level &lt;code&gt;build.gradle.kts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plugins {
    id("com.github.ben-manes.versions") version "0.50.0"
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Now, let’s add a few dependencies
&lt;/h3&gt;

&lt;p&gt;We can easily add a few dependencies that are necessary for the Jetpack Compose project, including the core Compose UI.&lt;/p&gt;

&lt;p&gt;Here’s the code to show you what that destination looks like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    // Compose BOM (Manages all versions automatically)
    implementation(platform("androidx.compose:compose-bom:2024.02.02"))

    // Core Compose UI
    implementation("androidx.activity:activity-compose:1.9.0")
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.material3:material3")

    // Tooling for debugging
    debugImplementation("androidx.compose.ui:ui-tooling")

    // ViewModel integration
    implementation("androidx.lifecycle:lifecycle-viewmodel-compose")

    // Navigation (If using Compose Navigation)
    implementation("androidx.navigation:navigation-compose")
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Part 3: Trouble-shooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Firstly, how do we deal with reflection?
&lt;/h3&gt;

&lt;p&gt;When we talk about reflection, we’re talking about code that can inspect other code.&lt;/p&gt;

&lt;p&gt;Reflection is commonly used in tasks such as serialization (where we convert the object state into a storable format), but this isn’t usually a good thing, because it can increase memory usage, slow your execution and require the usage of Dagger, a dependency injection framework that can greatly increase build time.&lt;/p&gt;

&lt;p&gt;For example, when analyzing the &lt;code&gt;Employee&lt;/code&gt; class, we may be tempted to use the &lt;code&gt;Gson&lt;/code&gt; library. This will involve reflection, and thus impact performance.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val gson = Gson()
val user = gson.fromJson(jsonString, Employee::class.java)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, as a Kotlin-based framework, the Jetpack Compose library usually avoids these problems. In the case of serialization, &lt;code&gt;Kotlinx&lt;/code&gt; serialization is compiler-based, so there is no need to use reflection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Serializable
data class Employee(val name: String, val empcode: Int)

val json = Json.encodeToString(Employee("Anupam", 1))
val user = Json.decodeFromString&amp;lt;Employee&amp;gt;(json)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, but we know what you’re asking here: what if we &lt;em&gt;absolutely need&lt;/em&gt; to use reflection? How do we use it without sacrificing performance?&lt;/p&gt;

&lt;p&gt;In this case, we need to update the rules for two code-shrinking tools, &lt;strong&gt;&lt;code&gt;ProGuard&lt;/code&gt;&lt;/strong&gt; and &lt;code&gt;R8&lt;/code&gt;. Both of these tools will identify and remove unused code, keeping your build nice and lean. Here’s how we put these into practice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
// com.example.MyClass replce with your own class with package name

-keep class com.example.MyClass { *; }    
-keep class kotlinx.serialization.** { *; }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Right. Now, how do we deal with Jetpack Compose memory leaks?
&lt;/h3&gt;

&lt;p&gt;Memory leaks are a common&lt;a href="https://bugfender.com/platforms/android/" rel="noopener noreferrer"&gt;issue that can affect any kind of Android&lt;/a&gt; application. Jetpack Compose has loads of advantages (as you’ve hopefully seen by now!) but it can still suffer memory leaks due to poor lifecycle management practices, such as the retention of objects, forgetting to get rid of observers and not dealing properly with lambda expressions, which are crucial to Jetpack Compose.&lt;/p&gt;

&lt;p&gt;Here’s an example of what happens if we use &lt;code&gt;context&lt;/code&gt; in a &lt;code&gt;ViewModel&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyViewModel(context: Context) : ViewModel() { // Can leak Activity
    private val myContext = context
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, we can significantly reduce memory leaks in applications by deploying &lt;code&gt;applicationContext&lt;/code&gt; instead of &lt;code&gt;context&lt;/code&gt; or &lt;code&gt;activity .&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class MyViewModel(application: Application) : AndroidViewModel(application) {
    private val context = getApplication&amp;lt;Application&amp;gt;().applicationContext //Safe to use
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also use the &lt;code&gt;leakcanary&lt;/code&gt; library to detect &lt;a href="https://bugfender.com/blog/prevent-android-memory-leaks/" rel="noopener noreferrer"&gt;memory leaks in Android&lt;/a&gt; applications. We need to add the dependency in the Gradle file the following way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    debugImplementation("com.squareup.leakcanary:leakcanary-android:2.12")
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ok. Any other lifecycle management tips for me?
&lt;/h3&gt;

&lt;p&gt;Why sure!&lt;/p&gt;

&lt;p&gt;Jetpack Compose does not use &lt;code&gt;activity&lt;/code&gt; or &lt;code&gt;fragment&lt;/code&gt; lifecycle methods like &lt;code&gt;onCreate()&lt;/code&gt;, &lt;code&gt;onStart()&lt;/code&gt;, and &lt;code&gt;onDestroy()&lt;/code&gt;. Instead, when Jetpack Compose runs your composable function the first time, it is called initial composition.&lt;/p&gt;

&lt;p&gt;During this time, Jetpack Compose keeps track of all the composables that will be used to build the UI. Then, when the state of our app has been changed (as with a text update or a color change), it will automatically update the UI without calling it again. This is called recomposition.&lt;/p&gt;

&lt;p&gt;&lt;a href="" class="article-body-image-wrapper"&gt;&lt;img alt="lifecycle-composition.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Composition can be called single-time in code, but recomposition is used multiple times in code. The instance of composable is distinct by each composition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Composable
fun MyComposable() {
    Column {
        Text("Anupam")
        Text("Jetpack")
        Text("Compose")
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, we use three pieces of text within a column. Here, when the composable enters the composition, three instances will create. Each has its own lifecycle in the composition, as you can see here.&lt;/p&gt;

&lt;h3&gt;
  
  
  And finally…how do we handle errors in Jetpack Compose?
&lt;/h3&gt;

&lt;p&gt;We can handle errors in Jetpack compose in several ways. It depends on which type of error we are dealing with, like UI, network, or exception.&lt;/p&gt;

&lt;p&gt;Here’s an example of an UI error handle. As you can see, we can use &lt;a href="https://dev.to/bugfenderapp/mastering-exception-handling-in-kotlin-a-comprehensive-guide-88l"&gt;try catch block to handle the UI error&lt;/a&gt;. This is really useful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Composable
fun ShowUserName(userName: String) {
    try {
        Text("Welcome, $userName")
    } catch (e: Exception) {
        Text("Failed to load user details")
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We may write a further blog in due course, highlighting how to &lt;a href="https://bugfender.com/platforms/android/" rel="noopener noreferrer"&gt;handle errors&lt;/a&gt; in Jetpack Compose.&lt;/p&gt;

&lt;h2&gt;
  
  
  To recap
&lt;/h2&gt;

&lt;p&gt;Jetpack Compose is the future of Android UI that replaces the XML layout with Kotlin code. If we have older code, then we can easily migrate it in Jetpack Compose. Jetpack provides an in-built function; we need to call in our code using a parameter, and the view will automatically build up.&lt;/p&gt;

&lt;p&gt;We don’t need fragments with Compose. We can navigate to another screen without needing a fragment or an activity. Using compose navigation, we can navigate from one screen to another screen. But if we are going to use Compose, there are lots of workarounds we can use.&lt;/p&gt;

&lt;p&gt;If you have any questions about Android Jetpack Compose Fragment, don’t be afraid to reach out to us. Happy Coding!&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Kotlin Apply and other Kotlin Scope Functions</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Thu, 03 Apr 2025 07:59:21 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/kotlin-apply-and-other-kotlin-scope-functions-3k6l</link>
      <guid>https://forem.com/bugfenderapp/kotlin-apply-and-other-kotlin-scope-functions-3k6l</guid>
      <description>&lt;p&gt;Last week, we got a question from one of our users asking us how to use Kotlin Apply. Specifically, the reader wanted to know whether it was best to use the apply function in their Android application, or another of the many Kotlin scope functions.&lt;/p&gt;

&lt;p&gt;So we got to thinking: &lt;em&gt;Why not write an article about the whole topic of Kotlin scope functions?&lt;/em&gt; After all, they’re awesome: they let us write readable, concise code in Kotlin, and work with an object without the need for repeated references.&lt;/p&gt;

&lt;p&gt;This not only cuts down boilerplate code, it provides a method to enhance the readability of our Kotlin apps and allow us to handle complex logic with minimal app maintenance. And if we want to build our own Kotlin DSL, then scope functions are absolutely crucial.&lt;/p&gt;

&lt;p&gt;So today we’re going to cover all the main Kotlin scope functions (and yes, there’ll be a big section on the apply function) with practical examples and use cases. The blog is ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Existing Kotlin developers&lt;/strong&gt; who already know or have seen scope functions in their programming lives, but struggle to fully grasp them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android developers&lt;/strong&gt; who work with Kotlin and want to write more idiomatic, less verbose code in their next Android app, and explore object oriented programming.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java developers learning Kotlin and Kotlin multiplatform&lt;/strong&gt; who want to drive their learning towards better practices in a new programming language.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One key thing you’ll learn is how to leverage scope functions in Android development, and why it is totally worth the effort for every Kotlin developer, whether they’re seasoned or new to the language.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, an introduction to scope functions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What are scope functions?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you’re new, scope functions are provided by a Kotlin standard library to simplify working with an object in a scope, and make our code much more readable and easy to maintain.&lt;/p&gt;

&lt;p&gt;They allow us to write code without writing the object reference every time, which can be a pain, and assist in realigning our code so that we can easily perform operations on an object, without polluting the base.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Types of scope functions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;There are a total of five scope functions in Kotlin, each with their own distinct use cases and advantages. The higher-order functions we will cover today are &lt;code&gt;let&lt;/code&gt;, &lt;code&gt;run&lt;/code&gt;, &lt;code&gt;with&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt; and &lt;code&gt;also&lt;/code&gt;. Here is a really quick deep-dive on each one:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;let&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;Let is used to run blocks of code on object, and then return the result or follow up with other operations in a chain (see example below), as well dealing with null safely. Here’s a code snippet to show you what we mean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val user = User("John", "Doe")
user?.let {
    Log.d("User", "First name: ${it.firstName}, Last name: ${it.lastName}")
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;run&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;Run is great because it combines the work of let and functions in tandem, facilitating both side effects while returning a value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val fullName = user.run {
    "$firstName $lastName"
}
Log.d("User", "Full name: $fullName")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;with&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;This function lets us perform an operation on a object without actually extending it. Like run, this is a non extension function (i.e. it doesn’t extend another class or type), and it requires the object as its parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;with(user) {
    Log.d("User", "First name: $firstName")
    Log.d("User", "Last name: $lastName")
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;apply&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;Now, we’re back to what we discussed at the top. Apply is a constructor, which is used to set up an object and return the object itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val user = User().apply {
    firstName = "John"
    lastName = "Doe"
}
Log.d("User", "User created: $user")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;also&lt;/code&gt;&lt;/strong&gt; :&lt;/p&gt;

&lt;p&gt;This is the same as the apply function, but for side-effects, like logging or validation. It returns the object itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val user = User("John", "Doe").also {
    Log.d("User", "User created: $it")
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Comparing scope functions
&lt;/h2&gt;

&lt;p&gt;The specific scope function to use depends on what you want to accomplish in your code. Here are some tips to choose the right one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;let:&lt;/code&gt;&lt;/strong&gt; We use &lt;code&gt;let&lt;/code&gt; when we perform a certain operation with a non-null object or transform the result. This is great for chaining calls or working with nullable objects to prevent &lt;a href="https://dev.to/bugfenderapp/mastering-exception-handling-in-kotlin-a-comprehensive-guide-88l"&gt;null pointer exceptions&lt;/a&gt;. &lt;code&gt;user?. let { }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;run:&lt;/code&gt;&lt;/strong&gt; This is great when we want to execute a block of code and get a receiver result from the last expression. Very useful for value object configuration and computation of your own.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;val result = configuration.run { //do computations and return result. }&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;with:&lt;/code&gt;&lt;/strong&gt; With is particularly useful for chaining multiple function calls over an object without repeating a reference to the same thing. Useful if you are doing a number of things to an existing object.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;with(configuration) { /* lots of operations */ }&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;apply:&lt;/code&gt;Apply comes into its own when you need to initialize or configure the object and want the function to return itself. Great for constructor execution and object initialization.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;val paint = Paint().apply { properties }&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;also:&lt;/code&gt;&lt;/strong&gt; We can use &lt;code&gt;also&lt;/code&gt; to peform more operations on an object, like l&lt;a href="https://bugfender.com/platforms/android/" rel="noopener noreferrer"&gt;ogging or debugging&lt;/a&gt;. Appropriate for operations returning the object itself, after performing the side effect. &lt;code&gt;val file = File(path).also { Log.d("File", "File path: ${it.path}") }&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Differences and similarities
&lt;/h2&gt;

&lt;p&gt;Now let’s go deeper.&lt;/p&gt;

&lt;p&gt;Each scope function carries its own explicit behavior, so let’s look at the specific use of each of them: in other words, where and when we can use these different scope functions in the &lt;a href="https://bugfender.com/guides/android-app-development-guide/" rel="noopener noreferrer"&gt;Android app development&lt;/a&gt; pipeline. We will also explore the similarities (and differences) of the various functions along the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context object:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;let&lt;/code&gt; &amp;amp; &lt;code&gt;also&lt;/code&gt; require the use of &lt;code&gt;it&lt;/code&gt; as the context object in a lambda function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run&lt;/code&gt;, &lt;code&gt;with&lt;/code&gt; and &lt;code&gt;apply&lt;/code&gt; require the use of &lt;code&gt;this&lt;/code&gt; as the context object in a lambda expression.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Return value:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;let&lt;/code&gt; &amp;amp; &lt;code&gt;run&lt;/code&gt; return the result of the lambda function.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;with&lt;/code&gt; returns the lambda result.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;apply&lt;/code&gt; and &lt;code&gt;also&lt;/code&gt; return the object itself.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Usage pattern:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;let&lt;/code&gt; is used for null-checks and transformations.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run&lt;/code&gt; – is the computation and result return code.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;with&lt;/code&gt; – is an operand that declares an already initialized object so you can perform a task with it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;apply&lt;/code&gt; is used to instantiate and configure objects.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;also&lt;/code&gt; is used for side effects, like logging and additional operations that do not violate the main object.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ok, that’s the basic primer done. Now let’s really dig into those weeds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced usage of scope functions
&lt;/h2&gt;

&lt;p&gt;Now we’re really cooking! We’ve gone through all the definitions and distinctions, so let’s start looking at advanced use cases and seeing how the different scope functions can be used in tandem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nested scope functions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nesting scope functions are great when performing complex object hierarchies. And with careful nesting of scope functions, you can maintain a clean, readable structure where operations related to each object are all inside the relevant block.&lt;/p&gt;

&lt;p&gt;To show you what we mean, let’s look at a code snippet involving &lt;code&gt;apply&lt;/code&gt; and &lt;code&gt;let&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Example of usng appy and let scope functions together
val user = User().apply {
    firstName = "Sam"
    lastName = "Doe"
}.let { user -&amp;gt;
    // We can perform additional operations here
    Log.d("User", "User is created: ${user.firstName} ${user.lastName}")
    user
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Chaining Scope functions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now this bit is crucial. Let’s look at chaining functions &lt;em&gt;together&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The most common way to chain scope functions is popping out of the last block, calling a new function and repeating that until we get what were expecting. It’s nice and simple, and is a really good way to avoid that dreaded boilerplate code.&lt;/p&gt;

&lt;p&gt;As an example, here’s a code snippet showing how to chain &lt;code&gt;apply&lt;/code&gt;, &lt;code&gt;also&lt;/code&gt; and &lt;code&gt;let&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Chaining example of the apply, also and let functions. 
val userDisplayName = User().apply {
    firstName = "Sam"
    lastName = "Doe"
}.also {
    Log.d("User", "User is created: ${it.firstName} ${it.lastName}")
}.let {
    "${it.firstName} ${it.lastName}"
}
Log.d("User", "Display Name: $userDisplayName")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s say we want to set some properties for a &lt;strong&gt;UserProfile&lt;/strong&gt; object, log its properties and then finally display a summary. How would we do this? Well, let’s get into the language.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Example of UserProfile class that has user properties and showing the use of different scope functions. 
data class UserProfile(
    var firstName: String = "",
    var lastName: String = "",
    var age: Int = 0,
    var email: String = ""
)

fun main() {
    val userProfileSummary = UserProfile().apply {
        firstName = "Sam"
        lastName = "Doe"
        age = 30
        email = "Sam.doe@test.com"
    }.also { profile -&amp;gt;
        Log.d("UserProfile", "Profile is created: $profile")
    }.run {
        "Name: $firstName $lastName, Age: $age, Email: $email"
    }

    Log.d("UserProfile", "The Profile Summary: $userProfileSummary")
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-world use cases
&lt;/h2&gt;

&lt;p&gt;The bit you’ve been waiting for, right? A goold old hand-ons tutorial.&lt;/p&gt;

&lt;p&gt;In a real-world application of Android example, the code above will feature scope functions.&lt;/p&gt;

&lt;p&gt;To build the use case, let’s start by creating a simple example where we have to create a user profile screen. These tasks often involve &lt;a href="https://dev.to/bugfenderapp/using-graphql-api-in-android-11ko"&gt;fetching user data&lt;/a&gt;, initializing views or displaying fetched data on screen.&lt;/p&gt;

&lt;p&gt;To initialize views in a user profile screen and set them, use &lt;code&gt;apply&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UserProfileActivity : AppCompatActivity() {

    private lateinit var binding: ActivityUserProfileBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityUserProfileBinding.inflate(layoutInflater)
        setContentView(binding.root)
        // Initialize views using apply
        binding.apply {
            userNameTextView.text = ""
            userEmailTextView.text = ""
            userProfileImageView.setImageResource(R.drawable.placeholder_profile)
        }

        // Fetch and display user data
        fetchUserDataAndDisplay()
    }

    private fun fetchUserDataAndDisplay() {
        val userRepository = UserRepository()

        // Fetch user data using run
        val user = userRepository.run {
            getUserData()
        }

        // Display user data using let
        user?.let {
            binding.userNameTextView.text = "${it.firstName} ${it.lastName}"
            binding.userEmailTextView.text = it.email
            // Assuming you have an image loading library like Glide or Picasso
            Glide.with(this).load(it.profileImageUrl).into(binding.userProfileImageView)
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There we go! Not bad right? The code all &lt;a href="https://dev.to/bugfenderapp/kotlin-flow-tutorial-build-reactive-and-scalable-applications-p1a"&gt;flows&lt;/a&gt; smoothly (if we say so ourselves!) and it’s all nice and clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best practices
&lt;/h2&gt;

&lt;p&gt;Ok, we’re nearly at the finish line now. Let’s cap things off by exploring some best practices that we can use when working with scope functions in our Kotlin projects.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pick your scope function:&lt;/strong&gt; Choose the appropriate scope function for your case. For example, &lt;code&gt;apply&lt;/code&gt; is great for object initialisation, &lt;code&gt;let&lt;/code&gt; is good for null checks and to transform a value, and &lt;code&gt;run&lt;/code&gt; is best on computations that return something.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the code short:&lt;/strong&gt; Never write large blocks of code into scope functions. The shorter your code, the more readable it will be, and the easier it will be to determine what the scope function does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retain scope functions for a proper level of nesting:&lt;/strong&gt; As nesting can be deeper down to the last level, you should not bury scope functions deep inside one another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clarify context object naming:&lt;/strong&gt; Use &lt;code&gt;let&lt;/code&gt; or &lt;code&gt;also&lt;/code&gt; where appropriate, When using the &lt;code&gt;let&lt;/code&gt; keyword in a regular lambda, provide more meaningful names to the context object if necessary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use scope functions for null-safety:&lt;/strong&gt; &lt;code&gt;let&lt;/code&gt; is used to execute blocks of code on nullable objects and avoid null pointer exceptions. In the context of Android app development, we often have to work with value references, so this particular facet is really useful.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use a scope functions chain:&lt;/strong&gt; You can chain scope functions to make the code look more clean and readable. Each step or function in the chain should do one, and only one thing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  To sum up
&lt;/h2&gt;

&lt;p&gt;Wow, we’ve covered a lot there right? We’ve discussed the key features of scope functions, we’ve introduced the basics behind scope functions and how they can make your Kotlin code more clean and concise, before detailing each scope function (&lt;code&gt;let&lt;/code&gt;, &lt;code&gt;run&lt;/code&gt;, &lt;code&gt;with&lt;/code&gt;, &lt;code&gt;apply&lt;/code&gt;, &lt;code&gt;also&lt;/code&gt;), its purposes and syntax and its use in real-life applications. We have also discussed the advanced usage of scope functions via nesting or chaining, to handle complex conditions more effectively.&lt;/p&gt;

&lt;p&gt;Remember: scope functions are ultimately a way of writing clean, concise code in Kotlin. The better we utilize scope functions, the more bespoke (and less boilerplated) our code will look. Happy coding guys!&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Swift Concurrency Explained: GCD, Operation Queues, and Async/Await</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Thu, 27 Mar 2025 08:33:16 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/swift-concurrency-explained-gcd-operation-queues-and-asyncawait-588j</link>
      <guid>https://forem.com/bugfenderapp/swift-concurrency-explained-gcd-operation-queues-and-asyncawait-588j</guid>
      <description>&lt;p&gt;Concurrency is the &lt;a href="https://bugfender.com/guides/ios-app-development/" rel="noopener noreferrer"&gt;ability of an app&lt;/a&gt; to perform multiple tasks at once, and it’s a crucial concept for apps that need to perform multiple tasks at once in an efficient, usable way. Thankfully Swift has made great strides with concurrency, and now provides simple tools for &lt;a href="https://bugfender.com/platforms/ios/" rel="noopener noreferrer"&gt;writing robust apps&lt;/a&gt; that are responsive and enjoyable to use.&lt;/p&gt;

&lt;p&gt;In this article we’ll explore two main ways of using threads for concurrency models. We’ll start with the classic way of doing it from the straight-up use of threads, &lt;code&gt;Grand Central Dispatch&lt;/code&gt; and &lt;code&gt;Operation Queues&lt;/code&gt;. Afterwards we’ll look at &lt;code&gt;async/await&lt;/code&gt;, a more modern, simple, approach to concurrency that has recently been added to the arsenal of &lt;a href="https://dev.to/bugfenderapp/swift-code-analysis-integrating-sonarqube-4cf1-temp-slug-2465979"&gt;Swift tools&lt;/a&gt; at our disposal.&lt;/p&gt;

&lt;p&gt;But before that, a quick look at the key concepts needed to fully grasp concurrency and threading.&lt;/p&gt;

&lt;h1&gt;
  
  
  What Is Concurrency?
&lt;/h1&gt;

&lt;p&gt;Concurrency means apps can run multiple tasks or processes, regardless of each other’s life cycle. This improves the overall performance and responsiveness of an app, since it allows multiple operations to run at &lt;a href="https://bugfender.com/blog/swift-dates/" rel="noopener noreferrer"&gt;the same&lt;/a&gt; time, whether they are in parallel or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are threads?
&lt;/h2&gt;

&lt;p&gt;We’re going to talk about threads in more detail soon, so here’s a quicker explanation of what they actually are:&lt;/p&gt;

&lt;p&gt;A thread is the context in which operations are executed. We can think of each thread as a small, independent worker who has a single task to execute, and will execute it independently.&lt;/p&gt;

&lt;p&gt;We can manually create threads, and use them, with some simple code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let thread = Thread {
    print("Here we can perform our task")
}

thread.start()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Even though we’ve given the code string, this course of action is not usually recommended. It should only be used for very specific, low-level tasks. Using the &lt;a href="https://dev.to/bugfenderapp/exploring-swift-extensions-strategies-for-efficient-ios-code-37gl-temp-slug-9200008"&gt;abstractions&lt;/a&gt;, like &lt;code&gt;Grand Central Dispatch,&lt;/code&gt; which we will explain shortly, is safer, simpler, and generally more advisable.&lt;/p&gt;

&lt;h1&gt;
  
  
  Classic Concurrency in Swift
&lt;/h1&gt;

&lt;p&gt;We will now look at the older concurrency models in &lt;code&gt;Swift&lt;/code&gt;, including &lt;code&gt;Grand Central Dispatch (GCD)&lt;/code&gt; and &lt;code&gt;Operation Queues&lt;/code&gt;. We could look directly at threads but, as we’ve just explained, they’re not the most recommended method of implementing concurrency, so we’ll put that to one side.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Grand Central Dispatch (GCD)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Grand Central Dispatch (GCD)&lt;/code&gt; is one of the oldest and most commonly used concurrency tools in iOS and macOS development. GCD is a low-level API that enables developers to manage tasks asynchronously, using dispatch queues. These queues can run tasks either serially (one task at a time) or concurrently (multiple tasks at once).&lt;/p&gt;

&lt;h3&gt;
  
  
  Queues
&lt;/h3&gt;

&lt;p&gt;One of the key concepts is that of &lt;code&gt;Queues&lt;/code&gt;. These are essentially &lt;a href="https://dev.to/bugfenderapp/complete-guide-to-swiftui-lists-for-developers-2b2k"&gt;lists&lt;/a&gt; of tasks that need to be executed in a specific order. There are many types of queues, so let’s take a look:&lt;/p&gt;

&lt;h3&gt;
  
  
  Serial queues and concurrent queues
&lt;/h3&gt;

&lt;p&gt;Serial queues guarantee &lt;code&gt;Tasks&lt;/code&gt; are executed in a serial manner. In other words, one after the other. Meanwhile, concurrent queues can execute and run many tasks simultaneously, as long as there are CPU cores to do so.&lt;/p&gt;

&lt;p&gt;They are very similar in usage, as shown here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Serial:
let serialQueue = DispatchQueue(label: "mySerialQueue")

serialQueue.async {
    print("1 started")
    print("1 finished")
}
serialQueue.async {
    print("2 started")
    print("2 finished")
}

//Concurrent
let concurrentQueue = DispatchQueue(label: "myConcorrentQueue", attributes: .concurrent)

concurrentQueue.async {
    print("3 started")
    print("3 finished")
}
concurrentQueue.async {
    print("4 started")
    print("4 finished")
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the serial Queue will print:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    print("1 started")
    print("1 finished")
    print("2 started")
    print("2 finished")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s likely that the concurrent Queue would have a different order of operations, so it could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; print("1 started")
 print("2 started")
 print("1 finished")
 print("2 finished")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; print("1 started")
 print("2 started")
 print("2 finished")
 print("1 finished")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we’re talking about prints, the concurrent Queue is likely to have the same output as the serial one. But with more complex operations it is certain to differ.&lt;/p&gt;

&lt;h3&gt;
  
  
  Global Queues
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Global Queues&lt;/code&gt; automatically let us choose a &lt;code&gt;Quality of Service&lt;/code&gt; level, which refers to the priority in which they get executed. There are several &lt;code&gt;QoS&lt;/code&gt;, or priority levels to note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.userInteractive&lt;/code&gt;: The highest priority for a Global Queue. This is to be used in tasks that require immediate results, usually triggered by user actions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.userInitiated&lt;/code&gt;: This has lower priority than &lt;code&gt;userInteractive&lt;/code&gt; but it should also be completed quickly to provide a good user experience.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.default&lt;/code&gt;: The default priority for GlobalQueues. That means it will run after any high-priority &lt;a href="https://dev.to/bugfenderapp/exploring-swift-collections-in-depth-guide-to-arrays-sets-and-dictionaries-503b"&gt;queues&lt;/a&gt;, and before any low-priority ones. It is, in practice, the average priority of all of them.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.utility&lt;/code&gt;: This is usually used for tasks that are not being actively tracked by the user, like downloading a higher resolution asset to use in apps.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.background&lt;/code&gt;: The lowest priority. Tasks with this priority can run in the background without interfering with anything, even if they take a while to complete.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They are all similar when it comes to usage, as we can see in this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DispatchQueue.global(qos: .userInteractive).async {
    print("My userInteractive task")
}

DispatchQueue.global(qos: .userInitiated).async {
    print("My userInitiated task")
}

DispatchQueue.global(qos: .default).async {
    print("My default task")
}

DispatchQueue.global(qos: .utility).async {
    print("My utility task")
}

DispatchQueue.global(qos: .background).async {
    print("My background task")
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The main Queue
&lt;/h3&gt;

&lt;p&gt;The main &lt;code&gt;Queue&lt;/code&gt; is a special case, since it’s the only one we rely on to update our UI. This means that if we use it for any task that takes a huge amount of time to complete, the UI of our apps will be frozen while the &lt;code&gt;Task&lt;/code&gt; completes.&lt;/p&gt;

&lt;p&gt;Thus, it is common practice to run more time-consuming tasks on other Queues, and then to get the results back to the main Queue to update our App’s UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; DispatchQueue.global(qos: .background).async {
    print("My background task that might take a while to complete")

    DispatchQueue.main.async {
        //Update the UI here after completing what was processing on the background global queue
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Important: You should &lt;strong&gt;not&lt;/strong&gt; use the main queue for expensive/slow computing, since that will freeze your &lt;code&gt;UI&lt;/code&gt; and impact your users’ experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Operation Queues
&lt;/h2&gt;

&lt;p&gt;While &lt;code&gt;Operation Queues&lt;/code&gt; are also built on top of &lt;code&gt;GCD&lt;/code&gt;, they are a higher-level abstraction over it. This means they’re simpler to learn and use, and provide us with enough tools to control task execution.&lt;/p&gt;

&lt;p&gt;They allow us to add operations, which are literally instances of the &lt;code&gt;Operation&lt;/code&gt; class, to a queue, manage dependencies between &lt;code&gt;Operations&lt;/code&gt;, and configure the maximum number of concurrent &lt;code&gt;Operations&lt;/code&gt; we’d like to have.&lt;/p&gt;

&lt;p&gt;To get a better idea, let’s look at a simple example of &lt;code&gt;Operation Queues&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
let operationQueue = OperationQueue()

let operation1 = BlockOperation {
    // Perform a Task
    print("Task 1 started")
}

let operation2 = BlockOperation {
    // Perform another Task
    print("Task 2 started")
}

operationQueue.addOperations([operation1, operation2], waitUntilFinished: false)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, our &lt;code&gt;Operation Queue&lt;/code&gt; will manage two &lt;code&gt;BlockOperation&lt;/code&gt; objects running concurrently. We can also set them to run sequentially if we configure the &lt;code&gt;Operation Queue&lt;/code&gt; that way. There are many important things we can do on our &lt;code&gt;OperationQueues&lt;/code&gt;, including:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let operationQueue = OperationQueue()

//Define how many concurrent operations we'd like, 
//if we set it to 1, in practice it becomes a sequential queue
operationQueue.maxConcurrentOperationCount = 1;

//Define the QoS of our Queue, as we saw earlier for Global Queues
operationQueue.qualityOfService = .background

//Define a name to help us identify a Queue
operationQueue.name = "myQueue"

//Set it to wait until all operations are finished, which is useful in some scenarios
operationQueue.waitUntilAllOperationsAreFinished()

//Cancel all operations if something made the queue finish irrelevant to us
operationQueue.cancelAllOperations()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Modern Concurrency: Async/Await in Swift
&lt;/h1&gt;

&lt;p&gt;Now that we’ve seen the basics of Classic concurrency in Swift, let’s have a look at the new kid on the block: &lt;code&gt;Async/Await&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This was introduced with Swift 5.5. and drastically improves how we write and handle concurrency. Everything becomes way simpler and easier to use, without losing any capability.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Basics of Async/Await
&lt;/h2&gt;

&lt;p&gt;We use async to mark any function/method as asynchronous. We then use await, on a caller, to instruct that it needs to wait for the async function/method to finish.&lt;/p&gt;

&lt;p&gt;It really is that simple. We don’t need blocks or callbacks, like we saw earlier.&lt;/p&gt;

&lt;p&gt;Let’s look at how it all works in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
func makeWebRequest() async -&amp;gt; String {
    // Simulate a network request
    try await Task.sleep(nanoseconds: 2 * 1_000_000_000)
    return "Data is here"
}

Task {
    let data = await makeWebRequest()
    print(data)
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;makeWebRequest&lt;/code&gt;  is marked with &lt;code&gt;async&lt;/code&gt;, indicating that it performs an asynchronous operation.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;await&lt;/code&gt; keyword is used to call &lt;code&gt;makeWebRequest()&lt;/code&gt; and wait for its result.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Async Let and Task Groups
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Swift 5.5&lt;/code&gt; introduced  &lt;strong&gt;structured concurrency&lt;/strong&gt; , a new way of managing asynchronous tasks that makes the lifecycle of tasks more predictable and easier to manage. Structured concurrency ensures that asynchronous tasks are completed or canceled in a controlled manner.&lt;/p&gt;

&lt;p&gt;Swift now allows asynchronous tasks to be created using &lt;code&gt;async let&lt;/code&gt; and the managing of multiple tasks with &lt;code&gt;TaskGroup&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
func fetchDataFromMultipleSources() async {
    async let data1 = fetchDataFromSource1()
    async let data2 = fetchDataFromSource2()

    let result1 = await data1
    let result2 = await data2

    print("Data1: \\(result1), Data2: \\(result2)")
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;async let&lt;/code&gt; is used to create two asynchronous tasks that run concurrently.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Tasks&lt;/code&gt; are awaited individually, and their results are processed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;Task groups&lt;/code&gt; allow you to create multiple asynchronous tasks, aggregate their results, and handle their cancellation together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
func fetchMultipleData() async {
    await withTaskGroup(of: String.self) { group in
        group.async {
            return await fetchDataFromSource1()
        }

        group.async {
            return await fetchDataFromSource2()
        }

        for await result in group {
            print(result)
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;withTaskGroup&lt;/code&gt; manages multiple concurrent tasks, and the &lt;code&gt;for await&lt;/code&gt; loop collects their results in a safe, structured manner.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summing up
&lt;/h2&gt;

&lt;p&gt;If you’re going to take three things away from this post, we urge you to remember the following.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Avoid blocking the main thread, regardless of what concurrency model we’re implementing.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Use &lt;code&gt;async/await&lt;/code&gt; when possible, because it is relatively simple and easy to use.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Grand Central Dispatch&lt;/code&gt;, when understood, is not hard to use and we can leverage it for our background tasks*&lt;/p&gt;

&lt;p&gt;If you remember these things, you’ll have a blast with Swift Concurrency. Happy coding!&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
    </item>
    <item>
      <title>SwiftUI Navigation Explained: Best Practices for Seamless App Flow</title>
      <dc:creator>Bugfender</dc:creator>
      <pubDate>Wed, 19 Mar 2025 07:39:01 +0000</pubDate>
      <link>https://forem.com/bugfenderapp/swiftui-navigation-explained-best-practices-for-seamless-app-flow-23kp</link>
      <guid>https://forem.com/bugfenderapp/swiftui-navigation-explained-best-practices-for-seamless-app-flow-23kp</guid>
      <description>&lt;p&gt;Navigation is one of the most basic functionalities of any app, and among the most crucial aspects of our &lt;a href="https://bugfender.com/guides/ios-app-development/" rel="noopener noreferrer"&gt;work as developers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;From replacing a login screen with our actual logged-in state app, to showing a modal with details of any item inside our app, all of these are navigational challenges we need to tackle in our day-to-day.&lt;/p&gt;

&lt;p&gt;SwiftUI has introduced a modern approach to navigation in Apple-based platforms. It replaces the older UINavigationController navigational system, which had issues around reliability and consistency, with different, more modern approaches. These new approaches can take a bit of getting used to, so we’re going to unpack them today.&lt;/p&gt;

&lt;p&gt;This article will be very hands-on and give you examples on how to navigate in scenarios that you will find in your own apps. All the insights are drawn from our own work as devs, building navigation into all kinds of projects.&lt;/p&gt;

&lt;p&gt;But that’s enough intro. Let’s get into it.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Navigation Basics
&lt;/h2&gt;

&lt;p&gt;We typically use the &lt;code&gt;NavigationStack&lt;/code&gt; and &lt;code&gt;NavigationLink&lt;/code&gt; components to handle navigation In our apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import SwiftUI

struct FirstView: View {
    var body: some View {
        NavigationStack {
            VStack {
                Text("Navigation article")
                    .font(.title)
                    .padding()

                NavigationLink("Go to the second view", destination: SecondView())
                    .padding()
            }
            .navigationTitle("First View")
        }
    }
}

struct SecondView: View {
    var body: some View {
        Text("This is the Second view")
            .font(.headline)
            .padding()
            .navigationTitle("Second View")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        FirstView()
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s take a detailed look at the most important navigational aspects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;NavigationStack&lt;/strong&gt; : This stack will handle our navigation. We wrap our content with it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NavigationLink&lt;/strong&gt; : This creates the link that will allow us to navigate to a different page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;.navigationTitle&lt;/strong&gt; : This allows us to define a title to each of our pages on the Navigation bar.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Injecting data to views
&lt;/h2&gt;

&lt;p&gt;We’re going to go slightly off-track here, if that’s ok. Injecting &lt;a href="https://dev.to/bugfenderapp/swiftdata-premier-beyond-persistence-in-ios-development-1c2o-temp-slug-8503372"&gt;data&lt;/a&gt; into views isn’t &lt;em&gt;directly&lt;/em&gt; related to navigation, but we often need to pass data between views, and this may still be useful when sharing new views.&lt;/p&gt;

&lt;p&gt;While there are many ways for views to access the business logic, and data, they need, we will look into a simple way of injecting data from one view to another.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import SwiftUI

struct FirstView: View {
    var recipes: [String]

    init(recipes: [String]) {
        self.recipes = recipes
    }

    var body: some View {
        NavigationStack {
            List(0..&amp;lt;recipes.count) { index in
                NavigationLink("\\(recipes[index])", destination: DetailView(recipe: recipes[index]))
            }
            .navigationTitle("Recipe List")
        }
    }
}

struct DetailView: View {
    let recipe: String

    var body: some View {
        Text("Here are the recipe details")
            .font(.largeTitle)
            .padding()
            .navigationTitle(recipe)
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to &lt;a href="https://dev.to/bugfenderapp/optimize-your-ios-app-perfomance-using-metrickit-41cg"&gt;launch our app&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@main
struct OurApp: App {
    var body: some Scene {
        WindowGroup {
            FirstView(recipes: ["Garlic bread", "Pepperoni Pizza", "Beef Gnocchi", "Fish and Chips"])
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, looking at both examples together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;DetailView&lt;/code&gt; accepts a parameter &lt;code&gt;recipe&lt;/code&gt;, which is provided when creating the &lt;code&gt;NavigationLink&lt;/code&gt;. The same exact thing happens with our &lt;code&gt;FirstView&lt;/code&gt;: it accepts an &lt;a href="https://dev.to/bugfenderapp/exploring-swift-collections-in-depth-guide-to-arrays-sets-and-dictionaries-503b"&gt;array of recipes&lt;/a&gt; that we need to feed it.&lt;/li&gt;
&lt;li&gt;Each &lt;code&gt;NavigationLink&lt;/code&gt; dynamically passes its recipe name to the &lt;code&gt;DetailView&lt;/code&gt; and we then use those as the titles of the &lt;code&gt;DetailView&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Navigation depending on our State
&lt;/h2&gt;

&lt;p&gt;The navigation we’ve seen so far is explicitly based on tapping a certain view. However, sometimes we might want to navigate forward/backward depending on the &lt;code&gt;@State&lt;/code&gt; of our &lt;code&gt;View&lt;/code&gt;. So let’s look at how to do this:&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import SwiftUI

struct FirstView: View {
    @State private var isDetailViewActive = false

    var body: some View {
        NavigationStack {
            VStack {
                Button("Go to Details") {
                    isDetailViewActive = true
                }
                .padding()

                .navigationDestination(isPresented: $isDetailViewActive, destination:{ DetailView()})
            }
            .navigationTitle("First View")
        }
    }
}

struct DetailView: View {
    var body: some View {
        Text("This is the detail view")
            .font(.headline)
            .padding()
            .navigationTitle("Detail View")
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;isDetailViewActive&lt;/code&gt; state variable controls the activation of the &lt;code&gt;NavigationLink&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This is useful when there’s the need to control when/where we should show a different page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important note:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While this is a very useful resource to navigate, be careful on how to use it. The user may find it odd if an app just navigates without telling them why, so don’t use this feature without contemplating user experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Navigation in Lists
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/bugfenderapp/complete-guide-to-swiftui-lists-for-developers-2b2k"&gt;Lists play a key role&lt;/a&gt; in many of our apps, and the cool thing is that integrating navigation within them is simple. We’ve already seen this on the second example, when we injected data into the next shown View, but we didn’t focus on that. So now let’s look again:&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import SwiftUI

struct FirstView: View {
    var recipes: [String]

    init(recipes: [String]) {
        self.recipes = recipes
    }

    var body: some View {
        NavigationStack {
            List(0..&amp;lt;recipes.count) { index in
                NavigationLink("\\(recipes[index])", destination: DetailView(recipe: recipes[index]))
            }
            .navigationTitle("Recipe List")
        }
    }
}

struct DetailView: View {
    let recipe: String

    var body: some View {
        Text("Here are the recipe details")
            .font(.largeTitle)
            .padding()
            .navigationTitle(recipe)
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two importan things are happening here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;List&lt;/code&gt; generates rows dynamically for each item in the &lt;code&gt;recipes&lt;/code&gt; &lt;a href="https://dev.to/bugfenderapp/advanced-swift-arrays-explore-sort-filter-map-and-reduce-and-more-41n8-temp-slug-162316"&gt;array&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Each row contains a &lt;code&gt;NavigationLink&lt;/code&gt; that leads to the &lt;code&gt;DetailView&lt;/code&gt; for the selected item, in which we then know what item was previously selected.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Customizing Appearance
&lt;/h2&gt;

&lt;p&gt;One of the great things about SwiftUI is that you can customize the navigation bar’s appearance, including adding &lt;a href="https://dev.to/bugfenderapp/swiftui-buttons-simplified-a-step-by-step-guide-jop-temp-slug-3372944"&gt;buttons or changing UI styles&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s look at an example of add a few of those buttons and, additionally, how we can navigate from them, modally, to new &lt;code&gt;Views&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import SwiftUI

struct FirstView: View {
    @State var areSettingsSelected: Bool = false
    @State var isHelpSelected: Bool = false

    var body: some View {
        NavigationStack {
            VStack {
                Text("Main View")
                    .font(.largeTitle)
                    .navigationDestination(isPresented: $areSettingsSelected, destination:{ SettingsView()})

                        .navigationDestination(isPresented: $isHelpSelected, destination:{ HelpView()})
            }

            .navigationTitle("Main page")
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button("Settings") {
                        areSettingsSelected = true
                    }
                }

                ToolbarItem(placement: .navigationBarTrailing) {
                    Button("Help") {
                        isHelpSelected = true
                    }
                }
            }

        }
    }
}

struct SettingsView: View {
    var body: some View {
        Text("This is the Settings view")
            .font(.headline)
            .padding()
            .navigationTitle("Settings")
    }
}

struct HelpView: View {
    var body: some View {
        Text("This is the Help view")
            .font(.headline)
            .padding()
            .navigationTitle("Help")
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Use the &lt;code&gt;.toolbar&lt;/code&gt; modifier to add custom buttons or other controls to the navigation bar.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;ToolbarItem&lt;/code&gt; placement determines where the control appears.&lt;/li&gt;
&lt;li&gt;We use the technique shown before, on 3, to show the user &lt;code&gt;Settings&lt;/code&gt; or &lt;code&gt;Help&lt;/code&gt; depending on the &lt;code&gt;View State&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Deep Linking and Navigation Paths
&lt;/h2&gt;

&lt;p&gt;For any less basic navigational needs, SwiftUI provide us with the &lt;code&gt;NavigationPath&lt;/code&gt; APIs, that help us handle the navigational stack.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import SwiftUI

struct FirstView: View {
    @State private var path = NavigationPath()
    let fruits: [Fruit] = [Fruit(name: "Apple"), Fruit(name: "Banana"), Fruit(name: "Strawberry")]

    var body: some View {
        NavigationStack(path: $path) {
            List(fruits) { fruit in
                Button(fruit.name) {
                    path.append(fruit.id)
                }
            }
            .navigationDestination(for: String.self) { fruit in
                DetailView(fruit: fruit)
            }
            .navigationTitle("Fruits")
        }
    }
}

struct DetailView: View {
    let fruit: String

    var body: some View {
        Text("You selected \\(fruit)")
            .font(.title)
            .padding()
            .navigationTitle(fruit)
    }
}

struct Fruit: Identifiable {
    var id: String

    var name: String

    init(name: String) {
        self.name = name;
        self.id = name
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Things of note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NavigationPath&lt;/code&gt; allows us to set the fruit’s ID as a new path node. This can later be used to dismiss all Views until we reach a destination one.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;.navigationDestination(for:)&lt;/code&gt; to specify how to handle destinations dynamically, based on the type of the path element. So we could have &lt;a href="https://dev.to/bugfenderapp/write-better-code-using-swift-enums-a-detailed-guide-5b34"&gt;different things&lt;/a&gt;, like animals, and it would handle them accordingly for each type.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SwiftUI’s navigation tools, including &lt;code&gt;NavigationStack&lt;/code&gt;, &lt;code&gt;NavigationLink&lt;/code&gt;, and &lt;code&gt;NavigationPath&lt;/code&gt;, provide the flexibility to build intuitive, dynamic navigation flows in your apps. From simple links to complex navigation paths, SwiftUI ensures your app’s navigation is smooth and user-friendly.&lt;/p&gt;

&lt;p&gt;Explore the examples above, adapt them to your projects, and you’ll be able to maximize SwiftUI’s capabilities to create great user experiences.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>switui</category>
    </item>
  </channel>
</rss>
