<?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>DEV Community: Qandil Tariq</title>
    <description>The latest articles on DEV Community by Qandil Tariq (@qandil_tariq_0e73fd980d58).</description>
    <link>https://dev.to/qandil_tariq_0e73fd980d58</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%2F3476376%2Faee39ad1-8ae5-4c7e-9a64-428c3c6260b9.png</url>
      <title>DEV Community: Qandil Tariq</title>
      <link>https://dev.to/qandil_tariq_0e73fd980d58</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/qandil_tariq_0e73fd980d58"/>
    <language>en</language>
    <item>
      <title>Building a Real-Time Heart Rate Monitoring App with Kotlin + Google Cloud</title>
      <dc:creator>Qandil Tariq</dc:creator>
      <pubDate>Wed, 17 Sep 2025 08:34:52 +0000</pubDate>
      <link>https://dev.to/qandil_tariq_0e73fd980d58/building-a-real-time-heart-rate-monitoring-app-with-kotlin-google-cloud-aic</link>
      <guid>https://dev.to/qandil_tariq_0e73fd980d58/building-a-real-time-heart-rate-monitoring-app-with-kotlin-google-cloud-aic</guid>
      <description>&lt;p&gt;Health monitoring apps are everywhere, but most rely on external wearables.&lt;br&gt;&lt;br&gt;
What if you could track your &lt;strong&gt;heart rate directly on your phone&lt;/strong&gt; and still sync results to the cloud for secure storage and analysis?  &lt;/p&gt;

&lt;p&gt;That’s exactly what I built as part of my MSc dissertation: a &lt;strong&gt;Heart Rate Monitoring App&lt;/strong&gt; powered by &lt;strong&gt;Kotlin (Jetpack Compose)&lt;/strong&gt; and a &lt;strong&gt;Python + Firestore backend&lt;/strong&gt;.  &lt;/p&gt;




&lt;h2&gt;
  
  
  🚩 Problem
&lt;/h2&gt;

&lt;p&gt;Wearables are expensive and not always accessible.&lt;br&gt;&lt;br&gt;
So the question was:  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can we capture, process, and store heart rate data using only a smartphone, and still keep it reliable and secure?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ✨ Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📱 Real-time heart rate monitoring
&lt;/li&gt;
&lt;li&gt;🚨 Threshold alerts (notify if BPM is abnormal)
&lt;/li&gt;
&lt;li&gt;☁️ Cloud sync with Firestore
&lt;/li&gt;
&lt;li&gt;📊 Reports + trends visualization
&lt;/li&gt;
&lt;li&gt;🔐 User authentication with Firebase
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🛠 Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Kotlin, Jetpack Compose, MVVM, Coroutines
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Python Flask microservice
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud:&lt;/strong&gt; Google Cloud Firestore
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth:&lt;/strong&gt; Firebase Authentication
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD:&lt;/strong&gt; GitHub Actions
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚙️ How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;User signs in (Firebase Auth).
&lt;/li&gt;
&lt;li&gt;The app captures heart rate (camera/sensor).
&lt;/li&gt;
&lt;li&gt;Readings are sent to &lt;strong&gt;Firestore&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;A Python backend checks for alerts.
&lt;/li&gt;
&lt;li&gt;Users view reports + trends in the app.
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  💻 Sample Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Kotlin — Saving Data to Firestore
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;db&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FirebaseFirestore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;heartRateData&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hashMapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"userId"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"heartRate"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;bpm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"status"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"timestamp"&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HeartRateReading"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heartRateData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOnSuccessListener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Firestore"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Saved!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOnFailureListener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;w&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Firestore"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Jetpack Compose — Displaying Trends
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;HeartRateCard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Avg: $avg BPM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fontWeight&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FontWeight&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Bold&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Max: $max BPM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Red&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Min: $min BPM"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Blue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Python — Simple Alerting Service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;google.cloud&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;firestore&lt;/span&gt;

