<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Rizwan - iOS Developer</title><description>iOS developer writing about Apple platforms, AI-augmented workflows, and open source.</description><link>https://rizwan.dev/</link><item><title>AI Agents - Close the loop for mobile apps</title><link>https://rizwan.dev/blog/2026-01-18_ai-agents-close-the-loop-for-mobile-apps/</link><guid isPermaLink="true">https://rizwan.dev/blog/2026-01-18_ai-agents-close-the-loop-for-mobile-apps/</guid><description>Implementing AI agents to enable autonomous decision-making and action loops in mobile applications</description><pubDate>Sun, 18 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;The Loop That Actually Ships&lt;/h2&gt;
&lt;p&gt;Most app flows are linear: a user does something, the app responds, and we stop. Agents break that line and turn it into a system that watches, decides, and tries again until the goal is met.&lt;/p&gt;
&lt;h3&gt;Diagram&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Goal
↓
Sense → Decide → Act → Measure
↑ ↓
└─────────── Update ────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the loop closed, you get:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tasks that finish without constant prompts&lt;/li&gt;
&lt;li&gt;Recovery paths when something fails&lt;/li&gt;
&lt;li&gt;Behavior that adapts as the environment changes&lt;/li&gt;
&lt;li&gt;Less manual babysitting&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Mobile-Specific Challenges&lt;/h2&gt;
&lt;p&gt;Mobile is different. In web or backend work, you can simulate most behavior locally. On mobile, the real device matters. Cameras, sensors, thermal throttling, permissions, and OEM quirks are hard to fake. That makes the loop slower because humans end up doing the device-side validation.&lt;/p&gt;
&lt;p&gt;Closing the loop on mobile means connecting the agent to real hardware and real state. For Android, that often looks like direct access to ADB so the agent can install builds, drive the UI, capture logs, and verify what actually happens on the device.&lt;/p&gt;
&lt;h2&gt;A Real Example&lt;/h2&gt;
&lt;p&gt;I was debugging a camera focus issue in an Android app. My old workflow was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ask a coding model for a fix.&lt;/li&gt;
&lt;li&gt;Build in Android Studio.&lt;/li&gt;
&lt;li&gt;Run on device.&lt;/li&gt;
&lt;li&gt;Reproduce manually.&lt;/li&gt;
&lt;li&gt;Repeat.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It worked, but it was slow and repetitive.&lt;/p&gt;
&lt;p&gt;So I asked if the agent could verify its own changes. Once my device was connected over USB, it could run ADB commands to install builds, start the app, drive the camera flow, and capture outputs. I let it run for a couple of hours while it tried different hypotheses.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hey, can you test this on the real device USB connected? Run whatever ADB commands you need, keep iterating until it works, and write every attempt plus screenshots into a md log file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The result was a detailed markdown log of every experiment, complete with screenshots and what changed between attempts. I still had to steer it a few times, but it eventually found the root cause and taught me a lot about CameraX and Camera2 along the way.&lt;/p&gt;
&lt;h2&gt;Why This Matters&lt;/h2&gt;
&lt;p&gt;The compounding benefit is speed and rigor:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The agent can execute more experiments than I can in the same time.&lt;/li&gt;
&lt;li&gt;Every attempt is logged, so the learning is reusable.&lt;/li&gt;
&lt;li&gt;The loop closes on the real device, not just on a simulated environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is what makes agents powerful for mobile: not just code generation, but autonomous testing and verification on actual hardware.&lt;/p&gt;
</content:encoded></item><item><title>Blank SFSafariViewController</title><link>https://rizwan.dev/blog/blank_safari_viewcontroller/</link><guid isPermaLink="true">https://rizwan.dev/blog/blank_safari_viewcontroller/</guid><description>Possible fix for blank SFSafariViewController</description><pubDate>Mon, 08 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently We got continuous report from our clients saying they were getting blank In-App browser (&lt;code&gt;SFSafariViewController&lt;/code&gt;) instead of the web page it was intended to show. After a little bit of debugging I figured out that It was actually bug from iOS side.&lt;/p&gt;
&lt;p&gt;There is even a radar about it which you can find &lt;a href=&quot;http://openradar.appspot.com/22972440&quot;&gt;here&lt;/a&gt;. Looks like it was intentionally done by Apple. Here is the response from the radar&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is done to ensure SafariViewController can’t be used for loading content offscreen.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In our app, we don&apos;t actually load anything offscreen. There could be some delay to present the &lt;code&gt;SFSafariViewController&lt;/code&gt;. These are the possible fix I found on the internet.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make a delay before presenting the view controller&lt;/li&gt;
&lt;li&gt;Load the view before in hand&lt;/li&gt;
&lt;li&gt;Make the current window as key window&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adding delay is not user friendly so let&apos;s not look into that.
Loading view before in hand works on some cases.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let controller = SFSafariViewController(url: url)
controller.delegate = self // load the view before in hand
present(controller, animated: true, completion: nil)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Other option would be make the current window as key window (only incase if you use multiple windows)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let window = &amp;lt;&amp;lt; your current window &amp;gt;&amp;gt;
window.makeKey()
let controller = SFSafariViewController(url: url)
controller.delegate = self
present(controller, animated: true, completion: nil)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What actually works for my case is to present the &lt;code&gt;SFSafariViewController&lt;/code&gt; from key window root view controller instead of some view controller in the view hierarchy.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; func openUrl(_ urlString: String, from viewController: UIViewController, completion: (() -&amp;gt; Swift.Void)? = nil) {
    guard let url = URL(string: urlString) else {
        showIncorrectUrlAlert(urlString, from: viewController)
        return
    }

    let vc = SFSafariViewController(url: url)
    vc.delegate = viewController as? SFSafariViewControllerDelegate
    // Present with window root view controller to avoid blank SFSafariViewController
    keyWindow?.rootViewController?.present(vc, animated: true, completion: nil)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My guess for this issue or fix would be disconnected view when you push safari view controller that causes window changes.&lt;/p&gt;
</content:encoded></item><item><title>Cleanup for &quot;Untitled&quot; tvOS App</title><link>https://rizwan.dev/blog/cleanup-for-untitled-tvos-app/</link><guid isPermaLink="true">https://rizwan.dev/blog/cleanup-for-untitled-tvos-app/</guid><description>How I cleaned up the MVP for the new tvOS app</description><pubDate>Wed, 05 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/blog/new-xcode-project&quot;&gt;Initial Idea and tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/plan-for-untitled-tvos-app&quot;&gt;Planning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/mvp-for-untitled-tvos-app&quot;&gt;MVP&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Cleanup&lt;/h2&gt;
&lt;p&gt;As I mentioned in my previous post, the initial MVP was okay with some quirks. My first goal was to get the app running reliably and worry about refinement later. To push it forward, I cycled through three tools and ran several sessions.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Claude Code with Sonnet 4.5 for planning and Haiku 4.5 for todos&lt;/li&gt;
&lt;li&gt;Codex CLI with GPT Codex Medium&lt;/li&gt;
&lt;li&gt;Amp in free mode&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Amp had just released its free plan, and I wanted to try it that day. I had a perfect candidate, so I asked Amp CLI to fix issues one after another. When I wasn’t happy with the outputs, I moved to different agents.&lt;/p&gt;
&lt;p&gt;I solved the following issues and learned a lot about how IPTV works in the background. Some of it felt not entirely above board, but it’s strictly for personal use.&lt;/p&gt;
&lt;p&gt;Here are the main issues I worked through:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Made sure the API service was working by asking for a test file. Instead of running the app in the simulator every time, I wanted the agent to verify its own work.&lt;/li&gt;
&lt;li&gt;Correctly wired the API service so every endpoint and tab view behaved.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By the end I had a somewhat working app where I could log in, see folders, browse live channels, and even play them with the default &lt;code&gt;AVPlayer&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Satisfaction Level&lt;/h2&gt;
&lt;p&gt;But I wasn’t satisfied with the results. Here’s what I would have done better if I were building it myself:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The whole app had focus issues.&lt;/li&gt;
&lt;li&gt;Weird focus states with white borders.&lt;/li&gt;
&lt;li&gt;Card layouts looked rough.&lt;/li&gt;
&lt;li&gt;Live streaming stopped after one minute.&lt;/li&gt;
&lt;li&gt;Settings UI stretched awkwardly.&lt;/li&gt;
&lt;li&gt;Search didn’t work.&lt;/li&gt;
&lt;li&gt;Folder navigation felt clunky.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even though the app technically ran, it still wasn’t usable. Up to this point I hadn’t reviewed the code much. I skimmed a few files, but because I’m still learning tvOS SwiftUI I assumed things were fine and accepted the changes.&lt;/p&gt;
&lt;p&gt;When I ran the app on Apple TV, I wasn’t happy with the outcome. I wanted a polished experience, and instead I found more problems with back-button behavior. So I stepped away—motivation gone.&lt;/p&gt;
</content:encoded></item><item><title>Hidden messages from Advent Of Code challenge</title><link>https://rizwan.dev/blog/Hidden-Message-Advent-Of-Code/</link><guid isPermaLink="true">https://rizwan.dev/blog/Hidden-Message-Advent-Of-Code/</guid><description>Hidden messages which came from Advent Of Code challenge</description><pubDate>Sun, 22 Dec 2019 06:44:02 GMT</pubDate><content:encoded>&lt;p&gt;Incase if you don&apos;t know &lt;a href=&quot;https://adventofcode.com/&quot;&gt;Advent Of Code&lt;/a&gt; is a small programming puzzles which can be solved in any programming language of your choice. It runs throughout the December and each day the complexity will increase.&lt;/p&gt;
&lt;p&gt;This year the over all theme was about Santa on some other planet and how we help him to reach earth on time for Christmas. I started little late on Day 2 and tried to catch up all 4 puzzles.&lt;/p&gt;
&lt;p&gt;Interesting thing I found today is one of the puzzle input was to figure out the input values to get output as &lt;code&gt;19690720&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you look closely to the value it&apos;s actually a Date - 1960/07/20. Which is the exact date of Apollo Moon landing. 🤯 Also lots of keywords and things used on Apollo Guidance computer like Noun and Verb. Never expected hidden messages from the puzzle. Good Job for the Apollo nostalgia &lt;a href=&quot;https://twitter.com/ericwastl&quot;&gt;Eric Wastl&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;P.S: If you are following the advent calendar and wanted to check my solution in swift, you can check at this &lt;a href=&quot;https://github.com/rizwankce/AdventOfCode&quot;&gt;repo&lt;/a&gt; on Github. I will try to push it to GitHub whenever I found a solution.&lt;/p&gt;
</content:encoded></item><item><title>Homework for &quot;Untitled&quot; tvOS App</title><link>https://rizwan.dev/blog/home-work-for-untitled-tvos-app/</link><guid isPermaLink="true">https://rizwan.dev/blog/home-work-for-untitled-tvos-app/</guid><description>How I did the homework for the new tvOS app</description><pubDate>Thu, 06 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/blog/new-xcode-project&quot;&gt;Initial Idea and tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/plan-for-untitled-tvos-app&quot;&gt;Planning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/mvp-for-untitled-tvos-app&quot;&gt;MVP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/cleanup-for-untitled-tvos-app&quot;&gt;Cleanup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Homework&lt;/h2&gt;
&lt;p&gt;The app wasn’t great. It was usable, but not even close to a four-star experience—more like a one-star prototype. I wanted to get it to at least four stars, so I did the one thing I hadn’t done yet: real homework, no LLMs involved.&lt;/p&gt;
&lt;p&gt;I’ve been building for Apple platforms since the pre-ARC iOS 5 days, and I usually follow a personal checklist for new apps. This time I skipped it, and the results made that obvious. Time to catch up.&lt;/p&gt;
&lt;h3&gt;Inspirations&lt;/h3&gt;
&lt;p&gt;First, I needed to study real-world apps and how their UI behaves. I installed a bunch of IPTV apps on Apple TV and tested almost all of them. One stood out: &lt;a href=&quot;https://apps.apple.com/us/app/uhf-love-your-iptv/id6443751726?platform=appleTV&quot;&gt;UHF&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I might have used an LLM here—I asked Perplexity for IPTV app inspiration. Reddit pointed me toward a few options, and that’s how I found UHF.&lt;/p&gt;
&lt;p&gt;I don’t know who built it, but it’s polished. The card layouts are great, everything feels snappy, and it has tons of features I hadn’t considered.&lt;/p&gt;
&lt;p&gt;Mad respect to that developer—they shipped it pre-LLM.&lt;/p&gt;
&lt;h3&gt;tvOS 101&lt;/h3&gt;
&lt;p&gt;Next, I needed to understand how modern tvOS apps are built with SwiftUI. What does it take to achieve that level of polish? So I did what any Apple platform developer would do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Read Apple’s Human Interface Guidelines for tvOS.&lt;/li&gt;
&lt;li&gt;Watched relevant WWDC sessions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Turns out tvOS didn’t get major features this year. I expected some SwiftUI updates—maybe liquid glass on tvOS 18—but the best resource I found was from tvOS 17: a session about migrating TVML-based apps to SwiftUI, complete with a sample project. I ran the sample and it worked perfectly on tvOS 18.&lt;/p&gt;
&lt;p&gt;The session was &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2024/10207/&quot;&gt;WWDC24 session 10207: Migrate your TVML app to SwiftUI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After watching the session and running the sample in the simulator, I had a much better sense of how tvOS and SwiftUI fit together. Now I knew what I needed.&lt;/p&gt;
&lt;p&gt;What do you think I did next?&lt;/p&gt;
</content:encoded></item><item><title>Introducing vector-triage</title><link>https://rizwan.dev/blog/introducing-vector-triage/</link><guid isPermaLink="true">https://rizwan.dev/blog/introducing-vector-triage/</guid><description>Zero-config duplicate and similar detection for GitHub Issues and Pull Requests</description><pubDate>Sun, 15 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I just open-sourced &lt;strong&gt;vector-triage&lt;/strong&gt; over the weekend:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href=&quot;https://github.com/rizwankce/vector-triage&quot;&gt;https://github.com/rizwankce/vector-triage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It runs as a GitHub Action and helps detect duplicates and similar items for Issues and PRs.&lt;/p&gt;
&lt;p&gt;The idea started from a Twitter discussion when &lt;a href=&quot;https://x.com/steipete&quot;&gt;Pieter (@steipete)&lt;/a&gt; asked about this kind of feature in another OSS project. That project needed extra third-party dependencies for vector DB and embeddings. I wanted a simpler path for GitHub-native repos, so I built this around &lt;strong&gt;GitHub Models&lt;/strong&gt;, which are free for minimal usage.&lt;/p&gt;
&lt;h2&gt;What vector-triage is for&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;vector-triage&lt;/code&gt; is a GitHub Action that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;embeds issue/PR content with GitHub Models&lt;/li&gt;
&lt;li&gt;searches a local SQLite index (vector + FTS)&lt;/li&gt;
&lt;li&gt;posts one managed triage comment when related items are found&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It works for both Issues and Pull Requests.&lt;/p&gt;
&lt;h2&gt;Design goals&lt;/h2&gt;
&lt;p&gt;I kept the project focused on a few principles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;zero-config setup&lt;/li&gt;
&lt;li&gt;deterministic behavior&lt;/li&gt;
&lt;li&gt;low comment noise&lt;/li&gt;
&lt;li&gt;safe defaults for OSS repos&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Quick start&lt;/h2&gt;
&lt;p&gt;Setup is a single workflow file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: Triage

on:
  issues:
    types: [opened, edited]
  pull_request_target:
    types: [opened, synchronize]

permissions:
  contents: write
  issues: write
  pull-requests: write
  models: read

jobs:
  triage:
    runs-on: ubuntu-latest
    concurrency:
      group: triage-index
      cancel-in-progress: false
    steps:
      - uses: rizwankce/vector-triage@v1.0.2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is the full setup.&lt;/p&gt;
&lt;h2&gt;How it behaves&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;First run: it creates the index and does not post noise comments.&lt;/li&gt;
&lt;li&gt;Later runs: if matches exist, it posts or updates one managed triage comment.&lt;/li&gt;
&lt;li&gt;If no matches are found, it stays quiet.&lt;/li&gt;
&lt;li&gt;Recoverable failures are logged as warnings and exit non-fatally.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Built with Codex&lt;/h2&gt;
&lt;p&gt;I built most of this in a weekend with the help of Codex.&lt;/p&gt;
&lt;p&gt;The biggest win was speed of iteration: shaping the retrieval behavior, comment format, and safety model quickly, then hardening with tests and E2E workflow checks.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Repository: &lt;a href=&quot;https://github.com/rizwankce/vector-triage&quot;&gt;https://github.com/rizwankce/vector-triage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Action usage: &lt;code&gt;rizwankce/vector-triage@v1.0.2&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Kotlin and Android Native</title><link>https://rizwan.dev/blog/kotlin_and_android_native/</link><guid isPermaLink="true">https://rizwan.dev/blog/kotlin_and_android_native/</guid><description>Learning Kotlin and Android Native as an iOS developer</description><pubDate>Fri, 05 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Long time iOS developer, but never actually tried anything in Android world. Though I Worked on the cross-platform stack and tried out Android Studio before but never tried to learn Android Native Development. Always I hear about Android X, app compatibility and Gradle build from my colleagues. Even I went into a rabbit hole of upgrading React Native version and stuck because It&apos;s breaking the whole project due to the need for AndroidX support.&lt;/p&gt;
&lt;h2&gt;30DaysOfKotlin&lt;/h2&gt;
&lt;p&gt;Last May, when everyone was under lockdown and I saw a tweet about #30DaysOfKotlin. It&apos;s an online Bootcamp program which teaches Kotlin and Android Native development with a series of Offical tutorials (called Codelabs) and webinar from Google. I registered immediately. Then days passed I never got the chance to go through the code labs but watched the first live stream webinar and which got me some interest about Kotlin because of the similarity between Swift and Kotlin. Then I went through the code labs for &quot;Kotlin for Beginners&quot; and &quot;Android Kotlin fundamentals&quot;.&lt;/p&gt;
&lt;p&gt;As part of the program, I had to develop something on Kotlin and submit before June 6th 2020. Since all the modern apps are pretty much JSON viewer with better UX. I decided to build an app which consumes some sort of REST web service and build a minimal but functional app in the given time.&lt;/p&gt;
&lt;h2&gt;DevCast&lt;/h2&gt;
&lt;p&gt;DevCast - A developer-focused podcast app which is using Offical APIs from &lt;a href=&quot;https://dev.to&quot;&gt;dev.to&lt;/a&gt;. It has two screens, one to list the recent episodes and one for more details. The whole project itself open sourced and can be found &lt;a href=&quot;https://github.com/rizwankce/DevCast&quot;&gt;here&lt;/a&gt;. Project is released under MIT License so you can do anything with it.&lt;/p&gt;
&lt;p&gt;Here is the screenshots of the app. It has two screens, where first screen lists all the latest episodes from differnet podcast and second screen show the details of the selected episode.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;These are some links where I learnt most about Kotlin and Android Native development. Incase if you would like to start your journey like me.&lt;/p&gt;
&lt;h3&gt;Google Codelabs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.android.com/courses/kotlin-bootcamp/overview?utm_source=week1&amp;amp;utm_medium=email&amp;amp;utm_campaign=30DaysOfKotlin&amp;amp;utm_term=Basic&quot;&gt;Kotlin Bootcamp for beginners&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.android.com/courses/kotlin-android-fundamentals/overview?utm_source=week1&amp;amp;utm_medium=email&amp;amp;utm_campaign=30DaysOfKotlin&amp;amp;utm_term=Intermediate&quot;&gt;Android Kotlin Fundamentals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.android.com/courses/kotlin-android-advanced/overview?utm_source=week1&amp;amp;utm_medium=email&amp;amp;utm_campaign=30DaysOfKotlin&amp;amp;utm_term=Advanced&quot;&gt;Advanced Android in Kotlin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eventsonair.withgoogle.com/events/kotlin/resources&quot;&gt;All Kotlin resources from Google Codelabs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Others&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.raywenderlich.com/10529094-android-bootcamp&quot;&gt;Andriod Bootcamp by Raywenderlich&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=gDEGd174K_Q&amp;amp;list=WL&amp;amp;index=42&amp;amp;t=0s&quot;&gt;Andriod Crash Course for iOS developers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Learning SwiftUI with NetNewsWire</title><link>https://rizwan.dev/blog/learning-swiftui-with-netnewswire/</link><guid isPermaLink="true">https://rizwan.dev/blog/learning-swiftui-with-netnewswire/</guid><description>Learning SwiftUI by contributing to OSS</description><pubDate>Fri, 17 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;After the release of SwiftUI 2.0 on WWDC 2020, I got excited and want to learn more about it. What is better than learning a new tech by building a real-world app? Here it goes my learnings...&lt;/p&gt;
&lt;h2&gt;NetNewsWire&lt;/h2&gt;
&lt;p&gt;I got the chance to learn SwiftUI from an open-source project called &lt;a href=&quot;https://github.com/Ranchero-Software/NetNewsWire&quot;&gt;NetNewsWire&lt;/a&gt;. It&apos;s an RSS reader app for iOS, iPadOS and macOS. Build with UIKit/AppKit.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Shameless plug:- It&apos;s a cool app, you should check it out&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After the WWDC, contributors to the project had a zoom call and decided to hop on the SwiftUI train. It&apos;s not new for the team. They have already burnt the hand by investing some time last year when the SwiftUI 1.0 was released. For &lt;a href=&quot;https://inessential.com/2019/10/21/swiftui_is_still_the_future&quot;&gt;various reasons&lt;/a&gt; the team had to stop and rollback everything done on SwiftUI. Since there are lots of improvements on 2.0, we have decided to try it to build the existing multi-platform app with the all-new SwiftUI multiplatform.&lt;/p&gt;
&lt;p&gt;The whole team is ramping up with the latest cool beta things and I learnt a ton by implementing some small set of features on the Settings screen for iOS/iPadOS. One of my goals, when I started learning, is to share what I have learnt via blog posts. So I&apos;m gonna kick start a set of blog posts which explains specific new features from SwiftUI 2.0.&lt;/p&gt;
&lt;p&gt;The whole NetNewsWire community is great and I like to thank &lt;a href=&quot;https://twitter.com/brentsimmons&quot;&gt;Brent Simmons&lt;/a&gt; for creating this project and making it open-source, &lt;a href=&quot;https://twitter.com/vincode_io&quot;&gt;Maurice Parker&lt;/a&gt;,&lt;a href=&quot;https://twitter.com/stuarticus&quot;&gt;Stuart&lt;/a&gt; for helping me and correct my direction whenever I lost in track, and all other &lt;a href=&quot;https://github.com/Ranchero-Software/NetNewsWire/graphs/contributors&quot;&gt;contributors&lt;/a&gt; and users. If you would like to join, please check out our &lt;a href=&quot;https://ranchero.com/netnewswire/slack&quot;&gt;slack&lt;/a&gt; group. Everybody is welcome and encouraged to join.&lt;/p&gt;
</content:encoded></item><item><title>MVP for &quot;Untitled&quot; tvOS App</title><link>https://rizwan.dev/blog/mvp-for-untitled-tvos-app/</link><guid isPermaLink="true">https://rizwan.dev/blog/mvp-for-untitled-tvos-app/</guid><description>How I created an MVP for the new tvOS app</description><pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/blog/new-xcode-project&quot;&gt;Initial Idea and tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/plan-for-untitled-tvos-app&quot;&gt;Planning&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, the actual MVP.&lt;/p&gt;
&lt;h2&gt;Phase 1&lt;/h2&gt;
&lt;p&gt;I was busy with other personal stuff and didn’t get a chance to work on the project. Around the same time, Claude launched Claude Code Web, so I spun up a new project session to try it out. I don’t know which model sits behind it, but my guess is Sonnet 4.5.&lt;/p&gt;
&lt;p&gt;My first session prompt was simple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;i have milestones.md file.. please make sure that 1st milestone is complete or not. if not pls complete it
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It finished quickly—Milestone 1 was just “Project Setup &amp;amp; Configuration.”&lt;/p&gt;
&lt;p&gt;Then I spun up continuous sessions one after another. Each prompt followed the same pattern:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Please complete milestone 3 and 4 from milestones.md file
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The whole time, I wasn’t even in front of my computer. I kicked off sessions from both the iOS app and Claude Web.&lt;/p&gt;
&lt;p&gt;Afterward, I raised a PR for each session and merged them. It took roughly 30 minutes to complete 21 milestones. I didn’t review the code closely—everything came from Claude Web sessions—but I mainly needed the scaffolding in place so I could refine it later.&lt;/p&gt;
&lt;h2&gt;MVP&lt;/h2&gt;
&lt;p&gt;When I got back to my machine and pulled the latest changes, I was pleasantly surprised. All the files were there, and the project built just fine. I ran it on the tvOS simulator, and the login screen worked, aside from some focus quirks showing a weird white overlay. The app shipped with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tab view layout with four tabs&lt;/li&gt;
&lt;li&gt;Login screen&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nothing worked after login, though. The app couldn’t fetch data from the server and threw errors—no live channel lists, no folders.&lt;/p&gt;
</content:encoded></item><item><title>Network Activity logger for AFNetworking 3.0</title><link>https://rizwan.dev/blog/network-activity-logger-for-afnetworking-3-0/</link><guid isPermaLink="true">https://rizwan.dev/blog/network-activity-logger-for-afnetworking-3-0/</guid><description>Log all of your iOS project network activity to your console.</description><pubDate>Fri, 23 Dec 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Log all of your iOS project network activity to your console.&lt;/p&gt;
&lt;p&gt;If you are an iOS developer (or Any mobile developer), you might be in a situation where you want to debug the network requests and responses for your app and figure out how exactly the app dealing with web services.&lt;/p&gt;
&lt;p&gt;With the help of &lt;a href=&quot;https://github.com/AFNetworking/AFNetworkActivityLogger/tree/master&quot;&gt;AFNetworkActivityLogger&lt;/a&gt; we can log all the details about the network operations to xCode console.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Incase if you are using swift and &lt;a href=&quot;https://github.com/Alamofire/Alamofire&quot;&gt;Alamofire&lt;/a&gt;, check out &lt;a href=&quot;https://github.com/Alamofire/AlamofireNetworkActivityIndicator&quot;&gt;AlamofireNetworkActivityIndicator&lt;/a&gt;. Everything below is covered for Obj-C&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;Install &lt;a href=&quot;http://cocoapods.org/&quot;&gt;Cocoapods&lt;/a&gt; if you don’t have already with the following command&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gem install cocoapods
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Note: CocoaPods version 0.39.0+ is required.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Podfile&lt;/h3&gt;
&lt;p&gt;To integrate &lt;a href=&quot;https://github.com/AFNetworking/AFNetworkActivityLogger/tree/master&quot;&gt;AFNetworkActivityLogger&lt;/a&gt; into your xCode project using Cocoapods, specify it in your Podfile&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source &apos;https://github.com/CocoaPods/Specs.git&apos;
platform :ios, &apos;8.0&apos;

target &apos;TargetName&apos; do
pod &apos;AFNetworkActivityLogger&apos;
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Above pod file integration will take the latest version from Github repo. Unfortunately the latest version only works with AFNetworking v2.0. Incase if you want to use it for AFNetworking v3.0 edit the pod file as below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pod ‘AFNetworkActivityLogger’, :git =&amp;gt; ‘https://github.com/AFNetworking/AFNetworkActivityLogger.git&apos;, :branch =&amp;gt; ‘3_0_0’
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Above line will take the latest version from Git branch 3_0_0 which can be used with AFNetworking v3.0&lt;/p&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;h4&gt;AFNetworking 3.0&lt;/h4&gt;
&lt;p&gt;Add the following code to &lt;code&gt;AppDelegate.m -application:didFinishLaunchingWithOptions:&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[[AFNetworkActivityLogger sharedLogger] startLogging];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ta-Da! now all your network activities will be logged in your Xcode console. By default log level is set as INFO, but incase if you want to see more logs follow the below lines.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AFNetworkActivityConsoleLogger *logger = [AFNetworkActivityLogger sharedLogger].loggers.anyObject;
logger.level = AFLoggerLevelDebug;
[[AFNetworkActivityLogger sharedLogger] startLogging];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Supported log levels are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AFLoggerLevelOff&lt;/code&gt;: Do not log requests or responses.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AFLoggerLevelDebug&lt;/code&gt; :Logs HTTP method, URL, header fields, &amp;amp; request body for requests, and status code, URL, header fields, response string, &amp;amp; elapsed time for responses.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AFLoggerLevelInfo&lt;/code&gt;: Logs HTTP method &amp;amp; URL for requests, and status code, URL, &amp;amp; elapsed time for responses.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AFLoggerLevelError&lt;/code&gt;: Logs HTTP method &amp;amp; URL for requests, and status code, URL, &amp;amp; elapsed time for responses, but only for failed requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;AFNetworking 2.0&lt;/h4&gt;
&lt;p&gt;If you are using version 2.0 follow the below code.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[[AFNetworkActivityLogger sharedLogger] startLogging];
[[AFNetworkActivityLogger sharedLogger] setLevel:AFLoggerLevelError];
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>New Xcode Project - &quot;Untitled&quot; tvOS app</title><link>https://rizwan.dev/blog/new-xcode-project/</link><guid isPermaLink="true">https://rizwan.dev/blog/new-xcode-project/</guid><description>Sharing my experience of developing a tvOS app from scratch with help from LLMs</description><pubDate>Sun, 02 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Just like many Apple platform developers, I finally did the thing and clicked:&lt;/p&gt;
&lt;p&gt;Xcode → New Project&lt;/p&gt;
&lt;p&gt;To be honest, this isn’t the first time I’ve done it—I have plenty of private GitHub repos to prove it. But this time feels different. I have access to the tools that let me get that first push out quickly and even ship most of the app while I’m eating breakfast, thanks to LLM agents.&lt;/p&gt;
&lt;p&gt;I’ve been leaning on both CLI and cloud agents at work lately, so I wanted to try something new for a personal project.&lt;/p&gt;
&lt;p&gt;The plan: build an app from scratch with LLM help. Not AI slop or vibe coding, but a human-in-the-loop setup where I decide what hits GitHub instead of blindly accepting every suggestion a model produces.&lt;/p&gt;
&lt;h2&gt;Idea&lt;/h2&gt;
&lt;p&gt;The idea sparked a while ago: a live IPTV app. I live outside my home country and rely on Apple TV all the time, so I’ve wanted a solid IPTV experience tailored for me.&lt;/p&gt;
&lt;p&gt;I’ve never developed a tvOS app before, so this will be an experiment.&lt;/p&gt;
&lt;p&gt;PS: I’m sharing this after spending about two weeks building the app on and off.&lt;/p&gt;
&lt;h2&gt;Tool Belt&lt;/h2&gt;
&lt;p&gt;Things are moving fast in the LLM world, so my tool belt keeps changing with it. So far I’ve used the tools below and I’ll share more in upcoming blog posts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Claude Code&lt;/li&gt;
&lt;li&gt;Codex CLI&lt;/li&gt;
&lt;li&gt;Codex Web&lt;/li&gt;
&lt;li&gt;Claude Code Web&lt;/li&gt;
&lt;li&gt;Amp CLI&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Plan for &quot;Untitled&quot; tvOS App</title><link>https://rizwan.dev/blog/plan-for-untitled-tvos-app/</link><guid isPermaLink="true">https://rizwan.dev/blog/plan-for-untitled-tvos-app/</guid><description>How I planned the new tvOS app</description><pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;/blog/new-xcode-project&quot;&gt;I have an idea and prepped some tools for it&lt;/a&gt;. Now it’s time for planning. After creating a blank Xcode project, setting up a GitHub repo, and pushing the first commit, I avoided jumping straight into coding or asking an LLM to “build me this app.” Instead, I spent time planning.&lt;/p&gt;
&lt;h2&gt;Planning&lt;/h2&gt;
&lt;p&gt;I used Claude Web with Sonnet 4.5.&lt;/p&gt;
&lt;p&gt;I asked Claude to create a &lt;code&gt;spec.md&lt;/code&gt; file with milestones and phases for my tvOS project. This is the exact prompt I used:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create a spec.md file for my personal app (not gonna release it to stores)

idea is to have tv OS app which deos followings.
* allow me to enter xstream code (username, password and url)
* fetch all the deatils and load into the UI (use swift ui and apple provided apis)
* when clicking the item from the list use apple provided api to stream content.

before creating the md file. ask me questions and then make it
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had to tell Claude that I’m not going to release it to stores because safety checks kicked in and refused to help with Xstream codes logins. Yes, I had misspelled a few things in the prompt.&lt;/p&gt;
&lt;p&gt;After I answered some of Claude’s questions, it generated a &lt;code&gt;spec.md&lt;/code&gt; file with two phases and lots of detail. Sharing just the phases here:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Phase 1 - Core Functionality (MVP)

 Authentication with credential storage
 Fetch and display Live TV channels
 Fetch and display Movies
 Fetch and display TV Series with episode navigation
 Search across all content
 Video playback with full controls
 Settings screen with account management
 Metadata display (when available)

Phase 2 - Enhanced Experience (Future)

 Categories/Genre filtering
 Favorites list
 Recently watched
 Continue watching
 EPG support for Live TV
 Multi-account support
 Parental controls (if needed later)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I copied this &lt;code&gt;spec.md&lt;/code&gt; into my GitHub repo, created a new &lt;code&gt;docs&lt;/code&gt; folder, and saved it there.&lt;/p&gt;
&lt;h2&gt;Milestones&lt;/h2&gt;
&lt;p&gt;Back on my machine, I opened the repo with &lt;code&gt;spec.md&lt;/code&gt; and asked Claude Code with Sonnet 4.5 to create &lt;code&gt;docs/milestones.md&lt;/code&gt; by reading &lt;code&gt;docs/spec.md&lt;/code&gt;. After running for a bit, it produced 21 milestones for Phase 1 and 5 for Phase 2. It also added sections for “Suggested folder structure,” “Development Tips for LLM Implementation,” and “Common Pitfalls to Avoid.”&lt;/p&gt;
&lt;p&gt;I really liked the plan and only edited a few parts. &lt;code&gt;milestones.md&lt;/code&gt; is fairly long, so I’m sharing just one milestone here.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;## Milestone 15: Settings Screen

**Objective:** Create settings/account management interface.

**File to Create:** `Views/Settings/SettingsView.swift`

**Implementation Requirements:**

1. **Server Information Section:**
   - Display server URL (read-only)
   - Display username (read-only)
   - Display connection status

2. **Account Management Section:**
   - &quot;Change Account&quot; button
   - Confirms action with alert
   - Clears credentials from Keychain
   - Returns to login screen

3. **App Information Section:**
   - App version number
   - About text

4. **Cache Management Section:**
   - &quot;Clear Cache&quot; button
   - Clears URLCache
   - Shows confirmation

**Success Criteria:**
- [x] Server info displays correctly
- [x] Change Account button works
- [x] Logout clears credentials
- [x] Returns to login after logout
- [x] App version displays
- [x] Clear cache works
- [x] Proper alert confirmations
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next comes my favorite part—building the actual app from &lt;code&gt;spec.md&lt;/code&gt; and &lt;code&gt;milestones.md&lt;/code&gt;. What do you think I did, and how do you think the first version turned out?&lt;/p&gt;
</content:encoded></item><item><title>qmd for faster Obsidian search</title><link>https://rizwan.dev/blog/qmd-for-faster-obsidian-search/</link><guid isPermaLink="true">https://rizwan.dev/blog/qmd-for-faster-obsidian-search/</guid><description>How I made my markdown search fast with qmd in my Obsidian vault</description><pubDate>Tue, 27 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I use Claude Code and Codex to search my Obsidian vault. The problem is those agents often load big markdown files into context and burn tokens. I keep a lot of notes in Obsidian, and the vault is now big enough that search started to feel slow. I wanted something fast, local, and still markdown-first.&lt;/p&gt;
&lt;p&gt;I found &lt;strong&gt;qmd&lt;/strong&gt; (Quick Markdown Search). It’s a CLI search engine for markdown files. It mixes classic full-text search (BM25), vector search, and LLM re-ranking, and it runs locally. That mix is exactly what I wanted: fast keyword search when I know the words, and semantic search when I don’t.&lt;/p&gt;
&lt;h2&gt;What I set up&lt;/h2&gt;
&lt;p&gt;I pointed qmd at my Obsidian vault and created a collection for it. Then I added a short context string so I can filter results later. After that I generated embeddings once, and now I just query.&lt;/p&gt;
&lt;p&gt;Here’s the flow I’m using:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# install
bun install -g https://github.com/tobi/qmd

# add my Obsidian vault
qmd collection add ~/Obsidian/Vault --name obsidian
qmd context add qmd://obsidian &quot;My Obsidian notes&quot;

# build embeddings once
qmd embed

# fast keyword search
qmd search &quot;work log okr&quot;

# semantic search
qmd vsearch &quot;what did i do on Jan first week?&quot;

# best quality (hybrid + reranking)
qmd query &quot;how I handle pagination in iOS app&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Why it feels faster&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;It’s a CLI, so I can keep my flow in the terminal.&lt;/li&gt;
&lt;li&gt;Keyword search is instant for exact matches.&lt;/li&gt;
&lt;li&gt;Semantic search gives me the file I &lt;em&gt;meant&lt;/em&gt; even if I wrote different words.&lt;/li&gt;
&lt;li&gt;Everything stays local, which is important for my notes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How I use it with Obsidian&lt;/h2&gt;
&lt;p&gt;I still capture and edit in Obsidian. When I’m using Claude Code or Codex, I let the agent call qmd so it can search my vault fast without burning a bunch of context. For manual lookups, I jump to the terminal, run qmd, and then open the file in Obsidian.&lt;/p&gt;
&lt;p&gt;If you have a big markdown vault, give qmd a try. It feels like a power-up for Obsidian.&lt;/p&gt;
</content:encoded></item><item><title>Second Chance for &quot;Untitled&quot; tvOS App</title><link>https://rizwan.dev/blog/second-chance-for-untitled-tvos-app/</link><guid isPermaLink="true">https://rizwan.dev/blog/second-chance-for-untitled-tvos-app/</guid><description>How I gave the new tvOS app a second chance</description><pubDate>Fri, 07 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;So far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/blog/new-xcode-project&quot;&gt;Initial Idea and tools&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/plan-for-untitled-tvos-app&quot;&gt;Planning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/mvp-for-untitled-tvos-app&quot;&gt;MVP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/cleanup-for-untitled-tvos-app&quot;&gt;Cleanup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/home-work-for-untitled-tvos-app&quot;&gt;Homework&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Second Chance&lt;/h2&gt;
&lt;p&gt;After I created an MVP with LLM help, I wasn’t thrilled with the outcome. But now I had more knowledge about tvOS apps and a handful of solid inspirations.&lt;/p&gt;
&lt;p&gt;So I asked Codex CLI to make the app better. I pasted the WWDC session link and the HIG link into the prompt and told it to go to town.&lt;/p&gt;
&lt;p&gt;I wasn’t kidding—I was that literal. Turns out LLMs can’t browse Apple docs like we do. After a few rounds of trial and error, I found the reason and a workaround.&lt;/p&gt;
&lt;p&gt;I discovered &lt;a href=&quot;https://sosumi.ai/&quot;&gt;Sosumi&lt;/a&gt;, a handy tool that fetches Apple docs in an LLM-friendly format. I didn’t want to mess with an MCP server, so I dropped this note into my &lt;code&gt;AGENTS.md&lt;/code&gt;/&lt;code&gt;Claude.md&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Apple Developer Documentation Tip
If the developer.apple.com documentation portal isn’t returning readable content from automated tooling, replace the host with sosumi.ai to get an AI-friendly copy:

Original: https://developer.apple.com/documentation/swift/array
AI-readable: https://sosumi.ai/documentation/swift/array
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now Codex could see what I see on Apple’s site. It fetched the docs and listed a set of improvements. I told it, “All clear, go ahead with the plan.”&lt;/p&gt;
&lt;p&gt;Instead of fixing issues, it introduced more UI problems thanks to SwiftUI navigation quirks.&lt;/p&gt;
&lt;p&gt;I tried the same request with Claude and Amp, but neither produced useful results.&lt;/p&gt;
&lt;p&gt;So I restructured part of the app myself. I edited code in Xcode like it was the pre-LLM era—copying patterns from Apple’s sample project—and built a better card layout and navigation stack.&lt;/p&gt;
&lt;p&gt;For once, I was happy with the result. Then I asked Codex to mirror the first tab’s structure across the remaining tabs and navigation flows. The UI finally felt decent: fewer focus glitches, better navigation.&lt;/p&gt;
&lt;p&gt;I’d rate it 2.5 stars at that point.&lt;/p&gt;
&lt;p&gt;But the default &lt;code&gt;AVPlayer&lt;/code&gt; still didn’t work. After lots of conversations with ChatGPT, I learned it wasn’t an easy fix. My inspiration app had a setting to switch players and suggested VLC or KSPlayer.&lt;/p&gt;
&lt;p&gt;So I asked Codex CLI to create a new &lt;code&gt;VideoPlayerView&lt;/code&gt; based on VLC’s sample project. It one-shotted the feature, and I fixed the media streaming issue on top of that.&lt;/p&gt;
&lt;p&gt;The app finally bumped up to a solid three stars.&lt;/p&gt;
</content:encoded></item><item><title>Secure API Key Management with 1Password CLI</title><link>https://rizwan.dev/blog/secure-api-keys-with-1password-cli/</link><guid isPermaLink="true">https://rizwan.dev/blog/secure-api-keys-with-1password-cli/</guid><description>Stop storing API keys in plaintext shell configuration files and use 1Password CLI for secure access</description><pubDate>Thu, 14 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We&apos;ve all been there - storing API keys directly in our &lt;code&gt;.zshrc&lt;/code&gt;, &lt;code&gt;.bashrc&lt;/code&gt;, or &lt;code&gt;.bash_profile&lt;/code&gt; files for easy access. While convenient, this approach has serious security implications. Your API keys are stored in plaintext, visible to anyone with access to your shell configuration, and at risk of being accidentally committed to version control.&lt;/p&gt;
&lt;h2&gt;The Problem with Plaintext Storage&lt;/h2&gt;
&lt;p&gt;The traditional approach looks like this in your &lt;code&gt;.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export GEMINI_API_KEY=&quot;your-secret-api-key-here&quot;
export OPENAI_API_KEY=&quot;another-secret-key&quot;
export AWS_ACCESS_KEY_ID=&quot;yet-another-key&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates several security risks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Plaintext exposure&lt;/strong&gt;: Keys are readable by any process or user with access to your shell files&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Version control leaks&lt;/strong&gt;: Easy to accidentally commit these files with sensitive data&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;System-wide access&lt;/strong&gt;: Any application can read these environment variables&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No audit trail&lt;/strong&gt;: No way to track when or how these keys are accessed&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Enter 1Password CLI&lt;/h2&gt;
&lt;p&gt;1Password CLI (&lt;code&gt;op&lt;/code&gt;) provides a secure way to store and retrieve sensitive information like API keys. Instead of storing keys in plaintext, we can store them securely in 1Password and retrieve them dynamically when needed.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;First, install the 1Password CLI if you haven&apos;t already:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# macOS with Homebrew
brew install --cask 1password-cli

# Or download from https://1password.com/downloads/command-line/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, authenticate with your 1Password account:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;op account add
op signin
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Storing API Keys in 1Password&lt;/h2&gt;
&lt;p&gt;Create a new item in 1Password for your API key. You can do this through the 1Password app or CLI:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Create a new item with your API key
op item create --category=&quot;API Credential&quot; \
  --title=&quot;Gemini CLI&quot; \
  --field=&quot;password=your-actual-api-key-here&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Dynamic Key Retrieval&lt;/h2&gt;
&lt;p&gt;Now, instead of hardcoding the key in your &lt;code&gt;.zshrc&lt;/code&gt;, use the 1Password CLI to retrieve it dynamically:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# In your .zshrc file
export GEMINI_API_KEY=$(op item get &apos;Gemini CLI&apos; --fields password --reveal)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;--reveal&lt;/code&gt; flag ensures that the password field is displayed in plaintext for use as an environment variable.&lt;/p&gt;
&lt;h2&gt;Complete Shell Configuration&lt;/h2&gt;
&lt;p&gt;Here&apos;s how your &lt;code&gt;.zshrc&lt;/code&gt; might look with multiple API keys managed securely:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Secure API key management with 1Password CLI
export GEMINI_API_KEY=$(op item get &apos;Gemini CLI&apos; --fields password --reveal)
export OPENAI_API_KEY=$(op item get &apos;OpenAI API&apos; --fields password --reveal)
export AWS_ACCESS_KEY_ID=$(op item get &apos;AWS Credentials&apos; --fields username --reveal)
export AWS_SECRET_ACCESS_KEY=$(op item get &apos;AWS Credentials&apos; --fields password --reveal)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Benefits&lt;/h2&gt;
&lt;p&gt;This approach provides several advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Encrypted storage&lt;/strong&gt;: Keys are stored encrypted in 1Password&apos;s secure vault&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit trail&lt;/strong&gt;: 1Password tracks access to your items&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Centralized management&lt;/strong&gt;: Manage all credentials from one secure location&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy rotation&lt;/strong&gt;: Update keys in 1Password without touching shell configuration&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Safe version control&lt;/strong&gt;: Your &lt;code&gt;.zshrc&lt;/code&gt; can be safely committed without exposing secrets&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Performance Considerations&lt;/h2&gt;
&lt;p&gt;You might wonder about the performance impact of calling &lt;code&gt;op&lt;/code&gt; for each key. In practice, the CLI is quite fast, but if you&apos;re concerned about startup time, you could implement caching:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Cache keys for the session
if [[ -z &quot;$GEMINI_API_KEY&quot; ]]; then
    export GEMINI_API_KEY=$(op item get &apos;Gemini CLI&apos; --fields password --reveal)
fi
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Alternative: Session Management&lt;/h2&gt;
&lt;p&gt;For even better security, you can use 1Password&apos;s session management to avoid keeping long-running authenticated sessions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Sign in and export session token
export OP_SESSION_my_account=$(op signin my_account --raw)

# Use the session token for subsequent calls
export GEMINI_API_KEY=$(op item get &apos;Gemini CLI&apos; --fields password --reveal --session=&quot;$OP_SESSION_my_account&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;By leveraging 1Password CLI, we can maintain the convenience of environment variables while significantly improving our security posture. Your API keys remain encrypted at rest, access is audited, and you eliminate the risk of accidentally exposing secrets in version control.&lt;/p&gt;
&lt;p&gt;The small performance overhead is a worthwhile trade-off for the security benefits, and your future self will thank you for taking the extra step to protect your valuable API credentials.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.1password.com/docs/cli/&quot;&gt;1Password CLI documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://1password.com/downloads/command-line/&quot;&gt;1Password CLI installation guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>📸 Swift Camera — Part 1</title><link>https://rizwan.dev/blog/swift-camera-part-1/</link><guid isPermaLink="true">https://rizwan.dev/blog/swift-camera-part-1/</guid><description>Create a custom camera view using AVFoundation</description><pubDate>Fri, 16 Jun 2017 13:28:59 GMT</pubDate><content:encoded>&lt;p&gt;Create a custom camera view using AVFoundation&lt;/p&gt;
&lt;p&gt;
&lt;em&gt;Photo credit: &lt;a href=&quot;https://unsplash.com/search/iphone-camera?photo=eX5CeferlDo&quot;&gt;unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;iOS 11 brings lots of new cool features like Machine Learning and Augmented Reality. So you might want to test those features or create awesome apps. But if you notice that some of them need a custom camera and accessing camera frames. iOS have lots of API’s for us to access device camera, capture image and process it. &lt;code&gt;AVFoundation&lt;/code&gt; is the framework you should be looking at. Since this framework is huge and there is lots of ways to achieve the desired features I decided to write set of blog posts about the following.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create custom camera view&lt;/li&gt;
&lt;li&gt;Take picture from custom camera&lt;/li&gt;
&lt;li&gt;Record Video using&lt;/li&gt;
&lt;li&gt;Detect Face and Scan QR code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;(If you want some specific things please do ask me in the comments. I will try to write/learn about it)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;To all those nerds who wants to skip the blog post and see the code in action. I got you covered. Here is the Github Repo. Keep an eye on this repo because I will be adding all features in the same app and if you want to improve the code quality PR/Issues are welcome.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rizwankce/Camera/&quot;&gt;rizwankce/Camera&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For others,&lt;/p&gt;
&lt;h1&gt;Let’s create a custom camera&lt;/h1&gt;
&lt;h2&gt;Step 1:&lt;/h2&gt;
&lt;p&gt;Create new project in xCode by selecting File → New → Project → Single View Application (under iOS tab)&lt;/p&gt;
&lt;p&gt;Give it a nice name and select swift as Language. Click Next → Create and save the project file. if everything goes well, you could see a project like below&lt;/p&gt;
&lt;p&gt;&amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img src=&quot;https://cdn-images-1.medium.com/max/800/1*hoxX5t0dPha_Y4XzMFucCQ.png&quot;&amp;gt;
&amp;lt;br/&amp;gt;
Project structure
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;h2&gt;Step 2:&lt;/h2&gt;
&lt;p&gt;Select Main.storyboard ,drag &lt;code&gt;UIView&lt;/code&gt; to your view controller and set top , bottom , leading, trailing to 0. This view will serve as “view finder” or “preview view” for your camera.&lt;/p&gt;
&lt;p&gt;&amp;lt;p&amp;gt;
&amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img src=&quot;https://cdn-images-1.medium.com/max/800/1&lt;em&gt;mvbETYE-5CJa6t_ZlxjOtg.png&quot;&amp;gt;
&amp;lt;br/&amp;gt;
Main.storyboard ViewController structure
&amp;lt;/p&amp;gt;
&amp;lt;br/&amp;gt;
&amp;lt;br/&amp;gt;
&amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img src=&quot;https://cdn-images-1.medium.com/max/800/1&lt;/em&gt;wUAgn8DwN0rXZ65Tcak4Xw.png&quot;&amp;gt;
&amp;lt;br/&amp;gt;
Preview View Constraints
&amp;lt;/p&amp;gt;
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;Control + drag our preview view to &lt;code&gt;ViewController.swift&lt;/code&gt; and create an IBOutlet named &lt;code&gt;previewView&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Step 3&lt;/h2&gt;
&lt;p&gt;At the top of your ViewController file, import &lt;code&gt;AVFoundation&lt;/code&gt; framework.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import AVFoundation
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Create the below instance variables so that we can access anywhere in the ViewController file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var captureSession: AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;here &lt;code&gt;captureSession&lt;/code&gt; helps us to transfer data between one or more device inputs like camera or microphone and view &lt;code&gt;videoPreviewLayer&lt;/code&gt; helps to render the camera view finder in our ViewController&lt;/p&gt;
&lt;h2&gt;Step 4&lt;/h2&gt;
&lt;p&gt;You will need a capture device, device input and preview layer to setup and start the camera. To make it simpler we will try to do everything related to camera setup in &lt;code&gt;viewDidLoad&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Get an instance of the AVCaptureDevice class to initialise a device object and provide the video as the media type parameter. You can even select which capture device you can choose like a dual camera or standard camera but for this post, we will just get default device (rear camera).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let captureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Get an instance of the AVCaptureDeviceInput class using the previous device object. This will serve as a middle man to attach our input device to capture device. Here there is a chance that input device might not be available, so wrap it inside a do…catch to handle errors&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;do {
let input = try AVCaptureDeviceInput(device: captureDevice)
} catch {
print(error)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Initialise our &lt;code&gt;captureSession&lt;/code&gt; object and add the input device to our session&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;captureSession = AVCaptureSession()
captureSession?.addInput(input)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next configure our preview View so that we can see the live preview of camera.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create an &lt;code&gt;AVCaptureVideoPreviewLayer&lt;/code&gt; from our session&lt;/li&gt;
&lt;li&gt;Configure the layer to resize while maintaining original aspect&lt;/li&gt;
&lt;li&gt;Set preview layer frame to our ViewController view bounds&lt;/li&gt;
&lt;li&gt;Add the preview layer as sublayer to our &lt;code&gt;previewView&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds
previewView.layer.addSublayer(videoPreviewLayer!)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, start our &lt;code&gt;captureSession&lt;/code&gt; to start video capture&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;captureSession?.startRunning()
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 5&lt;/h2&gt;
&lt;p&gt;Since the simulator does NOT have a camera to check our code. you might need a real device to run the code. So connect your phone and hit run, you should be see a live camera.&lt;/p&gt;
&lt;p&gt;Meh! The app will crash because we forgot to add one last thing. That is “Privacy — Camera Usage Description” in our plist. Add this to Info.plist file and give some nice message because that is what user will see when app is requesting for camera permission.&lt;/p&gt;
&lt;p&gt;Now if you hit run you should be seeing the magic!&lt;/p&gt;
&lt;p&gt;There are some things which I haven’t included here like checking user permission, adding capture output. But if you got the time you can read about all of them in Apple Documentation here.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/PhotoCaptureGuide/&quot;&gt;Photo Capture Programming Guide
&lt;/a&gt;&lt;a href=&quot;https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/PhotoCaptureGuide/&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On next part, I will write about having &lt;code&gt;AVCapturePhotoOutput&lt;/code&gt; and use its delegate method to take a picture and save it to iPhone Camera Roll. Follow me here or on &lt;a href=&quot;https://twitter.com/rizzu26&quot;&gt;twitter&lt;/a&gt; to get updates. If you like this post share and comments&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
</content:encoded></item><item><title>📸 Swift Camera — Part 2</title><link>https://rizwan.dev/blog/swift-camera-part-2/</link><guid isPermaLink="true">https://rizwan.dev/blog/swift-camera-part-2/</guid><description>Use custom camera view to take pictures and save it to photos album</description><pubDate>Tue, 20 Jun 2017 12:01:15 GMT</pubDate><content:encoded>&lt;p&gt;Use custom camera view to take pictures and save it to photos album&lt;/p&gt;
&lt;p&gt;
Photo credit: &lt;a href=&quot;https://unsplash.com/search/iphone-take?photo=PnyS8tiGdbo&quot;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Incase this is your first time here, you can go ahead read the part 1. Believe me — it would only take you 4 mins to read it. I explained in detail about “How to create custom camera view using &lt;code&gt;AVFoundation&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/2017-06-16_Swift-Camera-Part-1/&quot;&gt;📸 Swift Camera — Part 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As usual, to all those nerds who wants to see the code in action and skip this post. Here is your Github repo link. Everything I am gonna discuss is already in that repo master branch.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rizwankce/Camera&quot;&gt;rizwankce/Camera&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For others who want to learn before diving into code,&lt;/p&gt;
&lt;h1&gt;&lt;strong&gt;Let’s take pictures with our custom camera&lt;/strong&gt;&lt;/h1&gt;
&lt;p&gt;Before you dive in, you need the code from Part 1. You can find it in this &lt;a href=&quot;https://github.com/rizwankce/Camera/tree/feature/custom-camera&quot;&gt;Github branch&lt;/a&gt;. Below steps will use the part 1 view controller and add a feature to taking pictures.&lt;/p&gt;
&lt;h2&gt;Step 1&lt;/h2&gt;
&lt;p&gt;Select Main.storyboard, drag &lt;code&gt;UIButton&lt;/code&gt; to our view controller, give it a nice title like “Snap a photo” and set constraints. Make sure button is on top of the preview view like below.&lt;/p&gt;
&lt;p&gt;&amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img src=&quot;https://cdn-images-1.medium.com/max/800/1*ul8unoF8WI3gkcVJotHoMQ.png&quot;&amp;gt;
&amp;lt;br/&amp;gt;
Main.storyboard ViewController structure
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;Set below constraints for our button, So that it will be in correct position.&lt;/p&gt;
&lt;p&gt;&amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img src=&quot;https://cdn-images-1.medium.com/max/800/1*rHzGPk6R8CNpy26m8VVzaw.png&quot;&amp;gt;
&amp;lt;br/&amp;gt;
Capture button constraints
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;Here, I’m adding leading, trailing and bottom constraints to our capture button. So that our button will be placed the bottom of the screen.&lt;/p&gt;
&lt;p&gt;Control + drag our button to &lt;code&gt;ViewController.swift&lt;/code&gt; and create an IBOutlet action like below. This function will be called when we tap the capture button.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@IBAction func onTapTakePhoto(_ sender: Any) {

}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 2&lt;/h2&gt;
&lt;p&gt;Create below instance variable in our ViewController.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var capturePhotoOutput: AVCapturePhotoOutput?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;capturePhotoOutput&lt;/code&gt; helps us to snap a photo from our capture session. It has also provided lots of modern interface for capturing still photo, Live photo, wide-gamut colour including the variety of formats. To simplify this post, we are going to take still image from our capturePhotoOutput.&lt;/p&gt;
&lt;h2&gt;Step 3&lt;/h2&gt;
&lt;p&gt;Create an instance of AVCapturePhotoOutput in our &lt;code&gt;viewDidLoad&lt;/code&gt; method inside do…catch loop and add it to our session as output like below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Get an instance of ACCapturePhotoOutput class
capturePhotoOutput = AVCapturePhotoOutput()
capturePhotoOutput?.isHighResolutionCaptureEnabled = true

// Set the output on the capture session
captureSession?.addOutput(capturePhotoOutput)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.apple.com/documentation/avfoundation/avcapturephotooutput&quot;&gt;AVCapturePhotoOutput&lt;/a&gt; has lots of properties to control how the capture workflow should work. But we are just enabling High resolution capture here.&lt;/p&gt;
&lt;h2&gt;Step 4&lt;/h2&gt;
&lt;p&gt;Create and configure Photo Settings in our &lt;code&gt;onTapTakePhoto&lt;/code&gt; function like below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Make sure capturePhotoOutput is valid
guard let capturePhotoOutput = self.capturePhotoOutput else { return }

// Get an instance of AVCapturePhotoSettings class
let photoSettings = AVCapturePhotoSettings()

// Set photo settings for our need
photoSettings.isAutoStillImageStabilizationEnabled = true
photoSettings.isHighResolutionPhotoEnabled = true
photoSettings.flashMode = .auto

// Call capturePhoto method by passing our photo settings and a
// delegate implementing AVCapturePhotoCaptureDelegate
capturePhotoOutput.capturePhoto(with: photoSettings, delegate: self)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, create an instance of &lt;code&gt;AVCapturePhotoSettings&lt;/code&gt; and set some photo settings like image stabilisation, high resolution photo and auto flash mode.&lt;/p&gt;
&lt;p&gt;Then, call &lt;code&gt;capturePhoto&lt;/code&gt; method from &lt;code&gt;capturePhotoOutput&lt;/code&gt; instance and pass our photo settings with self as delegate. This will allow us to take a still image from our custom camera.&lt;/p&gt;
&lt;h2&gt;Step 5&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.apple.com/documentation/avfoundation/avcapturephotocapturedelegate&quot;&gt;AVCapturePhotoCaptureDelegate&lt;/a&gt; methods gives us lots of control over a photo capture output. To simply our needs we will use this below method to get the captured image.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;photoOutput(_:didFinishProcessingPhoto:previewPhoto:resolvedSettings:bracketSettings:error:)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Create an extension for ViewController and extends from AVCapturePhotoCaptureDelegate like below&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension ViewController : AVCapturePhotoCaptureDelegate {
    func capture( captureOutput: AVCapturePhotoOutput,
                  didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?,
                  previewPhotoSampleBuffer: CMSampleBuffer?,
                  resolvedSettings: AVCaptureResolvedPhotoSettings,
                  bracketSettings: AVCaptureBracketedStillImageSettings?,
                  error: Error?) {
        // get captured image
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;didFinishProcessingPhotoSampleBuffer&lt;/code&gt; gives us the captured photo buffer ,preview buffer and resolved photo settings.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Make sure we get some photo sample buffer
guard error == nil,
    let photoSampleBuffer = photoSampleBuffer else {
        print(&quot;Error capturing photo: \(String(describing: error))&quot;)
        return
}

// Convert photo same buffer to a jpeg image data by using // AVCapturePhotoOutput
guard let imageData =
    AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: photoSampleBuffer, previewPhotoSampleBuffer: previewPhotoSampleBuffer) else {
        return
}

// Initialise a UIImage with our image data
let capturedImage = UIImage.init(data: imageData , scale: 1.0)
if let image = capturedImage {
    // Save our captured image to photos album
    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we are getting only photoSampleBuffer. Need to convert it to a jpeg image data. To do that we can use &lt;code&gt;jpegPhotoDataRepresentation&lt;/code&gt; method from &lt;code&gt;AVCapturePhotoOutput&lt;/code&gt; which will take photo sample buffer and preview photo same buffer as input and gives us JPEG image data.&lt;/p&gt;
&lt;p&gt;To save our captured image to photos album, again we need to convert our image data to &lt;code&gt;UIImage&lt;/code&gt; . So initialise an &lt;code&gt;UIImage&lt;/code&gt; by passing our jpeg Image Data and scale of 1.0.&lt;/p&gt;
&lt;p&gt;Finally, use &lt;code&gt;UIImageWriteToSavedPhotoAlbum&lt;/code&gt; method and pass our image to be saved in photo album.&lt;/p&gt;
&lt;h2&gt;Step 6&lt;/h2&gt;
&lt;p&gt;if you run the app in a real device, it might crash now by giving below message.&lt;/p&gt;
&lt;p&gt;
Console message on Crash&lt;/p&gt;
&lt;p&gt;You guessed it correct! we need to add “Privacy — Photo Library Usage Description” in our plist. Since we are saving the image to photo library.#&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EDIT from future&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To prevent crashes on iOS 11, “Privacy — Photo Library Additions Usage Description” in info.plist with a usage description (string) should be added.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://medium.com/u/3e146f6d92fa&quot;&gt;Ingrid Silapan&lt;/a&gt; for pointing this out!&lt;/p&gt;
&lt;p&gt;Now if you hit run and click the button at the bottom. You will hear a sound indicating the camera is taking picture. Go ahead and check in your photos app to verify the captured image is actually saved or not.&lt;/p&gt;
&lt;p&gt;There are lots of things I haven’t included here. So I am trying to list as much as possible below.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AVCapturePhotoOutput&lt;/code&gt; class supports only from iOS 10 and above. So if you are targeting any iOS below then 10. You should be checking out &lt;code&gt;[AVCaptureStillImageOutput](https://developer.apple.com/documentation/avfoundation/avcapturestillimageoutput)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[didFinishProcessingPhotoSampleBuffer](https://developer.apple.com/documentation/avfoundation/avcapturephotocapturedelegate/2873949-photooutput)&lt;/code&gt; is deprecated from iOS 11. But you can use &lt;code&gt;didFinishProcessingPhoto&lt;/code&gt; method from &lt;code&gt;AVCapturePhotoOutputDelegate&lt;/code&gt; to achieve the same for iOS 11&lt;/li&gt;
&lt;li&gt;The reason these photo taking API’s from Apple is deprecated and added for every major release is that every major iOS release is included with lots of feature for photo capture. Do you know that you can take the RAW image file and save it as DNG and can be edited with your favourite photo editing app without losing any image data? Yes, its possible from iOS 10&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AVCapturePhotoOutput&lt;/code&gt; is so powerful that we need to take a bit more time to avoid errors. I would suggest reading more on &lt;a href=&quot;https://developer.apple.com/library/content/documentation/AudioVideo/Conceptual/PhotoCaptureGuide/&quot;&gt;“Photo Capture programming guide”&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;To save image to photos album we have used &lt;code&gt;UIImageWriteToSavedPhotoAlbum&lt;/code&gt; method. But if you want more control over saving photos, you need to check out &lt;code&gt;[PHPhotoLibrary](https://developer.apple.com/documentation/photos/phphotolibrary)&lt;/code&gt; Since photos app is become so powerful, starting from iOS 8 now we have more control over Photo Library as well.&lt;/li&gt;
&lt;li&gt;Having a “Snap a photo” button seems to be old-school. So I created a rounded white background button and the same code is available in Github repo.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;On next part, I will write about detect QR code or Face from our custom camera view. Follow me here or on &lt;a href=&quot;https://twitter.com/rizzu26&quot;&gt;twitter&lt;/a&gt; to get updates. If you like this post please share and recommend.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>📸 Swift Camera — Part 3</title><link>https://rizwan.dev/blog/swift-camera-part-3/</link><guid isPermaLink="true">https://rizwan.dev/blog/swift-camera-part-3/</guid><description>Use custom camera view to detect QR code</description><pubDate>Wed, 05 Jul 2017 08:59:57 GMT</pubDate><content:encoded>&lt;p&gt;Use custom camera view to detect QR code&lt;/p&gt;
&lt;p&gt;
Photo by Nadine Shaabana on &lt;a href=&quot;https://unsplash.com/search/iphone?photo=vuec_Nz_FnA&quot;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is third part of “Swift Camera” series. You can check those from below&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/2017-06-16_Swift-Camera-Part-1/&quot;&gt;📸 Swift Camera — Part 1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/2017-06-20_Swift-Camera-Part-2/&quot;&gt;📸 Swift Camera — Part 2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As usual, project code is available in Github from all parts.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rizwankce/Camera/&quot;&gt;rizwankce/Camera&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Before we dive in, you need to check the code from Part 1 where we discuss about creating custom camera view. Below steps will use the same view controller as other part and add detecting QR code feature to it.&lt;/p&gt;
&lt;p&gt;AVFoundation provides set of API’s to detect the barcodes from our custom camera. To check the list of supported barcode type check &lt;a href=&quot;https://developer.apple.com/documentation/avfoundation/avmetadataobject.objecttype&quot;&gt;AVMetadataObject.ObjectType&lt;/a&gt; . In this post we will learn about detecting QR (Quick Response) code.&lt;/p&gt;
&lt;h2&gt;Step 1&lt;/h2&gt;
&lt;p&gt;Create an instance of &lt;code&gt;AVCaptureMetaDataOutput&lt;/code&gt; in our &lt;code&gt;viewDidLoad&lt;/code&gt; method inside do…catch loop and add it to our session as output like below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Initialize a AVCaptureMetadataOutput object and set it as the
// input device
let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOutput)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make a note our captureSession can add more then one output. In our example we are having &lt;code&gt;AVCapturePhotoOuput&lt;/code&gt; and &lt;code&gt;AVCaptureMetadataOutput&lt;/code&gt; to simultaneously take photo or read meta data from single camera view.&lt;/p&gt;
&lt;h2&gt;Step 2&lt;/h2&gt;
&lt;p&gt;Set delegate for MetadataObjectsDelegate and metadataObjectTypes like below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Set delegate and use the default dispatch queue to execute the
// call back
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
captureMetadataOutput.metadataObjectTypes =\[AVMetadataObjectTypeQRCode\]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, meta data object delegate requires a dispatch queue. To simplify it, we are going with Main queue. But for production application it’s better to create a serial queue for our delegate.&lt;/p&gt;
&lt;p&gt;Let &lt;code&gt;captureMetadataOutput&lt;/code&gt; know what type of object types we need to detect. It accepts array of meta data types. If you want to detect all supported types just list them in an array.&lt;/p&gt;
&lt;h2&gt;Step 3&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;AVCaptureMetadataOutputObjectsDelegate&lt;/code&gt; methods gives us meta data objects which was detected from our capture session.&lt;/p&gt;
&lt;p&gt;Create an extension for ViewController and extends from &lt;code&gt;AVCaptureMetadataOutputObjectsDelegate&lt;/code&gt; like below&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;extension ViewController : AVCaptureMetadataOutputObjectsDelegate {

    func captureOutput(\_ captureOutput: AVCaptureOutput!,
        didOutputMetadataObjects metadataObjects: \[Any\]!,
        from connection: AVCaptureConnection!) {
        // get detected meta data
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;didOutputMetaDataObjects&lt;/code&gt; delegate method will give us detected meta data objects from our connection.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let metadataObj = metadataObjects\[0\] as! AVMetadataMachineReadableCodeObject
if metadataObj.type == AVMetadataObjectTypeQRCode {
   if metadataObj.stringValue != nil {
      debugPrint(metadataObj.stringValue)
   }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we are casting our meta data object to &lt;code&gt;AVMetaDataMeachineReadableCodeObject&lt;/code&gt; first and checking whether it is type of QR code or not. stringValue from meta data object is our decoded QR code message.&lt;/p&gt;
&lt;p&gt;Meta data object does provide the detected QR code frames. So we can show the detected frames in our camera preview as well. To get the frames use &lt;code&gt;transformedMetaDataObject&lt;/code&gt; method from AVVideoPreviewLayer class.&lt;/p&gt;
&lt;p&gt;let barCodeObject = videoPreviewLayer?.transformedMetadataObject(for: metadataObj)&lt;/p&gt;
&lt;p&gt;barCodeObject.bounds will give us the frames of detected code.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you want to detect the Face on camera view, we can follow the same method as QR code and add face type for metadataobjects. Check out m Github repo branch for more info.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rizwankce/Camera/tree/feature/detect-face&quot;&gt;rizwankce/Camera
&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iOS 11 has new Vision API’s to detect QR codes as well. &lt;em&gt;Documentation is not mature enough yet. Still trying to figure out how to read QR with Vision API&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Example project on Github has more functions like display the detected QR code in a UILabel and draw a square on camera preview by using detected meta data bounds.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Though Swift Camera series ends here. I will write tiny post in future for sure. So follow me on here or &lt;a href=&quot;https://twitter.com/rizzu26&quot;&gt;twitter&lt;/a&gt; to get updates. I would like to thank &lt;a href=&quot;https://medium.com/compileswift&quot;&gt;ComplieSwift&lt;/a&gt; editor and everyone who recommended and followed me on medium.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>SwiftUI Color Scheme</title><link>https://rizwan.dev/blog/swiftui-color-scheme/</link><guid isPermaLink="true">https://rizwan.dev/blog/swiftui-color-scheme/</guid><description>SwiftUI way to change the interface style</description><pubDate>Wed, 22 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In UIKit app, If you would like to change the color scheme within the app. Setting &lt;code&gt;UserInterfaceStyle&lt;/code&gt; object to your root window would help.&lt;/p&gt;
&lt;p&gt;Take a look at &lt;a href=&quot;https://github.com/Ranchero-Software/NetNewsWire&quot;&gt;NetNewsWire&lt;/a&gt; iOS app. It gives Appearance settings where you can choose your color palette for the app. These are the possible options&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic: respects system color scheme and changes whenever user switching between light and dark mode from the OS&lt;/li&gt;
&lt;li&gt;Light: Light mode for the whole app and didn&apos;t respect the system color scheme&lt;/li&gt;
&lt;li&gt;Dark: dark mode for the whole app and didn&apos;t respect the system color scheme&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;func updateUserInterfaceStyle() {
  switch AppDefaults.userInterfaceColorPalette {
    case .automatic:
      window!.overrideUserInterfaceStyle = .unspecified
    case .light:
      window!.overrideUserInterfaceStyle = .light
    case .dark:
      window!.overrideUserInterfaceStyle = .dark
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Setting &lt;code&gt;unspecified&lt;/code&gt; makes the app respects the system-wide color scheme here.&lt;/p&gt;
&lt;h2&gt;SwiftUI Way&lt;/h2&gt;
&lt;p&gt;SwiftUI gives us an environment variable and preferred color scheme modifier to change the color scheme.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct ContentView: View {
    @State var preferredColorScheme: ColorScheme? = nil

    var body: some View {
        List {
		        Button(action: {
                preferredColorScheme = .light
            }) {
                HStack {
                    Text(&quot;Light&quot;)
                    Spacer()
                    if preferredColorScheme == .light {
                        selectedImage
                    }
                }
            }

            Button(action: {
                preferredColorScheme = .dark
            }) {
                HStack {
                    Text(&quot;Dark&quot;)
                    Spacer()
                    if preferredColorScheme == .dark {
                        selectedImage
                    }
                }
            }
        }
        .listStyle(InsetGroupedListStyle())
        .preferredColorScheme(preferredColorScheme)
        .navigationBarTitle(&quot;ColorScheme Test&quot;)
    }

    var selectedImage: some View {
        Image(systemName: &quot;checkmark&quot;)
            .foregroundColor(.blue)
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above code, we are setting the &lt;code&gt;preferredColorScheme&lt;/code&gt; modifier to our content view and changing it via &lt;code&gt;@State&lt;/code&gt; object. It will give us one view with two options where you can change the color scheme.&lt;/p&gt;
&lt;p&gt;It&apos;s also possible with the environment value like below&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@main
struct ColorSchemeTestApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environment(\.colorScheme, .dark)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can also get to know the currently selected color scheme via environment object like below.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@Environment(\.colorScheme) var colorScheme
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;As of beta 2, there is no way that I can find, to set the &lt;code&gt;colorScheme&lt;/code&gt; to &lt;code&gt;unspecified&lt;/code&gt; to get automatic behaviour. I have even filed a Feedback. In case if you find a way to achieve it, let me know at &lt;a href=&quot;https://www.twitter.com/rizzu26&quot;&gt;twitter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I did create a sample project to explain the behaviour and you can find it &lt;a href=&quot;https://github.com/rizwankce/SwiftUIColorSchemeTest&quot;&gt;here&lt;/a&gt; at Github.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/environmentvalues/colorscheme&quot;&gt;Color Scheme&lt;/a&gt; Apple documentation&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/disclosuregroup/preferredcolorscheme(_:)&quot;&gt;Preferred Color Scheme&lt;/a&gt; modifier Apple documenation&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/Ranchero-Software/NetNewsWire&quot;&gt;NetNewsWire&lt;/a&gt; project on Github&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>SwiftUI Import/Export files</title><link>https://rizwan.dev/blog/swiftui-import-export-files/</link><guid isPermaLink="true">https://rizwan.dev/blog/swiftui-import-export-files/</guid><description>SwiftUI way to present document picker</description><pubDate>Sat, 18 Jul 2020 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Future Me: Things have changes with Xcode beta and this post is outdated. Please take a look at part 2 of this blog &lt;a href=&quot;https://blog.rizwan.dev/blog/swiftui-import-export-files-part-2&quot;&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As discussed on my previous blog &lt;a href=&quot;https://blog.rizwan.dev/blog/learning-swiftui-with-netnewswire&quot;&gt;post&lt;/a&gt;, kickstarting my learnings from contributing to SwiftUI open source project.
In this blog post, I try to explain what I have come across very recently which was missed out during WWDC and impressed when I came to know about it. It&apos;s about import and exporting files.&lt;/p&gt;
&lt;p&gt;In traditional UIKit, whenever you want to export or import files. We have to rely on &lt;code&gt;UIDocumentPickerViewController&lt;/code&gt;.
Here is the sample code from &lt;a href=&quot;https://github.com/Ranchero-Software/NetNewsWire&quot;&gt;NetNewsWire&lt;/a&gt; iOS app where we try to export and import OPML files.&lt;/p&gt;
&lt;h3&gt;Importing files&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;let docPicker = UIDocumentPickerViewController(documentTypes: opmlUTIs, in: .import)
docPicker.delegate = self
docPicker.modalPresentationStyle = .formSheet
self.present(docPicker, animated: true)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Above code will present a document picker and user can select any OPML files from the Files app.&lt;/p&gt;
&lt;h3&gt;Exporting files&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;let docPicker = UIDocumentPickerViewController(url: tempFile, in: .exportToService)
docPicker.modalPresentationStyle = .formSheet
self.present(docPicker, animated: true)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Above code will present a document picker and user can export the OPML files to the Files app.&lt;/p&gt;
&lt;h1&gt;SwiftUI way&lt;/h1&gt;
&lt;p&gt;We saw what it takes to import and export files in UIKit. But in SwiftUI all it takes is one environment object.By utilising the all new export and import files environment values we could easily add support to our SwiftUI apps for import/export functions.&lt;/p&gt;
&lt;h3&gt;Step 1&lt;/h3&gt;
&lt;p&gt;At first, define the environment object on your &lt;code&gt;View&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@Environment(\.exportFiles) var exportAction
@Environment(\.importFiles) var importAction
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Step 2&lt;/h3&gt;
&lt;p&gt;Use the environment variable to export or import files. As like UIKit importing action needs file types and exporting action needs the temporary file urls.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// consider you have file temporary url
exportAction(moving: url) { _ in }
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;importAction(multipleOfType: types) { (result: Result&amp;lt;[URL], Error&amp;gt;?) in
	if let urls = try? result?.get() {
		// you can do with the file urls here
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It&apos;s also possible to import/export multiple files.&lt;/p&gt;
&lt;p&gt;Check out more on my &lt;a href=&quot;https://github.com/Ranchero-Software/NetNewsWire/pull/2204/files&quot;&gt;PR on Github&lt;/a&gt; for how it works on NetNewsWire app.&lt;/p&gt;
&lt;h1&gt;UniformTypeIdenfiers&lt;/h1&gt;
&lt;p&gt;SwiftUI also comes with a new framework for all &lt;code&gt;UTTypes&lt;/code&gt; and it&apos;s called &lt;code&gt;UniformTypeIdentifiers&lt;/code&gt;. This also helps us to define what kind of files we can choose from. It comes with predefined files types and you can find all the system declared types &lt;a href=&quot;https://developer.apple.com/documentation/uniformtypeidentifiers/uttype/system_declared_types&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For any specific case, we can also create our own &lt;code&gt;UTType&lt;/code&gt; with file extensions as well. Here is the code to select only OPML files.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let type = UTType(filenameExtension: &quot;opml&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;p&gt;Check out more info on Apple documentation&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/importfilesaction&quot;&gt;Import files action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/swiftui/exportfilesaction&quot;&gt;Export files action&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.apple.com/documentation/uniformtypeidentifiers&quot;&gt;UniformTypeIdentifiers&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>SwiftUI Import/Export files - Part 2</title><link>https://rizwan.dev/blog/swiftui-import-export-files-part-2/</link><guid isPermaLink="true">https://rizwan.dev/blog/swiftui-import-export-files-part-2/</guid><description>SwiftUI way to present document picker</description><pubDate>Wed, 07 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There were lots of changes this year during the Xcode and iOS beta. Typically we will get major new features on initial beta
and subsequent beta will have only bug fixes. This year is a little bit different, we even got changes to Swift UI at the end of the beta cycle.&lt;/p&gt;
&lt;p&gt;Beta 6 made a change for SwiftUI import/export files. Instead of environment variable now its a modifier. My &lt;a href=&quot;https://blog.rizwan.dev/blog/swiftui-import-export-files&quot;&gt;previous&lt;/a&gt; blog post is outdated and now that Xcode 12 is out, I guess these needs follow up post.&lt;/p&gt;
&lt;h2&gt;Importing&lt;/h2&gt;
&lt;p&gt;To import file, we have to use the &lt;code&gt;fileImporter&lt;/code&gt; modifier. There is no limit where you have to use this modifier. It&apos;s working same as presenting sheets before.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.fileImporter(
      isPresented: $viewModel.isImporting,
      allowedContentTypes: viewModel.importingContentTypes,
      allowsMultipleSelection: true,
      onCompletion: { result in
        if let urls = try? result.get() {
          // you can do with the file urls here
        }
      }
    )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The parameters were self-explainable. It takes&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;code&gt;Binding&amp;lt;Bool&amp;gt;&lt;/code&gt; for presented statues&lt;/li&gt;
&lt;li&gt;an array of content types &lt;code&gt;[UTType]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;a bool to allow multiple selections while importing&lt;/li&gt;
&lt;li&gt;a completion block when the import is success&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Exporting&lt;/h2&gt;
&lt;p&gt;To export files, we have to use the &lt;code&gt;fileMover&lt;/code&gt; modifier.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.fileMover(isPresented: $viewModel.isExporting,
           file: viewModel.generateExportURL()) { _ in }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;File mover takes a &lt;code&gt;Binding&amp;lt;Bool&amp;gt;&lt;/code&gt; for presented status and the URL of the file which needs to be exported.&lt;/p&gt;
&lt;p&gt;In case, if you would like to know a working example, take a look at &lt;a href=&quot;https://github.com/Ranchero-Software/NetNewsWire/pull/2384/files&quot;&gt;this&lt;/a&gt; PR I have raised for NetNewsWire app.&lt;/p&gt;
</content:encoded></item><item><title>TestFlight Review Times</title><link>https://rizwan.dev/blog/test-flight-review-times/</link><guid isPermaLink="true">https://rizwan.dev/blog/test-flight-review-times/</guid><description>TestFlight review times and workflow improvments</description><pubDate>Wed, 03 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you ever submitted an app update recently to TestFlight review on App Store Connect, you know the time take to get it approved. It was the case for even App Store app updates some time back. Recently Apple is improving the total time taken for review resulting in one day review time (some developers from US time zone can get even less then an hour)&lt;/p&gt;
&lt;p&gt;At my work, we do have multiple beta groups and most of the time a new update would take more then 2 days to get it reviewed for TestFlight. Its doesn&apos;t work for urgent release. So we changed the release process little bit different and always keep the TestFlight app updates near instant.&lt;br /&gt;
&lt;br /&gt;
The way TestFlight update works is every new version update has to go to review and every build on the same version is apporved immediately.&lt;/p&gt;
&lt;p&gt;So whenever we released a version to App Store, immediately we submit the same build again for TestFlight app review by bumping the version number. By the time we finish developing the app update, the initial version submitted to review would be approved and submitting second build on same version would get approved immediately.&lt;/p&gt;
&lt;p&gt;For example, after releasing v5.0 to App Store, try submitting v5.1 (or whatever version on your pipeline) with the same build as v5.0. Only change here is version number. By the time you actually wants to push v5.1 for your beta testers. Just bump the build number and submit. It will be approved immediately.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Above works fine as of dated. Who knows WWDC 2020 might have some improvements.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>Tracker Prevention for WKWebView</title><link>https://rizwan.dev/blog/trackers-prevention-for-wkwebview/</link><guid isPermaLink="true">https://rizwan.dev/blog/trackers-prevention-for-wkwebview/</guid><description>Block online trackers in your webview</description><pubDate>Wed, 12 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In 2017, Apple WebKit team announced &lt;a href=&quot;https://webkit.org/tracking-prevention/&quot;&gt;&quot;Intelligent Trackers Protection&quot;&lt;/a&gt; feature which blocks cross-site trackers and cookies. Slowly WebKit team is adding new features to WebKit and bringing the same to Safari.&lt;/p&gt;
&lt;h2&gt;Safari 14&lt;/h2&gt;
&lt;p&gt;Safari 14 improves ITP and by default, it will block all the cross-site trackers by using on-device machine learning to identify trackers and the known trackers are identified by DuckDuckGo. Safari not only blocks the trackers, but it will also list all the trackers and websites in detail. Here is the screenshot of all trackers blocked from Safari 14. I have been using it from beta 1.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2&gt;WKWebView&lt;/h2&gt;
&lt;p&gt;The interesting thing with ITP is we (third party developers) can start using it for our in-app browsers which uses &lt;code&gt;WKWebView&lt;/code&gt; without any extra effort. It comes free when you use &lt;code&gt;WKWebView&lt;/code&gt; and you have iOS 14 or macOS Big Sur installed.&lt;/p&gt;
&lt;p&gt;In case if we want to opt-out from this default behaviour, we could simply set the purpose string &lt;code&gt;NSCrossWebsiteTrackingUsageDescription&lt;/code&gt; in &lt;code&gt;Info.plist&lt;/code&gt;. When present, this key causes the application’s Settings screen to display a user control to disable ITP. The setting cannot be read or changed through API calls.&lt;/p&gt;
&lt;p&gt;NOTE: As of beta 4, I couldn&apos;t get the settings even after adding purpose string. Probably it will work on upcoming beta&apos;s&lt;/p&gt;
&lt;p&gt;Incase if the app wants to be &lt;a href=&quot;https://developer.apple.com/documentation/xcode/allowing_apps_and_websites_to_link_to_your_content/preparing_your_app_to_be_the_default_browser_or_email_client&quot;&gt;&quot;Default Browser&quot;&lt;/a&gt;, purpose strings is not necessary.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;WebKit App Bound domains &lt;a href=&quot;https://webkit.org/blog/10882/app-bound-domains/&quot;&gt;blog post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Special thanks to &lt;a href=&quot;https://twitter.com/stuarticus&quot;&gt;Stuart&lt;/a&gt; who introduced to me this feature in NetNewsWire Slack channel when we discussed having tracker protection for app.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>Uses</title><link>https://rizwan.dev/blog/uses/</link><guid isPermaLink="true">https://rizwan.dev/blog/uses/</guid><description>Software and Hardware I use</description><pubDate>Thu, 07 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Inspired from wesbos’s &lt;a href=&quot;https://wesbos.com/uses&quot;&gt;&lt;code&gt;/uses&lt;/code&gt;&lt;/a&gt; trying to gather all the things I use.&lt;/p&gt;
&lt;h3&gt;Hardware &amp;amp; Accessories&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;MacBook Pro 15 inch - 2016 - i7, 16 GB RAM, 512 GB SSD, Radeon Pro 455 2 GB Graphics&lt;/li&gt;
&lt;li&gt;1st gen Magic keyboard / Keychron k2&lt;/li&gt;
&lt;li&gt;Logitech MX Master 3&lt;/li&gt;
&lt;li&gt;iPhone 11 Pro&lt;/li&gt;
&lt;li&gt;Roost laptop stand&lt;/li&gt;
&lt;li&gt;Bose QC II / AirPods 1st gen&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Software&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Xcode - ٩(◕‿◕｡)۶&lt;/li&gt;
&lt;li&gt;App code - for good old Objective-C&lt;/li&gt;
&lt;li&gt;VS Code - for everything else&lt;/li&gt;
&lt;li&gt;Nova from Panic - beta testing and mainly for React and React Native&lt;/li&gt;
&lt;li&gt;Paw / Postman - for all my HTTP test&lt;/li&gt;
&lt;li&gt;Sip - color picker - to pick color from anywhere on the screen&lt;/li&gt;
&lt;li&gt;1Password - weapon of choice for password manager&lt;/li&gt;
&lt;li&gt;NetNewsWire - RSS reader&lt;/li&gt;
&lt;li&gt;Source Tree - Git&lt;/li&gt;
&lt;li&gt;Things 3 - Goto task manager&lt;/li&gt;
&lt;li&gt;iTerm2 - Terminal of choice with zsh&lt;/li&gt;
&lt;li&gt;Contraste - Color picker and a11y checker&lt;/li&gt;
&lt;li&gt;Dev Cleaner - to clear everything from Xcode&lt;/li&gt;
&lt;li&gt;Hidden Bar - to hide menu bar icons&lt;/li&gt;
&lt;li&gt;One Switch&lt;/li&gt;
&lt;li&gt;Pastebot - clipboard manager&lt;/li&gt;
&lt;li&gt;Tot - stickies notes but better&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Others&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Font of choice - Dank Mono&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=wesbos.theme-cobalt2&quot;&gt;Cobalt 2 theme&lt;/a&gt; on VS Code and custom &lt;a href=&quot;https://github.com/rnystrom/Metro-Lights&quot;&gt;Metro lights theme&lt;/a&gt; for Xcode&lt;/li&gt;
&lt;li&gt;Backups are in iCloud - having 50GB tier&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rizwankce/dotfiles&quot;&gt;Dotfiles&lt;/a&gt; are in GitHub&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>When Not to Delegate to LLMs</title><link>https://rizwan.dev/blog/when-not-to-delegate-to-llms/</link><guid isPermaLink="true">https://rizwan.dev/blog/when-not-to-delegate-to-llms/</guid><description>Understanding the limits of LLM coding tools and knowing when manual work is actually faster</description><pubDate>Mon, 20 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We&apos;re living in the age of LLMs and coding agents - tools like Claude Code, GitHub Copilot, and various CLI assistants promise to accelerate our development workflow. And they do, most of the time. But there&apos;s a catch: not every task is a good fit for delegation to these tools.&lt;/p&gt;
&lt;h2&gt;The Storyboard Refactoring Story&lt;/h2&gt;
&lt;p&gt;Recently, I needed to refactor some Storyboard files in an iOS app. (Yes! Im stuck with work app on Storyboard files) Since Storyboards are XML-based, I thought this would be a perfect task for Claude Code or similar tools. Text manipulation is what LLMs excel at, right?&lt;/p&gt;
&lt;p&gt;Turns out, I was wrong.&lt;/p&gt;
&lt;p&gt;The problem wasn&apos;t the XML itself - it was everything around it. Xcode project files have their own special format. They maintain complex references between files, build configurations, and project settings. Interface Builder has specific constraints about how UI elements can be structured. Breaking any of these invisible rules means your project either won&apos;t build or will crash at runtime with cryptic errors.&lt;/p&gt;
&lt;p&gt;I spent more time debugging the LLM&apos;s output, fixing broken references, and troubleshooting build errors than I would have spent doing the refactoring myself. When I finally gave up and did it manually, it took about 5 minutes.&lt;/p&gt;
&lt;h2&gt;When LLMs Struggle&lt;/h2&gt;
&lt;p&gt;Three patterns trip them up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Tight file formats&lt;/strong&gt;: Xcode projects, Storyboards, and other bespoke files crash if a single reference goes missing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Toolchain nuance&lt;/strong&gt;: An LLM hasn&apos;t spent years with Xcode, Gradle, or your CI, so it misses the small switches that keep things running.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tiny chores&lt;/strong&gt;: If you can do it in minutes, the prompt-review-fix loop is slower than typing it yourself.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Real Skill&lt;/h2&gt;
&lt;p&gt;Before delegating, run through a fast checklist:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Does this rely on niche tooling or hidden rules?&lt;/li&gt;
&lt;li&gt;Can I finish it in under 10 minutes solo?&lt;/li&gt;
&lt;li&gt;Will I spend more time vetting the output than producing it?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Any yes means I stay hands-on.&lt;/p&gt;
</content:encoded></item><item><title>WWDC 2020 Wish-list</title><link>https://rizwan.dev/blog/wwdc-2020-wish-list/</link><guid isPermaLink="true">https://rizwan.dev/blog/wwdc-2020-wish-list/</guid><description>My top wish-list for this year WWDC</description><pubDate>Fri, 22 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I wrote my thoughts on WWDC 2020 wish-list on twitter. Here is the list not in any particular order but hope to see it in real.&lt;/p&gt;
&lt;p&gt;Also, did I tell you? This will be my first ever WWDC. Since It&apos;s online and free for everyone.&lt;/p&gt;
&lt;p&gt;https://twitter.com/rizzu26/status/1258537074556194818?s=20&lt;/p&gt;
&lt;p&gt;https://twitter.com/rizzu26/status/1258537254894485504?s=20&lt;/p&gt;
&lt;p&gt;https://twitter.com/rizzu26/status/1258537413271416832?s=20&lt;/p&gt;
&lt;p&gt;https://twitter.com/rizzu26/status/1258537588513619968?s=20&lt;/p&gt;
&lt;p&gt;https://twitter.com/rizzu26/status/1258538534295621633?s=20&lt;/p&gt;
&lt;p&gt;https://twitter.com/rizzu26/status/1258538768350375937?s=20&lt;/p&gt;
&lt;p&gt;https://twitter.com/rizzu26/status/1259826268444307456?s=20&lt;/p&gt;
</content:encoded></item></channel></rss>