&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;firestore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_alerts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HeartRateReading&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;heartRate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;140&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;⚠️ High HR detected: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;heartRate&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; BPM for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;userId&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📊 Screenshots
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(Add your app UI and Firestore console screenshots here)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎓 Lessons Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;On-device vs cloud → trade-offs between &lt;strong&gt;privacy and performance&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Importance of &lt;strong&gt;data security&lt;/strong&gt; in healthcare apps
&lt;/li&gt;
&lt;li&gt;A clean UI/UX makes adoption easier
&lt;/li&gt;
&lt;li&gt;Cloud pipelines = real-time dashboards + analysis
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🗺 Roadmap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🤖 Add ML models for anomaly detection
&lt;/li&gt;
&lt;li&gt;❤️ Expand to other vitals (SpO2, BP)
&lt;/li&gt;
&lt;li&gt;🍎 Build iOS client with Kotlin Multiplatform
&lt;/li&gt;
&lt;li&gt;🏥 Healthcare integration (e.g., NHS)
&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;This project showed me how &lt;strong&gt;AI + Mobile + Cloud&lt;/strong&gt; can combine to deliver meaningful health insights.  &lt;/p&gt;

&lt;p&gt;👉 Code available here: &lt;a href="https://github.com/Qandil11/HeartRateMobile" rel="noopener noreferrer"&gt;HeartRateMobile&lt;/a&gt;&lt;br&gt;&lt;br&gt;
👉 Medium version (more detailed): &lt;a href="https://medium.com/@qandil.tariq11/real-time-health-monitoring-on-android-from-device-to-cloud-cde080a2a70d" rel="noopener noreferrer"&gt;Real-Time Health Monitoring on Android&lt;/a&gt;  &lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading! Follow me for more posts about Android, AI, and cross-platform development 🚀&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>googlecloud</category>
      <category>kotlin</category>
      <category>ai</category>
    </item>
    <item>
      <title>SmartCV: An AI-Powered Resume Analyzer with Jetpack Compose &amp; Kotlin Multiplatform</title>
      <dc:creator>Qandil Tariq</dc:creator>
      <pubDate>Sat, 13 Sep 2025 20:11:17 +0000</pubDate>
      <link>https://dev.to/qandil_tariq_0e73fd980d58/smartcv-an-ai-powered-resume-analyzer-with-jetpack-compose-kotlin-multiplatform-16n6</link>
      <guid>https://dev.to/qandil_tariq_0e73fd980d58/smartcv-an-ai-powered-resume-analyzer-with-jetpack-compose-kotlin-multiplatform-16n6</guid>
      <description>&lt;p&gt;Job hunting is stressful — tailoring resumes to every job description is even worse.&lt;br&gt;
So I built SmartCV, a mobile app that analyzes your resume and matches it against a job description — highlighting missing skills and suggesting improvements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech Stack&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kotlin Multiplatform (KMP) → shared scoring + JD matching logic&lt;/li&gt;
&lt;li&gt;Jetpack Compose → Android UI&lt;/li&gt;
&lt;li&gt;PdfBox-Android → PDF parsing&lt;/li&gt;
&lt;li&gt;Custom NLP-inspired Scoring Engine → resume quality + JD keyword match&lt;/li&gt;
&lt;li&gt;GitHub Actions CI → build + test automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Resume Analysis (Core Logic)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;`fun analyzeResumeText(text: String): AnalysisResult {&lt;br&gt;
    val wordCount = text.split("\s+".toRegex()).size&lt;br&gt;
    val verbHits = ACTION_VERBS.count { text.contains(it, ignoreCase = true) }&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val score = (verbHits * 10) + (if (wordCount in 400..900) 20 else 0)

return AnalysisResult(
    overallScore = score,
    signals = mapOf("Word Count" to wordCount, "Action Verbs" to verbHits),
    recommendations = buildRecommendations(wordCount, verbHits)
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}`&lt;br&gt;
This checks word count, action verbs, and generates recommendations like:&lt;/p&gt;

&lt;p&gt;“Add more quantified bullets”&lt;/p&gt;

&lt;p&gt;“Resume is too long — trim to 2 pages”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Matching with a Job Description&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;val resumeTokens = tokenize(resumeText).tokens.keys.map(::stem).toSet()
val jdTokens = tokenize(jdText).tokens.keys.map(::stem).toSet()

val present = jdTokens.intersect(resumeTokens)
val matchScore = (present.size.toDouble() / jdTokens.size * 100).roundToInt()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SmartCV computes a match score (%) and highlights missing keywords.&lt;br&gt;
Example: if JD requires CI/CD and DataStore but your resume doesn’t mention them → flagged as gaps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UI (Jetpack Compose)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Circular gauge for overall score&lt;/li&gt;
&lt;li&gt;Progress bars for sub-scores (word count, verbs, metrics)&lt;/li&gt;
&lt;li&gt;Chips for matched &amp;amp; missing skills&lt;/li&gt;
&lt;li&gt;JD Match card with missing keyword suggestions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CI/CD&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The repo ships with a GitHub Actions workflow:&lt;/li&gt;
&lt;li&gt;Build &amp;amp; test on every PR&lt;/li&gt;
&lt;li&gt;Assemble APK &amp;amp; upload as artifact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What’s Next&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Export to PDF/CSV&lt;/li&gt;
&lt;li&gt;Add ATS-style scoring (readability, formatting)&lt;/li&gt;
&lt;li&gt;Extend KMP for iOS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Repo → &lt;a href="https://github.com/Qandil11/SmartCV-ResumeAnalyzer/tree/main" rel="noopener noreferrer"&gt;https://github.com/Qandil11/SmartCV-ResumeAnalyzer/tree/main&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo Video → &lt;a href="https://www.youtube.com/shorts/p_aBx-PjTWk" rel="noopener noreferrer"&gt;https://www.youtube.com/shorts/p_aBx-PjTWk&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Would love your feedback!&lt;/strong&gt;&lt;br&gt;
What other features would make this useful for developers/job seekers?&lt;/p&gt;

</description>
      <category>android</category>
      <category>ai</category>
      <category>kmp</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Build an OCR Action pipeline with Kotlin: ML Kit (Android) + Ktor (server) + KMP roadmap</title>
      <dc:creator>Qandil Tariq</dc:creator>
      <pubDate>Thu, 04 Sep 2025 13:29:26 +0000</pubDate>
      <link>https://dev.to/qandil_tariq_0e73fd980d58/build-an-ocr-action-pipeline-with-kotlin-ml-kit-android-ktor-server-kmp-roadmap-5efc</link>
      <guid>https://dev.to/qandil_tariq_0e73fd980d58/build-an-ocr-action-pipeline-with-kotlin-ml-kit-android-ktor-server-kmp-roadmap-5efc</guid>
      <description>&lt;p&gt;LetterLens scans a paper letter, extracts text with ML Kit, classifies it, pulls deadlines, and returns what/when/next. Android UI is Jetpack Compose; the API is Ktor; and the core parsing is moving to Kotlin Multiplatform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Demo:&lt;/strong&gt; &lt;a href="https://youtube.com/shorts/lvZbWHVL1h4" rel="noopener noreferrer"&gt;https://youtube.com/shorts/lvZbWHVL1h4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/Qandil11/LetterLens" rel="noopener noreferrer"&gt;https://github.com/Qandil11/LetterLens&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture (minimal)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Android app:&lt;/strong&gt; Compose + ML Kit OCR → sends extracted text&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ktor server:&lt;/strong&gt; /explain returns { type, deadline, summary, actions, citations }&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KMP (WIP):&lt;/strong&gt; move parsing/classifier into :shared for reuse&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run it locally (quickstart)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;//  Start the server&lt;br&gt;
./gradlew :server:run&lt;br&gt;
// healthcheck&lt;br&gt;
curl http://127.0.0.1:8080/health&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Device networking options&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Emulator:&lt;/strong&gt; base URL = &lt;a href="http://10.0.2.2:8080/" rel="noopener noreferrer"&gt;http://10.0.2.2:8080/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Physical device (USB):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;adb reverse tcp:8080 tcp:8080&lt;/p&gt;

&lt;p&gt;base URL = &lt;a href="http://127.0.0.1:8080/" rel="noopener noreferrer"&gt;http://127.0.0.1:8080/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Physical device (Wi-Fi):&lt;/strong&gt; use your Mac/PC LAN IP, e.g. &lt;a href="http://192.168.x.y:8080/" rel="noopener noreferrer"&gt;http://192.168.x.y:8080/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;!-- app/src/debug/AndroidManifest.xml --&amp;gt;&lt;br&gt;
&amp;lt;application android:usesCleartextTraffic="true" /&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tiny Ktor module (sketch)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fun Application.module() {&lt;br&gt;
  install(ContentNegotiation) { json() }&lt;br&gt;
  routing {&lt;br&gt;
    get("/health") { call.respond(mapOf("ok" to true)) }&lt;br&gt;
    post("/explain") {&lt;br&gt;
      val req = call.receive&amp;lt;ExplainReq&amp;gt;()&lt;br&gt;
      val type = classify(req.text, req.hint)&lt;br&gt;
      val deadline = extractDeadline(req.text, type)&lt;br&gt;
      val (summary, actions, citations) = explainForType(type, req.text)&lt;br&gt;
      call.respond(ExplainRes(type, deadline, summary, actions, citations))&lt;br&gt;
    }&lt;br&gt;
  }&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why KMP here?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The classification/date parsing is pure Kotlin → easy to share across Android/iOS/desktop.&lt;/p&gt;

&lt;p&gt;Keeps the server thin; long-term we can ship fully on-device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Roadmap&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Swap rule-based classifier for a compact model&lt;/p&gt;

&lt;p&gt;Expand letter libraries (NHS/Council/HMRC/DVLA/UKVI)&lt;/p&gt;

&lt;p&gt;Accessibility polish and offline mode&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; #android #kotlin #kmp #jetpackcompose #ktor #mlkit #ocr #nlp #govtech&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>ios</category>
    </item>
    <item>
      <title>A Tiny KMP Connectivity Monitor (Android + iOS) — No Pods Required</title>
      <dc:creator>Qandil Tariq</dc:creator>
      <pubDate>Tue, 02 Sep 2025 19:38:27 +0000</pubDate>
      <link>https://dev.to/qandil_tariq_0e73fd980d58/a-tiny-kmp-connectivity-monitor-android-ios-no-pods-required-1hof</link>
      <guid>https://dev.to/qandil_tariq_0e73fd980d58/a-tiny-kmp-connectivity-monitor-android-ios-no-pods-required-1hof</guid>
      <description>&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ItkwVbaN8eo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I open-sourced a minimal &lt;strong&gt;Kotlin Multiplatform&lt;/strong&gt; connectivity monitor that exposes a single&lt;br&gt;&lt;br&gt;
&lt;code&gt;StateFlow&amp;lt;ConnectivityStatus&amp;gt;&lt;/code&gt; from shared code.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Android:&lt;/strong&gt; &lt;code&gt;ConnectivityManager&lt;/code&gt; callbacks • &lt;strong&gt;iOS:&lt;/strong&gt; &lt;code&gt;SCNetworkReachability&lt;/code&gt; (no CocoaPods).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/Qandil11/KMP-Connectivity-Monitor" rel="noopener noreferrer"&gt;https://github.com/Qandil11/KMP-Connectivity-Monitor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Demo (YouTube): &lt;a href="https://www.youtube.com/watch?v=ItkwVbaN8eo" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=ItkwVbaN8eo&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Why I built this
&lt;/h2&gt;

&lt;p&gt;Most apps need to react to connectivity. I wanted a &lt;strong&gt;production-style&lt;/strong&gt;, zero-friction approach for &lt;strong&gt;Compose Multiplatform&lt;/strong&gt; that keeps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one API in &lt;code&gt;commonMain&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;native implementations per platform&lt;/li&gt;
&lt;li&gt;no CocoaPods requirement on iOS&lt;/li&gt;
&lt;li&gt;easy to use from Compose&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built &lt;strong&gt;KMP Connectivity Monitor&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The tiny API (commonMain)
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// com.qandil.kmpconnectivity.connectivity.Connectivity.kt&lt;/span&gt;
&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.qandil.kmpconnectivity.connectivity&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlinx.coroutines.flow.StateFlow&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConnectivityStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Online&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Offline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Unavailable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ConnectivityMonitor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ConnectivityStatus&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConnectivityMonitorFactory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;ConnectivityMonitor&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Android actual — ConnectivityManager
&lt;/h2&gt;

&lt;p&gt;Listen to ConnectivityManager callbacks and update a MutableStateFlow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shared/src/androidMain/.../connectivity/Connectivity.android.kt (excerpt)&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;updateNow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;caps&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getNetworkCapabilities&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;activeNetwork&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;online&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;caps&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;hasCapability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NetworkCapabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NET_CAPABILITY_INTERNET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                 &lt;span class="n"&gt;caps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hasCapability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NetworkCapabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NET_CAPABILITY_VALIDATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;online&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;ConnectivityStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Online&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nc"&gt;ConnectivityStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Offline&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;updateNow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;req&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NetworkRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCapability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NetworkCapabilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NET_CAPABILITY_INTERNET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;cm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerNetworkCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Android manifest (app module):
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;uses-permission&lt;/span&gt; &lt;span class="n"&gt;android&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.INTERNET"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;uses-permission&lt;/span&gt; &lt;span class="n"&gt;android&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.ACCESS_NETWORK_STATE"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tip (for library lint): shared/src/androidMain/AndroidManifest.xml
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;manifest&lt;/span&gt; &lt;span class="n"&gt;xmlns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;android&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res/android"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;uses-permission&lt;/span&gt; &lt;span class="n"&gt;android&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.ACCESS_NETWORK_STATE"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="n"&gt;iOS&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="nc"&gt;SystemConfiguration&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="nc"&gt;Pods&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use SCNetworkReachability to monitor the default route and update the flow.
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shared/src/iosMain/.../connectivity/Connectivity.ios.kt (excerpt)&lt;/span&gt;
&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;OptIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kotlinx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cinterop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ExperimentalForeignApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SCNetworkReachabilityFlags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;reachable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;kSCNetworkReachabilityFlagsReachable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;needsConn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="n"&gt;kSCNetworkReachabilityFlagsConnectionRequired&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toInt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;online&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reachable&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;needsConn&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;online&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;ConnectivityStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Online&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nc"&gt;ConnectivityStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Offline&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Gradle link (SystemConfiguration):
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shared/build.gradle.kts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget&lt;/span&gt;

&lt;span class="nf"&gt;kotlin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withType&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;KotlinNativeTarget&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;configureEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;binaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;linkerOpts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-framework"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SystemConfiguration"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prefer NWPathMonitor? Switch the iOS impl to Network.framework and link that instead.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Using it in Compose
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ConnectivityMonitorFactory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;monitor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;remember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;status&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collectAsState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConnectivityStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Unavailable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nc"&gt;LaunchedEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nc"&gt;DisposableEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;onDispose&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;monitor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// render your UI (e.g., a status card with Online / Offline / … Checking)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Platform entry points:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Android&lt;/span&gt;
&lt;span class="nf"&gt;setContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qandil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kmpconnectivity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connectivity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ConnectivityMonitorFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// iOS&lt;/span&gt;
&lt;span class="nc"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qandil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;kmpconnectivity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connectivity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ConnectivityMonitorFactory&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to run
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Android&lt;/strong&gt;: ./gradlew :androidApp:assembleDebug (or Run from Android Studio)&lt;/p&gt;

&lt;p&gt;iOS: Run from Android Studio’s iOS config or your Xcode project (Simulator/Device)&lt;/p&gt;

&lt;h2&gt;
  
  
  Roadmap / help wanted
&lt;/h2&gt;

&lt;p&gt;Flow.retryWhenOnline(monitor) helper&lt;/p&gt;

&lt;p&gt;Expose network type (Wi-Fi/Cellular)&lt;/p&gt;

&lt;h2&gt;
  
  
  Tests with stubs
&lt;/h2&gt;

&lt;p&gt;If this was useful, a ⭐ or PR would mean a lot:&lt;br&gt;
Repo: &lt;a href="https://github.com/Qandil11/KMP-Connectivity-Monitor" rel="noopener noreferrer"&gt;https://github.com/Qandil11/KMP-Connectivity-Monitor&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;MIT (in the repo).&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>kotlinmultiplatform</category>
      <category>composemultiplatform</category>
      <category>android</category>
    </item>
  </channel>
</rss>
