<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Mike Salari on Medium]]></title>
        <description><![CDATA[Stories by Mike Salari on Medium]]></description>
        <link>https://medium.com/@salari-dev?source=rss-a6212e16c88------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*6fAQhc2m58Wo9vzkDyNZhQ.jpeg</url>
            <title>Stories by Mike Salari on Medium</title>
            <link>https://medium.com/@salari-dev?source=rss-a6212e16c88------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 21:45:27 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@salari-dev/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[You have 40 interview tabs open and still don’t know what to study.]]></title>
            <link>https://salari-dev.medium.com/you-have-40-interview-tabs-open-and-still-dont-know-what-to-study-1f6a8826c871?source=rss-a6212e16c88------2</link>
            <guid isPermaLink="false">https://medium.com/p/1f6a8826c871</guid>
            <category><![CDATA[android-development]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[career-advice]]></category>
            <category><![CDATA[job-interview]]></category>
            <category><![CDATA[ios-development]]></category>
            <dc:creator><![CDATA[Mike Salari]]></dc:creator>
            <pubDate>Thu, 12 Nov 2026 14:16:01 GMT</pubDate>
            <atom:updated>2026-11-12T14:16:01.762Z</atom:updated>
            <content:encoded><![CDATA[<h4>Interview preparation gets worse when everything looks important. The closer the interview gets, the more your calendar should force you to cut.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*eHntKUeb5LbrdYN5.png" /></figure><p><em>Originally published on </em><a href="https://salari.dev/writing/40-interview-tabs-dont-know-what-to-study/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=40-interview-tabs-dont-know-what-to-study&amp;utm_content=top"><em>salari.dev</em></a><em>, where the full series on senior mobile interviews lives.</em></p><p>Your interview is in nine days. You have a concurrency article open, a system design course, three Reddit threads, a list of behavioral questions, a half-watched YouTube video and twenty coding problems marked “must redo.” If you are on iOS, add Swift, SwiftUI, persistence and whatever WWDC session suddenly looks urgent. If you are on Android, add Kotlin, coroutines, Flow, Compose, lifecycle and another argument about whether the company will ask architecture or algorithms.</p><p>Everything looks useful. That is the problem.</p><p>You do not have an information shortage. You have more material than the remaining calendar can support, and every evening spent deciding what to study is one less evening actually preparing.</p><p>Prioritisation is uncomfortable because it admits you cannot cover everything. The calendar already admitted that for you.</p><h3>“What could they ask?” is a terrible question</h3><p>They could ask almost anything. That question creates infinite preparation.</p><p>What if they go deep on concurrency? What if system design dominates the loop? What if there is LeetCode? What if they ask persistence, performance, platform internals or the framework you have not touched in two years?</p><p>You can keep adding material forever. The useful question is narrower:</p><p>What can still materially change the outcome before the interview?</p><p>If the interview is three months away, build depth. If it is three weeks away, prioritise. If it is next Tuesday, triage. If it is tomorrow, stop pretending you are going to rebuild your engineering career overnight.</p><p>The closer the interview gets, the smaller the preparation surface should become.</p><h3>Your last interviews are better data than another generic checklist</h3><p>If you have interviewed recently, use the evidence.</p><p>Where did the process end? Did you fail before the technical rounds started? Did you pass coding and fall apart in system design? Did the interviewer keep drilling into concurrency because your first answer never became precise? Did your behavioral answers get weak the moment the question moved from what happened to what you personally decided? Did you survive the loop and still get downlevelled?</p><p>Those patterns matter more than another “Top 100 interview questions” page.</p><p>You can spend weeks making your strongest round even stronger because it is the part you enjoy practising. A candidate who likes algorithms keeps solving algorithms. An architect keeps reading system design. Someone comfortable with platform trivia keeps collecting more trivia.</p><p>It feels productive because you are getting better at something.</p><p>It may not change the outcome at all.</p><p>Preparation should react to the failure you actually have, not the one you prefer working on.</p><h3>Stop studying subjects. Start repairing failures.</h3><p>“I need to learn Swift concurrency” is too big.</p><p>“I cannot explain cancellation, isolation and ordering once the interviewer changes the happy path” is useful.</p><p>“I need Android architecture” is too big.</p><p>“I get confused about state ownership across Compose, ViewModel, process death and persistence” is useful.</p><p>“I need system design” is too big.</p><p>“I can draw the happy-path architecture, but I fall apart when offline state, retries and conflict resolution enter the conversation” is useful.</p><p>The first version gives you a syllabus. The second gives you something you can attack tonight.</p><p>Good preparation gets narrower as you learn more about the weakness. That is how you stop opening new tabs and start closing them.</p><p><em>With days left, the </em><a href="https://salari.dev/books/24-hour-ios-interview-answer-book/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=40-interview-tabs-dont-know-what-to-study&amp;utm_content=mid"><em>24-Hour iOS Interview Answer Book</em></a><em> is the highest-frequency questions as spoken answer cards.</em></p><h3>A real preparation plan has casualties</h3><p>Engineers hate deleting topics because removing something feels irresponsible. You cut one subject and immediately imagine the interviewer asking exactly that question, so you put it back. Soon the plan contains everything, which means it contains no priorities at all.</p><p>Maybe system design is weak, the role is Senior, and you know there is a dedicated design round. That deserves time. Maybe you have passed coding comfortably in your last five loops but still spend every evening on coding puzzles because the progress is easy to measure. That probably does not.</p><p>Maybe your interview is in four days and you discover a platform area that would take two weeks to understand properly. You may have to accept that risk and spend those four days repairing something you can actually move.</p><p>That is not laziness. It is what engineers do everywhere else when resources are limited and a deadline is real.</p><p>The goal is not to finish the syllabus. The goal is to change the hiring decision.</p><h3>The night before is not for heroics</h3><p>This is where otherwise sensible engineers start behaving strangely.</p><p>The interview is tomorrow, so at 11:30 PM one Reddit comment sends them into a topic they have never studied before. Two hours later they are tired, anxious and still do not understand it.</p><p>There is a point where another hour of study produces less value than sleep. There is a point where reviewing five strong answers is better than discovering twenty new weak ones. There is also a point where the correct preparation decision is closing the laptop.</p><p>A deadline should force those decisions earlier, not at 2 AM.</p><p>When the interview is close, your job is not to become a different engineer. It is to make the engineer you already are easier to retrieve under pressure.</p><h3>This is why the 24-Hour books exist</h3><p>I built <a href="https://salari.dev/books/24-hour-ios-interview-answer-book/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=40-interview-tabs-dont-know-what-to-study&amp;utm_content=inline">The 24-Hour iOS Interview Answer Book</a> and <a href="https://salari.dev/books/24-hour-android-interview-answer-book/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=40-interview-tabs-dont-know-what-to-study&amp;utm_content=inline">The 24-Hour Android Interview Answer Book</a> for a different problem from a full interview curriculum.</p><p>The 24-Hour books are for the point where time itself has become part of the problem. You need current questions, useful answer depth, follow-ups and a way to concentrate on what can still change the room you are about to enter.</p><p>Each book includes its own <a href="https://salari.dev/study-room/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=40-interview-tabs-dont-know-what-to-study&amp;utm_content=inline">Study Room</a> online. Every card is there, and the book’s answer stays hidden until you have written yours, so you find out which answers you can actually give before the interviewer does.</p><p>They will not make you an expert overnight. Nothing will.</p><p>What they can do is stop you wasting the remaining hours pretending every weakness deserves equal time. For iOS, that means getting quickly to the questions and follow-ups that matter in the modern iOS loop. For Android, it means doing the same across the current Android interview surface.</p><p>You do not need another forty tabs. You need a decision about what deserves the next hour, and the discipline to let the rest wait.</p><p>Your interview date should delete things from your study plan.</p><h3>Where to go from here</h3><p>I write these books from the same side of the table. Each has a free sample, and the price adjusts to your country at checkout.</p><ul><li><strong>iOS interview this week:</strong> <a href="https://salari.dev/books/24-hour-ios-interview-answer-book/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=40-interview-tabs-dont-know-what-to-study&amp;utm_content=end">The 24-Hour iOS Interview Answer Book</a>, the highest-frequency questions as spoken answer cards.</li><li><strong>Android interview this week:</strong> <a href="https://salari.dev/books/24-hour-android-interview-answer-book/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=40-interview-tabs-dont-know-what-to-study&amp;utm_content=end">The 24-Hour Android Interview Answer Book</a>, the highest-frequency Android questions as answer cards.</li></ul><p><em>Mike Salari is a Staff Mobile Engineer &amp; Tech Lead with 15 years in mobile and 500+ technical interviews from the hiring side. He writes interview tools from the hiring side at </em><a href="https://salari.dev/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=40-interview-tabs-dont-know-what-to-study&amp;utm_content=bio"><em>salari.dev</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1f6a8826c871" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[10 Senior iOS interview questions that reveal how You actually Think]]></title>
            <link>https://salari-dev.medium.com/10-senior-ios-interview-questions-that-reveal-how-you-actually-think-ff55ba9ebe9a?source=rss-a6212e16c88------2</link>
            <guid isPermaLink="false">https://medium.com/p/ff55ba9ebe9a</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[interview]]></category>
            <category><![CDATA[careers]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[interview-questions]]></category>
            <dc:creator><![CDATA[Mike Salari]]></dc:creator>
            <pubDate>Thu, 12 Nov 2026 12:48:47 GMT</pubDate>
            <atom:updated>2026-11-12T12:48:47.670Z</atom:updated>
            <content:encoded><![CDATA[<h4>What 500+ technical interviews taught me about the difference between knowing Swift and thinking like a senior engineer.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T8KjcT7r1ME4TtloyxXn5w.png" /><figcaption>Senior iOS Interviews · From the Hiring Side.</figcaption></figure><p>A senior iOS interview can go badly even when every answer is technically correct.</p><p>I’ve seen candidates explain ARC, actors, SwiftUI and dependency injection confidently, then struggle when the discussion moves from definitions to decisions.</p><p>The difference often becomes obvious in the follow-up.</p><p>Knowing that actors protect isolated mutable state is useful. Knowing which parts of your application should own that state, how cancellation affects the workflow and what you would do when the design starts failing under load is a different level of understanding.</p><p>Here are ten questions I would use to prepare for that kind of conversation.</p><h3>1. When would you choose an actor over @MainActor?</h3><p>A basic answer explains actor isolation.</p><p>A stronger answer starts with ownership. Is the data UI-bound? Does it represent an independently mutable service? What work needs to happen without blocking UI responsiveness?</p><p>An actor serialises access to its isolated state, but it does not automatically make an entire business workflow atomic across suspension points.</p><p>The follow-up I would prepare for:</p><p><strong>What can change while your actor-isolated method is suspended at an </strong><strong>await?</strong></p><h3>2. How would you stop an old network response from changing a new screen?</h3><p>Cancellation is part of the answer.</p><p>It isn’t the whole answer.</p><p>The engineer needs to consider task ownership, whether the underlying operation cooperates with cancellation, and whether the result still belongs to the current screen or session when it returns.</p><p>Ask yourself:</p><p><strong>If a user signs out and another user signs in before the response arrives, what prevents the first user’s data from appearing?</strong></p><p>That is a much better test of production reasoning than merely knowing how to call cancel().</p><h3>3. Why is this SwiftUI view updating so often?</h3><p>Don’t begin by suggesting EquatableView or extracting a subview.</p><p>First establish what is happening.</p><p>Is the body being evaluated frequently? Is layout expensive? Is the rendering pipeline missing frames? Is an observed dependency changing? Is the expensive work somewhere else entirely?</p><p>Apple’s SwiftUI Instruments can help distinguish these causes.</p><p>The follow-up:</p><p><strong>How would you demonstrate that your optimisation improved user-visible performance?</strong></p><h3>4. What happens to your application’s data when the process is terminated?</h3><p>An engineer should distinguish a view update, navigation, scene lifecycle and process termination.</p><p>In-memory state has different guarantees from persisted state. A restored interface does not prove that the underlying business data survived.</p><p>The interesting question is:</p><p><strong>Which state must survive termination, and which state would be dangerous to restore?</strong></p><p>A search query can often be rebuilt. An unsent payment operation needs much more careful treatment.</p><h3>5. How would you design offline writes in a mobile app?</h3><p>“Caching” is not enough.</p><p>A reliable answer needs an explicit write model, durable local intent, retry behaviour and conflict policy.</p><p>If two devices update the same record while disconnected, what happens when both reconnect?</p><p>There is no universal answer. The interviewer is looking for a policy that fits the product and can be explained and tested.</p><h3>6. How would you migrate a large UIKit screen to SwiftUI?</h3><p>I’d be interested in the boundary, not a promise to rewrite everything.</p><p>Which components can move safely? What happens to existing navigation? Where does state live? How do you compare performance and accessibility before and after?</p><p>The follow-up:</p><p><strong>What evidence would make you stop the migration?</strong></p><p>Being able to stop a technically fashionable project is part of senior engineering judgement.</p><h3>7. How would you investigate a memory leak after dismissing a screen?</h3><p>ARC is the starting point.</p><p>I would expect the candidate to investigate ownership: closures, subscriptions, coordinators, UIKit bridges and long-lived services.</p><p>Then I would ask:</p><p><strong>If you remove the retained objects and the screen is still slow, what do you measure next?</strong></p><p>Fixing an ownership bug does not necessarily fix an independent performance problem.</p><h3>8. How do you decide where to place module boundaries?</h3><p>A module boundary is a cost as well as an organisational tool.</p><p>It can isolate ownership and improve build behaviour. It can also create unnecessary interfaces, dependency friction and coordination overhead.</p><p>A convincing answer explains which changes should be independent and why.</p><p>The follow-up:</p><p><strong>What would make you merge two modules you previously separated?</strong></p><h3>9. How would you roll out a risky persistence migration?</h3><p>I’d want to hear about compatibility between app versions, data integrity checks, backups where appropriate, observability and recovery.</p><p>Mobile apps are unusual because older binaries can remain in the wild long after a new release.</p><p>The question isn’t simply whether the migration runs successfully on your machine.</p><p><strong>What happens when an older client encounters data written by a newer one?</strong></p><h3>10. What makes an engineering decision a senior-level decision?</h3><p>Scope, uncertainty, consequences and accountability.</p><p>A senior engineer should be able to describe the constraints, compare credible alternatives, identify failure modes and choose evidence for evaluating the result.</p><p>That can happen in a single difficult feature. It doesn’t require claiming ownership of an entire platform.</p><p>A good interview answer usually starts plainly.</p><p>Then it becomes stronger as the constraints become harder.</p><h3>How I would prepare</h3><p>Pick any three of these questions and answer them aloud.</p><p>Give yourself two minutes for the first answer.</p><p>Then change a constraint: your app must work offline, your team has four engineers rather than forty, the rollout cannot interrupt existing users, or a dependency cannot be replaced.</p><p>If your answer still consists mainly of framework names, you have found something worth practising.</p><p>My <strong>iOS Interview Blueprint</strong> goes further into how answers differ across career levels, from technical foundations to senior engineering judgement, architecture and leadership.</p><p><a href="https://mikesalari.gumroad.com/l/ios-interview-blueprint?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=senior-ios-questions">https://mikesalari.gumroad.com/l/ios-interview-blueprint</a></p><p>The goal isn’t to memorise what an interviewer wants to hear.</p><p>It’s to become better at explaining decisions you can genuinely defend.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ff55ba9ebe9a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[React Native interviews happen at the boundary]]></title>
            <link>https://salari-dev.medium.com/react-native-interviews-happen-at-the-boundary-85fb38a59f21?source=rss-a6212e16c88------2</link>
            <guid isPermaLink="false">https://medium.com/p/85fb38a59f21</guid>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[react-native]]></category>
            <category><![CDATA[technical-interview]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Mike Salari]]></dc:creator>
            <pubDate>Tue, 10 Nov 2026 14:16:01 GMT</pubDate>
            <atom:updated>2026-11-10T14:16:01.580Z</atom:updated>
            <content:encoded><![CDATA[<h4>Senior React Native interview questions and answers: JSI, Fabric, TurboModules, Hermes, lists, OTA releases, and the follow-ups that decide the loop.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*MRJ670PtHNn1Y2RK.png" /></figure><p><em>Originally published on </em><a href="https://salari.dev/writing/react-native-interviews-happen-at-the-boundary/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=react-native-interviews-happen-at-the-boundary&amp;utm_content=top"><em>salari.dev</em></a><em>, where the full series on senior mobile interviews lives.</em></p><p>A React Native interview rarely ends with the JavaScript answer. The first answer only gives the interviewer somewhere to push.</p><p>They ask what crosses the native boundary, which thread is blocked, what survives a process restart, what happens during an over-the-air update and how you would know whether the problem belongs to React, the native module, the renderer or the product design.</p><p>That is where a question bank becomes useful. Not as a list to memorise, but as a map of boundaries you must be able to defend. This page gives you the boundaries, ten questions senior loops actually ask, and the follow-up each one leads to.</p><h3>Name which system owns the failure</h3><p>A weak answer says React Native is slow. A useful answer identifies the work, the thread, the data crossing the boundary and the measurement that would separate JavaScript cost from native rendering cost.</p><p>Separate three layers out loud. The JavaScript engine, <strong>Hermes</strong> by default, executes your code. React’s reconciler decides what changed. The native layer turns that into real platform views and platform calls. A slow screen lives in one of them first.</p><p>The same rule applies to navigation, storage, networking and background work. Start by naming the owner of the state and the lifecycle that may invalidate it. Then explain what evidence would move the investigation somewhere else.</p><h3>Know what the New Architecture changed and what it did not</h3><p>Current React Native runs on the New Architecture. Since 0.82 it is the only runtime you can select, and the old asynchronous bridge is gone. Interviewers expect you to name the parts, not say “it is faster”.</p><ul><li><strong>JSI</strong> is a C++ interface that lets JavaScript hold references to native objects and call them directly, including synchronously, without serialising every message into a queue.</li><li><strong>Fabric</strong> is the renderer. Its core is shared C++ across platforms, and it can measure layout synchronously when a screen needs it.</li><li><strong>TurboModules</strong> are native modules defined by a typed spec. Codegen produces the glue, and modules load lazily instead of all at startup.</li><li><strong>Hermes</strong> ships precompiled bytecode, which removes parse and compile work from startup. It does not fix a slow network call or a heavy first render.</li></ul><p>The senior part is the cost model that remains. JSI changes the transport. It does not remove CPU, memory or thread limits. Large buffers can still be copied, a synchronous native call can still block JavaScript, and a chatty interface ported to a TurboModule keeps its original performance problem.</p><h3>Do not hide platform knowledge behind the framework</h3><p>Cross-platform engineering does not remove iOS and Android behavior. It concentrates the risk at the places where the abstraction stops matching them.</p><p>A senior answer can descend one layer when the constraint requires it. That does not mean implementing every module in Swift or Kotlin. It means recognising when permissions, background execution, memory pressure, accessibility or release behavior cannot be reasoned about from JavaScript alone.</p><h3>Design the native boundary as a contract</h3><p>When a question turns to native modules, the interviewer wants a designed interface, not a pass-through of a vendor SDK. Keep the contract small and typed. Use a promise for a one-shot result, events for a stream with explicit cleanup, and one coarse call for what is logically one operation.</p><pre>// NativeImageTools.ts: a TurboModule spec. Codegen reads this file.<br>import type { TurboModule } from &#39;react-native&#39;;<br>import { TurboModuleRegistry } from &#39;react-native&#39;;<br> <br>export interface Spec extends TurboModule {<br>  // One coarse, async call: disk and decode work never block JS.<br>  resizeBatch(uris: string[], maxEdge: number): Promise&lt;string[]&gt;;<br>}<br> <br>export default TurboModuleRegistry.getEnforcing&lt;Spec&gt;(&#39;NativeImageTools&#39;);</pre><p>Say why it looks like this. The work touches disk, so it is asynchronous. The batch crosses the boundary once instead of once per image. The spec is the contract that catches a mismatch at build time instead of in production.</p><p><a href="https://salari.dev/books/top-react-native-interview-questions/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=react-native-interviews-happen-at-the-boundary&amp;utm_content=mid"><em>Top 300 React Native Interview Questions</em></a><em> goes through these boundaries question by question, from junior to staff.</em></p><h3>Release strategy is part of the architecture</h3><p>A JavaScript update can change application behavior faster than a store release. That power creates compatibility questions. Which native versions can run the bundle? What happens to persisted state? How is a bad update stopped? Which change still requires the stores?</p><p>An answer that names only delivery speed misses the operating cost. The senior signal is the rollback and compatibility model: a bundle targets a specific native runtime version, persisted data carries a schema version, and a bad update has a kill path that does not depend on the broken code.</p><h3>React Native interview questions senior loops ask</h3><p>These are the questions that separate a mid-level answer from a senior one. For each, the point is what the interviewer is testing and where they push next.</p><ul><li><strong>What does the React Native runtime actually do?</strong> Testing: whether you separate the JS engine, the reconciler and the native integration. Follow-up: which of the three you would profile first for a slow screen, and why.</li><li><strong>What is JSI, and what did the New Architecture replace?</strong> Testing: whether you can name JSI, Fabric and TurboModules and what each owns. Follow-up: what costs remain now that the bridge is gone.</li><li><strong>When would you make a native method synchronous?</strong> Testing: thread judgment. Synchronous is only for trivial, predictable work. Follow-up: what happens to the JS thread if that call touches disk.</li><li><strong>How do you optimise a FlatList that drops frames?</strong> Testing: whether you treat row cost as the cause and props as tuning. Follow-up: when getItemLayout is correct, and what it breaks on variable-height rows.</li><li><strong>Your screen is janky. How do you find which thread is responsible?</strong> Testing: measurement before fixes. Follow-up: why memoisation will not fix UI-thread jank from native layout or a JS-driven animation.</li><li><strong>Where should a gesture-driven animation run?</strong> Testing: whether you keep it on the UI thread, typically with Reanimated and Gesture Handler, so it stays smooth while JS is busy. Follow-up: what still has to come back to JS, and how often.</li><li><strong>How would you wrap a third-party native SDK?</strong> Testing: contract design. Follow-up: how you keep an SDK upgrade from leaking into every screen.</li><li><strong>How do you decide whether a library is ready for the current architecture?</strong> Testing: dependency risk. A successful compile is the start. Follow-up: what you exercise on real devices before you trust it.</li><li><strong>What can an over-the-air update change, and what can it not?</strong> Testing: the native compatibility boundary. Follow-up: how a user on an old binary with new persisted state is protected.</li><li><strong>Why does Hermes improve startup, and what does it not fix?</strong> Testing: whether you know it removes parse and compile cost only. Follow-up: how you would measure where the rest of cold start goes.</li></ul><h3>A list re-render bug interviewers like to show</h3><p>A common live exercise is a list that re-renders every row on each keystroke. The row is wrapped in React.memo, and it still re-renders.</p><pre>// Before: a new function and a new style object every render.<br>// React.memo sees changed props, so every row renders again.<br>&lt;FlatList<br>  data={items}<br>  keyExtractor={(item, index) =&gt; String(index)}<br>  renderItem={({ item }) =&gt; (<br>    &lt;Row item={item} style={{ padding: 12 }} onPress={() =&gt; open(item.id)} /&gt;<br>  )}<br>/&gt;<br> <br>// After: stable identities and a real key.<br>const rowStyle = { padding: 12 };<br>const onPressRow = useCallback((id: string) =&gt; open(id), [open]);<br>const renderItem = useCallback(<br>  ({ item }: { item: Item }) =&gt; (<br>    &lt;Row item={item} style={rowStyle} onPress={onPressRow} /&gt;<br>  ),<br>  [onPressRow],<br>);<br> <br>&lt;FlatList data={items} keyExtractor={(item) =&gt; item.id} renderItem={renderItem} /&gt;</pre><p>The index key is the second bug. When items are inserted or removed, rows keep the wrong identity. Name both, then say what you would measure to confirm the fix. If each row is still expensive to render, stable props only hide the real cost.</p><h3>Recovery lines when you are unsure</h3><p>Every senior loop reaches a question you cannot fully answer. The recovery is to state the boundary you do know and the evidence you would gather.</p><ul><li>“I have not measured that on this version. I would check which thread is busy before choosing a fix.”</li><li>“I would expect that call to cross into native once. If it crosses per item, that is the first thing I would change.”</li><li>“I am not sure the update system allows that without a store release. I would check the runtime version rule before promising it.”</li></ul><p>A clear boundary with a named next step scores better than a confident guess. Interviewers remember the guess when the follow-up exposes it.</p><h3>Prepare answers as decisions</h3><p>For each topic, rehearse four sentences: the boundary, the default choice, the condition that changes it and the evidence you would measure.</p><p>That structure turns a list of framework facts into a production answer. It also makes the follow-up easier, because you already stated where your first choice stops being safe.</p><h3>Where to go from here</h3><p>I write these books from the same side of the table. Each has a free sample, and the price adjusts to your country at checkout.</p><ul><li><strong>React Native:</strong> <a href="https://salari.dev/books/top-react-native-interview-questions/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=react-native-interviews-happen-at-the-boundary&amp;utm_content=end">Top 300 React Native Interview Questions</a>, questions from junior to staff, with the production depth.</li><li><strong>Mobile system design:</strong> <a href="https://salari.dev/books/mobile-system-design-blueprint/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=react-native-interviews-happen-at-the-boundary&amp;utm_content=end">Mobile System Design Blueprint</a>, scenarios and exercises with answer keys.</li></ul><p><em>Mike Salari is a Staff Mobile Engineer &amp; Tech Lead with 15 years in mobile and 500+ technical interviews from the hiring side. He writes interview tools from the hiring side at </em><a href="https://salari.dev/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=react-native-interviews-happen-at-the-boundary&amp;utm_content=bio"><em>salari.dev</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=85fb38a59f21" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[I interviewed 13 Senior software developers. 12 used AI. Only one admitted It.]]></title>
            <link>https://salari-dev.medium.com/i-interviewed-13-senior-software-developers-12-used-ai-only-one-admitted-it-30259c6bd9b7?source=rss-a6212e16c88------2</link>
            <guid isPermaLink="false">https://medium.com/p/30259c6bd9b7</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[career-development]]></category>
            <category><![CDATA[mobile]]></category>
            <category><![CDATA[system-design-interview]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[Mike Salari]]></dc:creator>
            <pubDate>Sun, 08 Nov 2026 10:27:04 GMT</pubDate>
            <atom:updated>2026-11-08T10:27:04.134Z</atom:updated>
            <content:encoded><![CDATA[<p>What a week of technical interviews revealed about the difference between having an answer and understanding it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*joz5l4LUVYrypas4v4bsdg.jpeg" /><figcaption>The second screen nobody was supposed to notice.</figcaption></figure><blockquote>The only developer who passed was the one who didn’t use it. Here’s what I noticed from the other side of the interview.</blockquote><p>This week, I interviewed 13 software developers.</p><p>Twelve used AI during their technical interviews. Only one admitted it. The other eleven tried to hide it.</p><p>The only developer who didn’t use AI was also the only one who passed.</p><p>I’ve been interviewing engineers for years, and I’ve seen candidates arrive unprepared, get nervous, forget things they clearly knew, or struggle with questions outside their experience. None of that is unusual.</p><p>What caught my attention this week was something different.</p><h3>The second screen</h3><p>Some candidates had ChatGPT open on their phones.</p><p>I’d ask a technical question. Their eyes would move away from the laptop. There would be a pause while they typed a few keywords into the phone, followed by an answer that sounded remarkably polished.</p><p>It happened during ordinary technical questions and even during system design.</p><p>They seemed to think I wouldn’t notice.</p><p>After 500+ technical interviews from the hiring side, you become familiar with how people communicate when they’re thinking through a problem.</p><p>You notice when someone who was speaking naturally suddenly starts using language that sounds like documentation. Or when a candidate struggles to explain a relatively simple idea but can recite an elaborate definition without hesitation.</p><p>Of course, looking away or pausing doesn’t prove anything. People get nervous. Some need time to translate their thoughts into English. Others simply think better when they’re not staring at a camera.</p><p>I’m not interested in judging candidates by their eye movements.</p><p>I’m interested in what happens when I ask them to explain their answers.</p><h3>System design exposed the gap</h3><p>This was especially noticeable during system design discussions.</p><p>You can ask ChatGPT how to design a messaging system and get a convincing architecture in seconds. Load balancers, databases, caches, message queues. Everything in the right place, at least on paper.</p><p>But a list of components isn’t a design.</p><p>Why did you choose that database? What happens if your queue falls behind? Which part of the system would you change first if the requirements doubled?</p><p>I don’t need a candidate to know every answer immediately. I want to understand how they approach the problem.</p><p>The difficulty with repeating an AI-generated answer is that the explanation often stops where the generated text stops. Once the conversation moves away from the original question, the candidate has to reason independently.</p><p>That’s where the difference becomes visible.</p><p>A good engineer might choose the wrong approach initially. That’s not necessarily a problem. If they recognize the limitation, explain the trade-off, and adjust their decision, I’m learning something valuable about how they work.</p><p>A perfectly structured answer that someone can’t explain tells me very little.</p><h3>The one who passed</h3><p>The only developer who didn’t use AI passed both the technical questions and system design.</p><p>His answers weren’t always perfect.</p><p>He worked through the problems himself, explaining what he understood and how he reached his decisions.</p><p>And that was enough to demonstrate the technical ability I was looking for.</p><p>I don’t expect engineers to remember everything. I’ve worked with experienced developers who need to look up APIs, revisit documentation, or spend time understanding an unfamiliar concept.</p><p>I do the same.</p><p>What matters is whether they understand the decisions they’re making.</p><p>When someone can explain why they chose an approach, where it might fail, and what they’d reconsider, I have something meaningful to evaluate.</p><p>That’s very different from hearing a correct answer without knowing whether the person understands a word of it.</p><h3>AI isn’t the problem</h3><p>I use AI in my own engineering work. I think it’s valuable, and I expect its role in software development to keep growing.</p><p>But using a tool and depending on it to think for you are two different things.</p><p>In real engineering work, AI can suggest an implementation, help investigate a bug, or propose an architecture. You still have to decide whether its suggestions make sense.</p><p>You still own the code, the design, and the consequences.</p><p>If a production issue appears tomorrow, you can’t defend a bad architectural decision by saying ChatGPT recommended it.</p><p>And if you’re using AI during an interview where you’re expected to demonstrate your own technical reasoning, hiding that fact creates another problem: trust.</p><p>I’m not suggesting that every technical interview should prohibit AI. An interview designed to evaluate how developers work with these tools can be perfectly reasonable.</p><p>But the expectations need to be clear.</p><p>When I’m assessing someone’s independent technical ability, I need to hear their thinking, not a response generated on a second screen.</p><p>After those 13 interviews, what stayed with me wasn’t how easily candidates could get convincing answers from AI.</p><p>It was that the person who passed didn’t need to pretend he knew more than he did.</p><h4><strong>AI can generate an answer. It cannot give you the understanding behind it.</strong></h4><p>That’s the difference I was looking for.</p><h3>If you’re preparing for a technical interview</h3><p>I’ve spent years on the hiring side of technical interviews, and that experience has shaped how I write my interview preparation books.</p><p>For iOS engineers, there’s <a href="https://salari.dev/books/"><strong>The iOS Interview Blueprint</strong></a>. For Android engineers, <a href="https://salari.dev/books/"><strong>The Senior Signal</strong></a>.</p><p>Both focus on the knowledge and reasoning you need to explain technical decisions, not just memorize answers.</p><p>You can find them at <a href="https://salari.dev/books/">salari.dev</a>.</p><p><em>Mike Salari has conducted 500+ technical interviews from the hiring side and writes about software engineering, technical interviews, and engineering judgment.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=30259c6bd9b7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why iOS apps feel slow even when the API is fast]]></title>
            <link>https://salari-dev.medium.com/why-ios-apps-feel-slow-even-when-the-api-is-fast-28481cfed6c1?source=rss-a6212e16c88------2</link>
            <guid isPermaLink="false">https://medium.com/p/28481cfed6c1</guid>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[ios-development]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Mike Salari]]></dc:creator>
            <pubDate>Thu, 05 Nov 2026 14:16:01 GMT</pubDate>
            <atom:updated>2026-11-05T14:16:01.491Z</atom:updated>
            <content:encoded><![CDATA[<h4>iOS performance from the hiring side: main-thread hangs, image downsampling, SwiftUI invalidation, how to measure with Instruments and MetricKit.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*i9gfV3irnJxxJSLE.png" /></figure><p><em>Originally published on </em><a href="https://salari.dev/writing/why-ios-apps-feel-slow-even-when-the-api-is-fast/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=why-ios-apps-feel-slow-even-when-the-api-is-fast&amp;utm_content=top"><em>salari.dev</em></a><em>, where the full series on senior mobile interviews lives.</em></p><h3>The API is only one timer</h3><p>Mobile teams often start a performance conversation with backend latency. The endpoint is 180 ms, the payload is cached at the edge, the database query is fine. Then somebody opens the app on an older phone and the screen still feels heavy.</p><p>That is not a contradiction. On iOS, the user does not experience API latency in isolation. They experience launch work, navigation state, JSON decoding, image loading, view invalidation, layout, diffing, persistence, main-thread contention and the timing of all of those things together.</p><p>A fast backend can still feed a slow app. The client can take good data and turn it into a bad experience. Most of the iOS performance optimization that matters happens after the response arrives.</p><h3>The main thread tells the truth</h3><p>If a screen stutters, freezes during navigation or ignores taps for a beat, start by asking what is happening on the main thread. It is common to find work there that nobody considers expensive because each piece looks harmless alone.</p><p>Date formatting in a cell. JSON mapping that grew over time. Image decoding after download. A large diff. A synchronous database fetch. A SwiftUI body invalidating more of the tree than expected. A logging call that does disk work. None of these needs to be dramatic to hurt the feel of the app.</p><p>The fix is not to move everything to background queues randomly. The fix is to identify the user-visible lane and protect it. Rendering, input and navigation need room. Expensive transformation, decoding, persistence and precomputation should happen where they cannot block touch and animation.</p><p>Decoding is the most common offender I see in code reviews. A view model receives Data, decodes it in a property setter, and the setter runs on the main actor. On a small payload nobody notices. On a large one, the tap that triggered the load freezes the screen. Decode away from the main actor and hop back only to publish:</p><pre>@MainActor<br>final class FeedModel: ObservableObject {<br>    @Published private(set) var items: [FeedItem] = []<br> <br>    func load() async throws {<br>        let (data, _) = try await URLSession.shared.data(from: FeedAPI.url)<br> <br>        // Decode and shape the rows off the main actor.<br>        let rows = try await Task.detached(priority: .userInitiated) {<br>            let dtos = try JSONDecoder().decode([FeedItemDTO].self, from: data)<br>            return dtos.map(FeedItem.init)   // formatting happens here, once<br>        }.value<br> <br>        items = rows   // back on the main actor, one assignment<br>    }<br>}</pre><p>Two details matter. The mapping into display values happens in the same background step, so the view never formats anything. And the result lands in one assignment, so SwiftUI sees one change, not a stream of them.</p><h3>Images are usually a system, not an asset</h3><p>A feed can have excellent API latency and still feel slow because images are treated as an afterthought. Large images arrive, get decoded at the wrong time, fight for memory, get requested again, or pop into cells after the user has already scrolled past them.</p><p>A real image pipeline has several decisions: request sizing, disk cache, memory cache, downsampling, cancellation, placeholder behavior, prefetch distance and eviction policy. The right answer depends on the product. A shopping app, banking app, chat app and media feed do not all need the same image behavior.</p><p>Downsampling is the decision most apps skip. A decoded bitmap costs memory by pixel count, not by file size. A 4000 pixel photo shown in a 300 point cell is decoded at full size unless you say otherwise. ImageIO lets you decode straight to the display size, without building the full bitmap first:</p><pre>import ImageIO<br>import UIKit<br> <br>func downsampledImage(at url: URL, to pointSize: CGSize, scale: CGFloat) -&gt; UIImage? {<br>    let sourceOptions = [kCGImageSourceShouldCache: false] as CFDictionary<br>    guard let source = CGImageSourceCreateWithURL(url as CFURL, sourceOptions) else {<br>        return nil<br>    }<br> <br>    let maxPixels = max(pointSize.width, pointSize.height) * scale<br>    let options = [<br>        kCGImageSourceCreateThumbnailFromImageAlways: true,<br>        kCGImageSourceShouldCacheImmediately: true,   // decode now, not at first draw<br>        kCGImageSourceCreateThumbnailWithTransform: true,<br>        kCGImageSourceThumbnailMaxPixelSize: maxPixels<br>    ] as CFDictionary<br> <br>    guard let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, options) else {<br>        return nil<br>    }<br>    return UIImage(cgImage: cgImage)<br>}</pre><p>Call it off the main thread, cache the result keyed by URL and target size, and cancel the work when the cell leaves the screen. kCGImageSourceShouldCacheImmediately moves the decode cost to the moment you create the thumbnail, which is on your background task, instead of the first time the image is drawn on the main thread.</p><p>The mistake is thinking of images as UI decoration. In many mobile products, images are the performance profile. They decide memory pressure, scroll smoothness, perceived loading and how quickly the screen becomes useful.</p><p><a href="https://salari.dev/books/swiftui-under-load/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=why-ios-apps-feel-slow-even-when-the-api-is-fast&amp;utm_content=mid"><em>SwiftUI Under Load</em></a><em> is the long version of this: list stutter, stale requests and slow rendering, with labs you can break and fix.</em></p><h3>SwiftUI performance is about what gets re-evaluated</h3><p>SwiftUI does not redraw everything on every change. It re-evaluates the bodies of views that read the state that changed. That makes dependency reads the main performance lever. A view that reads one wide object is invalidated by every change to that object, and so is everything it builds below it.</p><p>The second lever is identity. ForEach and List track rows by their id. If the id is an array index, an insertion at the top renumbers every row, and SwiftUI treats them all as changed. If the id comes from the domain, a reorder is a reorder and nothing else is rebuilt.</p><p>The third is what the row body costs. A row body runs during scrolling, so whatever it allocates, it allocates at scroll rate. Here is a row that looks fine in review and stutters on a long feed:</p><pre>// Before: sorts in body, keys on index, builds a formatter per row.<br>struct FeedView: View {<br>    @ObservedObject var model: FeedModel<br> <br>    var body: some View {<br>        let sorted = model.items.sorted { $0.date &gt; $1.date }<br>        List(sorted.indices, id: \.self) { i in<br>            let formatter = DateFormatter()<br>            formatter.dateStyle = .medium<br>            Text(formatter.string(from: sorted[i].date))<br>        }<br>    }<br>}<br> <br>// After: the model holds the presented order and the display string.<br>struct FeedItem: Identifiable {<br>    let id: String          // the server&#39;s stable id<br>    let dateText: String    // formatted once, when the data arrived<br>}<br> <br>struct FeedView: View {<br>    @ObservedObject var model: FeedModel<br> <br>    var body: some View {<br>        List(model.items) { item in<br>            Text(item.dateText)<br>        }<br>    }<br>}</pre><p>The fixed version hands SwiftUI a collection that only changes when the data changes, rows keyed on a stable id, and a body that renders a value instead of computing one. Moving formatting into the model when the data arrives is the single change that most often removes a list stutter, and it is usually the last thing people try.</p><p>On List versus LazyVStack: both create rows on demand. List reclaims rows the user has scrolled past, the way a collection view does. LazyVStack keeps rows it has created, so memory grows with how far the user scrolls. For an unbounded feed, List is the default. Switching a slow List to LazyVStack is often the wrong direction.</p><h3>State can make a fast screen feel unstable</h3><p>Performance is not only frame rate. A screen feels slow when it keeps changing shape. Content flashes in, disappears, reorders, shows stale values, then corrects itself. The user reads that as slowness even if every individual operation is technically fast.</p><p>This usually comes from unclear state ownership. The view loads cached data, then network data, then derived data, then an optimistic update, then a server correction. If those transitions are not designed, the screen feels nervous.</p><p>A better approach is to define the states explicitly: cached, refreshing, empty, failed, partial, pending action, synced. The app should not merely have data or not have data. It should know what kind of truth it is showing.</p><p>Ownership also decides invalidation. When one store holds balances, the feed and the form, a balance tick re-evaluates all three. Split them so each store owns one kind of truth, and a change reaches only the views that read it. Paging has the same rule: new pages append, they never reorder rows the user has already seen, or rows jump under the thumb and no rendering work fixes it.</p><h3>How to measure iOS performance</h3><p>Guessing is the anti-pattern. The workflow is short: measure on a real device, fix the biggest thing the data shows, measure again. Profile a release build on the oldest device you support. The simulator and a debug build both lie.</p><ul><li><strong>Name the symptom first.</strong> Slow launch, a hitch while scrolling, a freeze after a tap, and memory growth are four different problems with four different instruments.</li><li><strong>Time Profiler</strong> for CPU. It samples call stacks, so you can see which of your functions own main-thread time during the slow moment. Filter to the main thread and look for your code, not the system frames around it.</li><li><strong>Hangs</strong> for freezes. It flags periods where the main thread was unresponsive long enough for the user to notice, and shows what it was doing.</li><li><strong>The SwiftUI instrument</strong> for view updates. It shows which view bodies were evaluated and how often. A count that scales with the number of rows, when one value changed, points to a wide dependency or unstable identity.</li><li><strong>Allocations</strong> for memory. Mark a generation before and after a flow, such as pushing and popping a screen, and look at what survived that should not have.</li><li><strong>Signposts</strong> for your own spans. Wrap a suspect region with OSSignposter intervals and it appears on the timeline, which turns &quot;the feed feels slow&quot; into &quot;decode took most of the load&quot;.</li><li><strong>Change one thing, then re-record.</strong> Stack five fixes and measure once, and you will not know which one worked or which one regressed something else.</li></ul><p>Instruments answers why it is slow on your desk. The field needs different tools. <strong>MetricKit</strong> delivers daily aggregated payloads from real users, covering launch time, hang rate, memory and disk writes, plus diagnostic payloads with call trees for hangs and crashes. <strong>Xcode Organizer</strong> shows the same signals per release, so a launch regression shows up as a trend line instead of a support ticket.</p><p>Beyond the tools, measure the path the user feels: time to first meaningful content, scroll hitches, image cache hit rate, main-thread stalls, launch time, pagination delay, and how often the user sees a full-screen loading state. The useful question is not whether the system is fast somewhere. It is where the user waits, where they lose trust and where the app blocks the next action.</p><p>Once you measure that path, performance work stops being mystical. You can decide whether to precompute, cache, cancel, paginate earlier, reduce payload size, split state, defer work, or change the product behavior.</p><h3>What interviewers ask about iOS performance</h3><p>I have asked some version of “the app is slow, what do you do?” in hundreds of interviews. The question is open on purpose. Weak answers jump straight to a fix: use LazyVStack, add caching, move it to a background thread. Strong answers ask what slow means, pick the matching instrument, and only then discuss causes.</p><p>The shape I listen for is a story with numbers: a baseline, one targeted change, a re-measurement. “I made scrolling smoother” tells me nothing. “Time Profiler showed most of the frame time in JSON decoding on the main thread, I moved it to a background task, and the hang rate in MetricKit dropped” tells me the candidate has done this for real.</p><p>The follow-ups separate levels:</p><ul><li><strong>“You have ten thousand rows and scrolling stutters.”</strong> Mid-level: lazy container, stable ids, cheap rows. Senior: List over LazyVStack because it reclaims rows, formatting moved into the model, no sorting in body. Staff: the list is a window onto data you do not fully hold, so page the source, keep a bounded working set, and derive identity from the server id across pages.</li><li><strong>“Why is this image-heavy screen using so much memory?”</strong> The answer I want mentions decoded size versus file size and downsampling to the display size at decode time.</li><li><strong>“How would you know this is slow for users, not just for you?”</strong> MetricKit and Organizer, and at senior level, hang rate and launch time as release criteria rather than dashboards someone checks occasionally.</li><li><strong>“Which state change here invalidates the most views?”</strong> A candidate who can point at one wide dependency read and propose a narrower one understands SwiftUI performance. One who says SwiftUI redraws everything does not.</li></ul><p>Red flags are consistent: a formatter created inside a row body, sorting or filtering in body, rows keyed on array index, and &quot;we only have a few hundred items&quot;. That last one holds until the account with forty thousand arrives.</p><h3>Where to go from here</h3><p>I write these books from the same side of the table. Each has a free sample, and the price adjusts to your country at checkout.</p><ul><li><strong>Production SwiftUI:</strong> <a href="https://salari.dev/books/swiftui-under-load/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=why-ios-apps-feel-slow-even-when-the-api-is-fast&amp;utm_content=end">SwiftUI Under Load</a>, races, stale requests and slow rendering, with failure labs.</li><li><strong>Mobile system design:</strong> <a href="https://salari.dev/books/mobile-system-design-blueprint/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=why-ios-apps-feel-slow-even-when-the-api-is-fast&amp;utm_content=end">Mobile System Design Blueprint</a>, scenarios and exercises with answer keys.</li></ul><p><em>Mike Salari is a Staff Mobile Engineer &amp; Tech Lead with 15 years in mobile and 500+ technical interviews from the hiring side. He writes interview tools from the hiring side at </em><a href="https://salari.dev/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=why-ios-apps-feel-slow-even-when-the-api-is-fast&amp;utm_content=bio"><em>salari.dev</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=28481cfed6c1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SwiftUI dismiss closes the wrong screen: why it happens and how to fix it]]></title>
            <link>https://salari-dev.medium.com/swiftui-dismiss-closes-the-wrong-screen-why-it-happens-and-how-to-fix-it-dd7f1bb34046?source=rss-a6212e16c88------2</link>
            <guid isPermaLink="false">https://medium.com/p/dd7f1bb34046</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[books]]></category>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[beta]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Mike Salari]]></dc:creator>
            <pubDate>Wed, 04 Nov 2026 12:07:23 GMT</pubDate>
            <atom:updated>2026-11-04T12:07:23.601Z</atom:updated>
            <content:encoded><![CDATA[<p>If your sheet’s Done button does nothing, or closes the screen behind the sheet too, check where you read dismiss.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Yo3XRPV-q6eX9z3fOp5T8Q.jpeg" /><figcaption>Debugging SwiftUI | Beta</figcaption></figure><h3>SwiftUI dismiss closes the wrong screen: why it happens and how to fix it</h3><p>Subtitle: If your sheet’s Done button does nothing, or closes the screen behind the sheet too, check where you read dismiss.</p><p>If a Done button inside a SwiftUI sheet does nothing, or closes the sheet and the screen under it, the cause is usually one line: the dismiss action was read in the screen that opened the sheet, not inside the sheet. dismiss acts on the place where it was read. Move it into the sheet’s own view and the problem goes away.</p><p>That is the short answer. Below is the code, why it behaves this way, the fix, and the test that lets this bug ship.</p><h3>The code that breaks</h3><pre>struct HostView: View {<br>    @Environment(\.dismiss) private var hostDismiss<br>    @State private var showingEditor = false<br><br>    var body: some View {<br>        Button(&quot;Edit&quot;) { showingEditor = true }<br>            .sheet(isPresented: $showingEditor) {<br>                Button(&quot;Done&quot;) { hostDismiss() } // wrong scope<br>            }<br>    }<br>}</pre><p>hostDismiss is read in HostView. The sheet calls it. The sheet is not the thing being dismissed.</p><h3>Why it happens</h3><p>Apple documents that the dismiss action applies to the environment where it was declared, not to the view that calls it.</p><p>So hostDismiss belongs to HostView. When the sheet calls it, SwiftUI tries to dismiss HostView.</p><p>What that does depends on where HostView sits. Apple also documents that dismiss can pop a view from a navigation stack. With HostView inside a NavigationStack, UI tests on an iOS 27.2 simulator recorded two results:</p><p>Where HostView sits What happens when you tap Done First screen of the stack Nothing to pop. The sheet stays open. Pushed from a list HostView is popped. The sheet closes with it.</p><p>One mistake, two symptoms. That is why it is hard to spot: from one screen it looks like a dead button, from another it looks like a navigation bug.</p><p>These results come from one tested setup. Other hierarchies or OS versions may behave differently, so test yours.</p><h3>The fix</h3><p>Read dismiss inside the view that the sheet shows.</p><pre>private struct PresentedSheet: View {<br>    @Environment(\.dismiss) private var dismiss<br><br>    var body: some View {<br>        Button(&quot;Dismiss sheet&quot;) { dismiss() }<br>            .accessibilityIdentifier(&quot;dismissSheet&quot;)<br>    }<br>}</pre><p>Present PresentedSheet from the parent’s sheet modifier. Its dismiss now comes from the sheet’s environment, and in the same tests only the sheet closes.</p><p>If the parent needs to do something before the sheet closes, like save, pass the sheet a closure from the parent that updates the parent’s state. Keep one owner for closing the sheet. Mixing dismiss, a binding change and a callback without a clear order is how this kind of bug comes back.</p><h3>The test that misses it</h3><p>The usual UI test taps Done and checks that the sheet is gone.</p><p>When HostView was pushed, that test passes, because the whole screen is gone. The bug ships with a green test.</p><p>Check the screen that should still be there instead. After Done, HostView must still be visible. Wait a moment before the check: a check made right after the tap can pass while the closing animation is still running.</p><h3>How this comes up in interviews</h3><p>A common follow-up: a sheet contains a pushed detail, which dismiss closes what?</p><p>A mid-level answer: any dismiss closes the sheet.</p><p>A senior answer: it depends where the action was read. Then it says how to prove it, by checking the screen that should remain, and what has not been tested.</p><h3>Where this is from</h3><p>This is one of 12 cases in Debugging SwiftUI, a short field guide to real SwiftUI bugs. Each case has the symptom, how to find the cause, the fix, and how it was checked.</p><p>The book is in a free private beta. Read it online, tell me where it is wrong or unclear, and if your feedback leads to a fix, your name goes in the acknowledgements. You also get the final book free.</p><p>Apply here: <a href="https://salari.dev/beta/debugging-swiftui/">https://salari.dev/beta/debugging-swiftui</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dd7f1bb34046" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Swift 6 rejected the race. ARC stayed atomic.]]></title>
            <link>https://salari-dev.medium.com/swift-6-rejected-the-race-arc-stayed-atomic-49ecfea687e7?source=rss-a6212e16c88------2</link>
            <guid isPermaLink="false">https://medium.com/p/49ecfea687e7</guid>
            <category><![CDATA[ios-interview-question]]></category>
            <category><![CDATA[compilers]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios-development]]></category>
            <category><![CDATA[concurrency]]></category>
            <dc:creator><![CDATA[Mike Salari]]></dc:creator>
            <pubDate>Tue, 03 Nov 2026 14:16:01 GMT</pubDate>
            <atom:updated>2026-11-03T14:16:01.788Z</atom:updated>
            <content:encoded><![CDATA[<h4>In Apple Swift 6.3.3, actor isolation and Sendable did not change swift_retain and swift_release. Only -assume-single-threaded made ARC non-atomic.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*2BbW1bGc9zKjdYnG.png" /></figure><p><em>Originally published on </em><a href="https://salari.dev/writing/swift-6-arc-atomicity/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=swift-6-arc-atomicity&amp;utm_content=top"><em>salari.dev</em></a><em>, where the full series on senior mobile interviews lives.</em></p><p>Swift 6 rejected the unsafe program exactly where I expected it to.</p><p>I had a small reference-type experiment with state shared across concurrency boundaries. In Swift 6 language mode, the negative control failed with concurrency diagnostics. Good. That part was boring in the best possible way.</p><p>Then I fixed the concurrency problem.</p><p>I moved the state behind actor isolation. I tested Sendable variants. I also tested an @unchecked Sendable case where I was explicitly taking responsibility for the safety guarantee.</p><p>The code compiled.</p><p>Then I looked at what ARC was doing.</p><p>I expected the generated code to change.</p><p>It did not.</p><p>In the Apple Swift 6.3.3 cases I tested, the runtime still went through the normal:</p><pre>swift_retain<br>swift_release</pre><p>Actor isolation had changed what Swift could prove about access to the program state.</p><p>It had not changed the ARC runtime entry points I was observing.</p><p>That was the interesting part.</p><h3>The question</h3><p>Swift 6 has a much stronger concurrency model than earlier Swift versions.</p><p>The compiler can reject code that moves non-Sendable state across isolation boundaries. Actors provide isolation. Sendable describes values that can safely cross concurrency domains. Strict concurrency checking catches classes of mistakes that previously survived compilation.</p><p>So I wanted to test a fairly simple question:</p><blockquote><em>If Swift 6 can prove that some object access is isolated from concurrent mutation, can ARC use cheaper non-atomic reference counting for that object?</em></blockquote><p>That sounds plausible.</p><p>If the compiler knows an object is actor-isolated, why pay synchronization costs for reference-count updates that should not race?</p><p>The problem is that two different questions are being mixed together.</p><p>Swift concurrency asks whether program state is being accessed or transferred safely.</p><p>ARC asks how object lifetime is accounted for.</p><p>Those are related, but they are not the same contract.</p><p>I wanted to see where that distinction showed up in generated code rather than stopping at the language model.</p><h3>The experiment</h3><p>I ran the experiment using <strong>Apple Swift 6.3.3 from Xcode 26.6</strong>, explicitly compiling in Swift 6 language mode.</p><p>The test environment was:</p><pre>Apple Swift 6.3.3<br>Xcode 26.6<br>macOS 26.6.2<br>arm64<br>Swift language mode 6<br>iPhone 17 Simulator for simulator validation</pre><p>The repository contains a negative control plus several valid cases, including actor isolation and Sendable variants.</p><p>The negative control matters.</p><p>Without it, seeing a successful Swift 6 build tells us almost nothing. I wanted a case where the compiler demonstrably rejected unsafe sharing, then closely related cases where the concurrency problem had actually been addressed.</p><p>The unsafe case failed as expected.</p><p>The valid cases compiled.</p><p>Then I looked at multiple stages of compiler output.</p><p>That distinction matters because SIL ownership instructions are not the same thing as the final ARC runtime calls.</p><p>At the SIL level you can see ownership operations and retain/release behaviour represented in Swift’s intermediate representation.</p><p>But if the question is:</p><blockquote><em>Which runtime reference-counting entry point does this build actually use?</em></blockquote><p>then LLVM IR and assembly are the more interesting places to look.</p><p>In the ordinary Swift 6 builds I tested, I continued to find:</p><pre>swift_retain<br>swift_release</pre><p>That remained true when the concurrency model around the object changed.</p><p>Actor isolation did not turn the observed calls into non-atomic ARC operations.</p><p>Neither did the Sendable or @unchecked Sendable cases I tested.</p><h3>Then I changed one compiler assumption</h3><p>There is a compiler option that makes this experiment much more useful:</p><pre>-Xfrontend -assume-single-threaded</pre><p>The name is refreshingly direct.</p><p>Instead of asking the compiler to infer from actor isolation that some individual object’s reference counting can be cheaper, this flag changes a much broader assumption about the program.</p><p>The compiler is being told to assume single-threaded execution.</p><p>I compiled the controlled case again.</p><p>This time, the output changed.</p><p>Instead of the ordinary ARC runtime entry points, I observed:</p><pre>swift_nonatomic_retain<br>swift_nonatomic_release</pre><p>And that happened in both the unoptimized and optimized configurations I tested.</p><p>The clean A/B looked like this:</p><ul><li><strong>normal </strong><strong>-Onone</strong>. Retain: swift_retain; Release: swift_release</li><li><strong>single-threaded </strong><strong>-Onone</strong>. Retain: swift_nonatomic_retain; Release: swift_nonatomic_release</li><li><strong>normal </strong><strong>-O</strong>. Retain: swift_retain; Release: swift_release</li><li><strong>single-threaded </strong><strong>-O</strong>. Retain: swift_nonatomic_retain; Release: swift_nonatomic_release</li></ul><p>That gave me the selector I wanted.</p><p>The same general reference-counting operation could be lowered differently when the compiler was explicitly told to assume a single-threaded program.</p><p>But Swift 6 actor isolation by itself had not produced the same result in my experiment.</p><h3>Why this distinction matters</h3><p>It would be easy to look at Swift 6 concurrency and reason like this:</p><ul><li>An actor guarantees isolated access.</li><li>Therefore this reference cannot be touched concurrently.</li><li>Therefore ARC for this reference does not need atomic operations.</li></ul><p>The first statement can be valid.</p><p>The jump from the first statement to the third is where things become much less obvious.</p><p>Actor isolation is a source-level concurrency guarantee about access to isolated state.</p><p>ARC lifetime operations may happen for references that move through code outside the isolated body, through calling conventions, temporary values, captures, ownership transfers, bridging, optimizer transformations, and other parts of the generated program.</p><p>A proof that some mutable state is actor-isolated is not automatically the same thing as a whole-program proof that every relevant lifetime operation for an object can use non-atomic accounting.</p><p>The experiment does not prove why every individual retain or release remains on a particular runtime path.</p><p>What it does show is narrower and more useful:</p><blockquote><em>In the Apple Swift 6.3.3 cases I tested, making the program concurrency-safe through actor isolation, </em><em>Sendable, or </em><em>@unchecked Sendable did not change the observed ARC calls from </em><em>swift_retain / </em><em>swift_release to their non-atomic counterparts.</em></blockquote><p>Explicitly switching the compiler to a single-threaded assumption did.</p><p>This is <strong>compiler/runtime implementation evidence for the tested toolchain, not a Swift language guarantee</strong>.</p><p>That distinction matters.</p><h3>Following the compiler</h3><p>I also traced the relevant compiler path instead of treating the runtime symbol names as magic.</p><p>For the toolchain path examined in the repository, the single-threaded assumption flows roughly like this:</p><pre>frontend option<br>↓<br>CompilerInvocation<br>↓<br>SILOptions::AssumeSingleThreaded<br>↓<br>SILModule::isDefaultAtomic()<br>↓<br>IRGen Atomicity<br>↓<br>GenHeap runtime call selection</pre><p>That is useful because it connects the frontend assumption to the ARC lowering decision.</p><p>The interesting part is what I did not find in that path.</p><p>I did not find a corresponding rule that says:</p><pre>actor-isolated object<br>↓<br>non-atomic ARC</pre><p>or:</p><pre>Sendable<br>↓<br>non-atomic ARC</pre><p>And the generated output from the experiment matched that.</p><p>That does not mean such an optimization could never exist.</p><p>It means I could not demonstrate it in these cases, with this toolchain, and the compiler path I traced points to a broader default-atomicity decision rather than a simple mapping from Swift concurrency annotations to ARC atomicity.</p><p>There is one additional limitation worth stating clearly: the open-source Swift source revision inspected in the repo is not proven to be the exact source revision Apple used to build Swift 6.3.3.</p><p>So the compiler-source tracing is supporting evidence, not a claim of exact source-build identity.</p><h3>Sendable is not an ARC annotation</h3><p>This is also a useful reminder about Sendable.</p><p>Sendable is frequently discussed as if it were a general &quot;thread safe&quot; marker.</p><p>That is too vague.</p><p>Its job is about whether values can safely cross concurrency boundaries under Swift’s concurrency model.</p><p>It does not say:</p><blockquote><em>Use non-atomic reference counting for this instance.</em></blockquote><p>Likewise, @unchecked Sendable does not unlock an ARC optimization.</p><p>It tells the compiler that you are taking responsibility for a guarantee it cannot verify itself.</p><p>In my tested cases, neither changed the ARC runtime calls I was looking at.</p><p>That separation is important because otherwise it becomes very easy to assign performance properties to concurrency annotations that the language never promised.</p><p><em>The ARC question, three answers deep, is in </em><a href="https://salari.dev/books/ios-interview-blueprint/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=swift-6-arc-atomicity&amp;utm_content=mid"><em>The iOS Interview Blueprint</em></a><em>: what most candidates say, the senior answer and the staff answer.</em></p><h3>Actor isolation is still doing real work</h3><p>None of this makes actor isolation less valuable.</p><p>The negative control was rejected.</p><p>The isolated version compiled.</p><p>That is a meaningful change in the program.</p><p>Swift 6 was enforcing a concurrency contract and preventing a class of unsafe access.</p><p>What did not happen was a second, independent assumption:</p><blockquote><em>Because this code is now accepted by the concurrency checker, ARC can use non-atomic lifetime accounting.</em></blockquote><p>Those are two different decisions.</p><p>That distinction is probably the main thing I took away from the experiment.</p><p>Swift’s concurrency model can become more precise without every other subsystem automatically inheriting those proofs.</p><h3>Why not just look at SIL?</h3><p>Because SIL can easily tempt you into making claims one level too early.</p><p>Ownership operations in SIL tell you something about Swift’s ownership representation and optimization pipeline.</p><p>They do not necessarily tell you which reference-counting runtime function survives into the final generated program.</p><p>Optimization makes this even more complicated.</p><p>A retain may disappear. Two operations may be combined. A lifetime can be shortened or extended. Something visible at -Onone may not exist at -O.</p><p>For this question, I wanted to check SIL, then continue down to LLVM IR and assembly rather than infer the final runtime behaviour from one intermediate stage.</p><p>That also makes the experiment easier to falsify.</p><p>You do not have to trust my interpretation of a SIL ownership instruction.</p><p>You can inspect the generated IR or assembly and search for the actual runtime symbols.</p><h3>What this experiment does not prove</h3><p>There are several conclusions I would not draw from this.</p><p>It does <strong>not</strong> prove:</p><blockquote><em>Swift ARC is always atomic.</em></blockquote><p>It does not prove:</p><blockquote><em>Actor-isolated objects always use atomic refcounts.</em></blockquote><p>It does not prove:</p><blockquote><em>Sendable can never influence ARC optimization.</em></blockquote><p>It does not prove:</p><blockquote><em>Atomic reference counting protects your mutable program state.</em></blockquote><p>It does not prove:</p><blockquote><em>-assume-single-threaded is a production optimization you should enable.</em></blockquote><p>And it does not prove that the behaviour observed in this toolchain is a permanent Swift language rule.</p><p>Those claims are much broader than the evidence.</p><p>The result is specifically about the compiler configurations and source cases I tested.</p><p>The defensible claim is:</p><blockquote><em>In these Apple Swift 6.3.3 experiments, actor isolation, </em><em>Sendable, and </em><em>@unchecked Sendable changed the concurrency validity of the program but did not change the observed ARC runtime entry points. Explicitly compiling with </em><em>-assume-single-threaded did.</em></blockquote><p>That is the claim I am comfortable publishing.</p><h3>The practical lesson</h3><p>Most application developers will never choose ARC entry points manually.</p><p>That is not really the point.</p><p>The useful lesson is about how to reason about compiler guarantees.</p><p>Swift 6 rejecting a data race does not mean every subsystem now gets to assume that the entire lifetime of every involved object is single-threaded.</p><p>Concurrency safety is one contract.</p><p>Object lifetime accounting is another.</p><p>Sometimes a compiler can connect proofs across those boundaries.</p><p>Sometimes it cannot, or deliberately does not.</p><p>And sometimes you need to stop looking at the language feature and inspect the generated program.</p><p>That is what happened here.</p><p>Swift 6 rejected the race.</p><p>ARC stayed on the ordinary path.</p><p>Only when I explicitly told the compiler to assume a single-threaded world did the reference-counting path change.</p><h3>Reproduce it</h3><p>The full experiment is public:</p><p><strong>GitHub:</strong> <a href="https://github.com/salari-dev/swift6-arc-atomicity-lab">https://github.com/salari-dev/swift6-arc-atomicity-lab</a></p><p>The repository includes:</p><ul><li>the Swift test cases;</li><li>the negative concurrency control;</li><li>generated SIL, LLVM IR and assembly evidence;</li><li>the atomic versus non-atomic A/B;</li><li>compiler-selection tracing;</li><li>runtime-source mapping;</li><li>claim auditing;</li><li>simulator validation;</li><li>reproduction scripts.</li></ul><p>To regenerate the compiler experiment:</p><pre>./run-experiment.sh</pre><p>For simulator validation:</p><pre>./run-simulator-validation.sh</pre><p>The repository preserves the frozen evidence under Evidence/, while regenerated output is written separately.</p><p>The experiment currently uses one official Apple Swift toolchain. No second official Swift toolchain was installed and tested, so cross-toolchain reproduction remains unresolved.</p><p>I would encourage you to run it rather than take this article on faith.</p><p>Compiler behaviour is exactly the kind of thing where the interesting answer may change with a future toolchain.</p><p>And if a newer Swift compiler starts lowering one of these cases differently, that would make the experiment more interesting, not less.</p><h3>References</h3><ul><li>Swift, <a href="https://docs.swift.org/main/documentation/swift/actor/">Actor</a></li><li>Swift, <a href="https://docs.swift.org/latest/documentation/swift/sendable/">Sendable</a></li><li>Reproduction repository: <a href="https://github.com/salari-dev/swift6-arc-atomicity-lab">salari-dev/swift6-arc-atomicity-lab</a></li></ul><h3>Where to go from here</h3><p>I write these books from the same side of the table. Each has a free sample, and the price adjusts to your country at checkout.</p><ul><li><strong>Production SwiftUI:</strong> <a href="https://salari.dev/books/swiftui-under-load/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=swift-6-arc-atomicity&amp;utm_content=end">SwiftUI Under Load</a>, races, stale requests and slow rendering, with failure labs.</li><li><strong>You have weeks:</strong> <a href="https://salari.dev/books/ios-interview-blueprint/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=swift-6-arc-atomicity&amp;utm_content=end">The iOS Interview Blueprint</a>, the full senior iOS loop, level by level.</li></ul><p><em>Mike Salari is a Staff Mobile Engineer &amp; Tech Lead with 15 years in mobile and 500+ technical interviews from the hiring side. He writes interview tools from the hiring side at </em><a href="https://salari.dev/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=swift-6-arc-atomicity&amp;utm_content=bio"><em>salari.dev</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=49ecfea687e7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[You watched the solution. Then you opened an empty editor.]]></title>
            <link>https://salari-dev.medium.com/you-watched-the-solution-then-you-opened-an-empty-editor-73fb58e9d970?source=rss-a6212e16c88------2</link>
            <guid isPermaLink="false">https://medium.com/p/73fb58e9d970</guid>
            <category><![CDATA[job-interview]]></category>
            <category><![CDATA[coding-interviews]]></category>
            <category><![CDATA[ios-development]]></category>
            <category><![CDATA[ios-interview-question]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Mike Salari]]></dc:creator>
            <pubDate>Thu, 29 Oct 2026 14:16:01 GMT</pubDate>
            <atom:updated>2026-10-29T14:16:01.595Z</atom:updated>
            <content:encoded><![CDATA[<h4>Watching someone code can make you feel ready. Live coding exposes the gap between recognition, retrieval, and making your judgment visible under pressure.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*lNH-pl-sXYi5SotF.png" /></figure><p><em>Originally published on </em><a href="https://salari.dev/writing/you-watched-the-solution-then-opened-an-empty-editor/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=you-watched-the-solution-then-opened-an-empty-editor&amp;utm_content=top"><em>salari.dev</em></a><em>, where the full series on senior mobile interviews lives.</em></p><p>You have a live coding interview next week, so you do what seems reasonable. You search YouTube, find someone solving exactly the kind of problem you are worried about, and spend two hours following along.</p><p>They structure the code, make the tradeoffs, fix the mistake, add the test. Everything makes sense while you are watching. You can almost predict the next line before they type it.</p><p>The next morning you open an empty editor and try to do the same thing yourself.</p><p>Nothing comes out the way it did in the video.</p><p>The naming feels harder. The order disappears. You forget one API, then another. You start questioning architecture you understood perfectly twelve hours ago.</p><p>That does not mean the tutorial was bad.</p><p>It means recognising someone else’s thinking is not the same skill as retrieving your own.</p><p>I have seen this catch experienced engineers in live coding rounds. Give them their normal codebase and twenty quiet minutes, and they can do the work. Put another engineer on a call, start a timer and ask them to make the thinking visible while they code, and the experience changes.</p><h3>Live coding is an unnatural version of a normal job</h3><p>Think about how you actually write software.</p><p>You go quiet, stare at the same function, try one direction, delete half of it, check documentation and come back with a better idea. That is normal engineering.</p><p>Live coding removes most of that privacy. Now somebody is watching while you understand the task, choose a direction, write code, manage the clock, catch your own mistakes and expose enough reasoning for them to tell whether the mess on the screen is controlled or accidental.</p><p>For an introverted engineer, this can feel especially unnatural. Thinking silently may be how you have done good work for years. The interview suddenly asks you to do something you barely practise at work: speak while the thought is still forming.</p><p>That is why someone with ten years of production experience can look less comfortable than someone with three years who has trained for this exact room.</p><h3>“Keep talking” is close to useless advice</h3><p>A lot of live coding advice tells candidates to keep talking.</p><p>That is close to useless because it does not tell you what deserves to be said.</p><p>Nobody needs a radio commentary on your keyboard. “Now I am creating a variable. Now I am adding an if statement.” That gives the interviewer noise, not evidence.</p><p>What they need access to are the decisions.</p><p>“I’m going to get the happy path working before I abstract this.”</p><p>“I see two possible ownership models here. I’m choosing this one because the lifecycle is simpler for the exercise.”</p><p>“I don’t remember the exact API. I know the behaviour I need, so I’m going to sketch it and come back.”</p><p>“I could extract this behind a protocol, but with twenty minutes left that abstraction is not buying us anything.”</p><p>That is useful narration. It tells the interviewer where you are, what you are optimising for and whether your unfinished code is unfinished on purpose.</p><p>A Senior engineer does not need to look perfect during live coding.</p><p>They need to look in control of imperfection.</p><h3>The strongest candidates recover in public</h3><p>This is another place where polished tutorials can give you the wrong mental model. The person in the video usually knows the path. Your interview will not feel like that.</p><p>You will make a mistake.</p><p>Good.</p><p>The interviewer now gets to see something much more interesting than whether you remembered an API.</p><p>Do you panic and start apologising? Do you keep digging because changing direction feels embarrassing? Or do you stop, inspect what happened and say:</p><p>“This is getting more complicated than the requirement deserves. I took the wrong boundary. I’m going to simplify it.”</p><p>That sentence can tell me more than ten minutes of flawless typing.</p><p>Real engineers are wrong constantly. The valuable skill is recognising it before the mistake becomes architecture.</p><p>Live coding gives the interviewer a rare chance to watch that mechanism happen in real time.</p><p><a href="https://salari.dev/books/share-your-screen-live-coding-ios/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=you-watched-the-solution-then-opened-an-empty-editor&amp;utm_content=mid"><em>Share Your Screen</em></a><em> is built for exactly this gap: timed rounds, narrated out loud, with the recovery lines ready.</em></p><h3>Watching helps until it replaces doing</h3><p>YouTube, courses, books and walkthroughs are good ways to learn a pattern. The problem starts when consuming the pattern becomes the practice.</p><p>You can watch twenty implementations of pagination and still freeze when the empty file belongs to you. You can understand someone explaining state ownership and still struggle to choose a boundary without their finished code in front of you. You can follow a system design perfectly and discover that you cannot reconstruct it when one requirement changes.</p><p>The information needs another pass through you.</p><p>Your eyes see it. Your brain understands it. Your hands need to reproduce it. Your mouth needs to defend it.</p><p>That last half is where interview ability gets built.</p><h3>Practise the ugly version</h3><p>If you have a live coding round coming, stop trying to make practice comfortable.</p><p>Open an empty project. Set the actual interview timer. Put the tutorial away. Read the requirement once, decide what matters first and start.</p><p>Speak when there is a decision worth exposing. If you forget an API, say what behaviour you need and keep moving. If the architecture becomes too heavy, simplify it. If something is ugly but correct and the clock is running, leave it ugly and explain the tradeoff.</p><p>You do not need a studio setup. Talk out loud to an empty room and notice where you suddenly go quiet. If you record one practice round, listen back to the reasoning, not the code.</p><p>Where did you go silent because you did not know what to do? Where did nerves make you over-explain? Where did you build something the task never needed? Where did you spot the mistake but refuse to change direction?</p><p>That is live coding practice.</p><h3>You are training retrieval under pressure</h3><p>This is the distinction I wish more experienced engineers understood before their first serious loop in years.</p><p>You probably do not need another hundred hours of engineering knowledge. You may need to make what you already know available on demand.</p><p>Can you reach the idea without the video? Can you implement it without the finished solution? Can you explain the tradeoff before someone supplies the vocabulary? Can you recover when the first idea fails?</p><p>Those are trainable skills.</p><p>But you cannot train them entirely by watching somebody else succeed.</p><p>That is why I wrote <a href="https://salari.dev/books/share-your-screen-live-coding-ios/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=you-watched-the-solution-then-opened-an-empty-editor&amp;utm_content=inline">Share Your Screen</a>.</p><p>The article you just read is platform-neutral. The book is not. Share Your Screen is for Senior iOS engineers, because it goes directly into the live iOS rounds: coding while somebody watches, narrating the decisions that matter, code review, system design, managing the clock, recovering when you stall and holding a technical position when the interviewer pushes back.</p><p>Knowing iOS and showing how you think with iOS under pressure are different skills.</p><p>The first one built your career.</p><p>The second one may decide the next job.</p><p>YouTube gave you recognition. The interview requires retrieval.</p><h3>Where to go from here</h3><p>I write these books from the same side of the table. Each has a free sample, and the price adjusts to your country at checkout.</p><ul><li><strong>The shared-screen round:</strong> <a href="https://salari.dev/books/share-your-screen-live-coding-ios/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=you-watched-the-solution-then-opened-an-empty-editor&amp;utm_content=end">Share Your Screen</a>, senior iOS live coding and system design, solved out loud.</li><li><strong>You have weeks:</strong> <a href="https://salari.dev/books/ios-interview-blueprint/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=you-watched-the-solution-then-opened-an-empty-editor&amp;utm_content=end">The iOS Interview Blueprint</a>, the full senior iOS loop, level by level.</li></ul><p><em>Mike Salari is a Staff Mobile Engineer &amp; Tech Lead with 15 years in mobile and 500+ technical interviews from the hiring side. He writes interview tools from the hiring side at </em><a href="https://salari.dev/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=you-watched-the-solution-then-opened-an-empty-editor&amp;utm_content=bio"><em>salari.dev</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=73fb58e9d970" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SwiftUI vs UIKit: architecture is state ownership]]></title>
            <link>https://salari-dev.medium.com/swiftui-vs-uikit-architecture-is-state-ownership-691e8e8627a3?source=rss-a6212e16c88------2</link>
            <guid isPermaLink="false">https://medium.com/p/691e8e8627a3</guid>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[ios-development]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[swiftui]]></category>
            <dc:creator><![CDATA[Mike Salari]]></dc:creator>
            <pubDate>Tue, 27 Oct 2026 14:16:01 GMT</pubDate>
            <atom:updated>2026-10-27T14:16:01.561Z</atom:updated>
            <content:encoded><![CDATA[<h4>Compare SwiftUI vs UIKit through state ownership, identity, lifecycle, navigation, interoperability and production migration tradeoffs.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*OyC6YLlntmlz5G90.png" /></figure><p><em>Originally published on </em><a href="https://salari.dev/writing/swiftui-architecture-is-state-ownership/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=swiftui-architecture-is-state-ownership&amp;utm_content=top"><em>salari.dev</em></a><em>, where the full series on senior mobile interviews lives.</em></p><h3>SwiftUI vs UIKit is not a universal verdict</h3><p>SwiftUI gives teams a declarative state model, faster composition and strong alignment with current Apple platform direction. UIKit gives mature lifecycle control, deep ecosystem coverage and predictable behavior in large existing applications. Neither framework removes the need for architecture.</p><p>For a new isolated flow on supported operating systems, SwiftUI can reduce presentation code and make state transitions easier to test. For a mature application with complex navigation, specialized collection layouts or years of UIKit infrastructure, gradual interoperability is often safer than a rewrite.</p><p>The senior decision weighs product lifetime, supported OS versions, team experience, accessibility, performance measurement and migration risk. The answer is usually a boundary: where SwiftUI creates leverage now, where UIKit remains the lower-risk owner and how both exchange state without duplicating truth.</p><h3>The architecture debate starts too late</h3><p>Many SwiftUI architecture debates start with the wrong question: should we use MVVM, TCA, Clean Architecture or something custom? That question matters, but it is not the first question.</p><p>The first question is: who owns the state? Most SwiftUI problems that feel architectural are really ownership problems. A value changes in one place, the screen updates somewhere else, an effect returns late, identity changes unexpectedly, and the team starts blaming SwiftUI.</p><p>SwiftUI is not forgiving when ownership is vague. It will render exactly what your model says, even if your model has no clear source of truth.</p><h3>A screen has several kinds of state</h3><p>A production screen usually has more than one kind of state. There is server state, local persisted state, in-memory view state, form state, navigation state, loading state, optimistic state and derived state.</p><p>If those are all pushed into one view model without names, the model becomes a drawer for everything the screen might need. That is not architecture. It is storage.</p><p>A stronger design names the state and decides which layer owns each piece. The view can own short-lived presentation state. A model can own screen behavior. A repository can own persistence and remote refresh. A coordinator or router can own navigation. The exact names matter less than the ownership being explicit.</p><p><a href="https://salari.dev/books/swiftui-under-load/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=swiftui-architecture-is-state-ownership&amp;utm_content=mid"><em>SwiftUI Under Load</em></a><em> goes deeper on ownership, identity and effects in production SwiftUI, with labs you can break and fix.</em></p><h3>Identity is not a cosmetic detail</h3><p>SwiftUI identity decides whether state is preserved, reset or duplicated. A list row, tab, sheet or navigation destination can behave strangely when identity changes without the team noticing.</p><p>This is why production SwiftUI architecture needs rules for IDs, lifetime and view creation. If a view creates an object, it owns that object’s lifetime. If it receives an object, ownership lives somewhere else. If a task is tied to view identity, it may restart when identity changes.</p><p>The senior answer is not just which property wrapper to use. The senior answer explains what lifetime the property wrapper represents.</p><h3>Effects need ownership too</h3><p>Networking, search, image loading, analytics, persistence and sync are effects. If the effect belongs to what the user currently sees, it can often be tied to view lifetime. If the effect represents user intent or data integrity, it needs a more durable owner.</p><p>A search request can be cancelled when the query changes. A draft save should not disappear because the view navigated away. A payment confirmation should not depend on a transient view still existing.</p><p>Good SwiftUI architecture separates visual work from user-intent work. That distinction prevents many bugs that otherwise look like random async behavior.</p><h3>The useful architecture question</h3><p>When reviewing a SwiftUI architecture, ask this: if the app is backgrounded, the network returns late, the user navigates away, the same screen appears twice and cached data refreshes underneath it, does the state model still make sense?</p><p>If the answer is no, changing the pattern name will not save the feature. The ownership model has to become clearer.</p><p>SwiftUI rewards small, honest boundaries. Not because small files are magic, but because clear ownership makes rendering, testing and failure recovery easier to reason about.</p><h3>Use the practical guide</h3><p><a href="https://salari.dev/interview-prep/ios/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=swiftui-architecture-is-state-ownership&amp;utm_content=inline">Senior iOS interview preparation</a>: Prepare for senior iOS interviews across Swift, concurrency, architecture, system design, debugging and staff-level judgment.</p><h3>Where to go from here</h3><p>I write these books from the same side of the table. Each has a free sample, and the price adjusts to your country at checkout.</p><ul><li><strong>Production SwiftUI:</strong> <a href="https://salari.dev/books/swiftui-under-load/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=swiftui-architecture-is-state-ownership&amp;utm_content=end">SwiftUI Under Load</a>, races, stale requests and slow rendering, with failure labs.</li><li><strong>Mobile system design:</strong> <a href="https://salari.dev/books/mobile-system-design-blueprint/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=swiftui-architecture-is-state-ownership&amp;utm_content=end">Mobile System Design Blueprint</a>, scenarios and exercises with answer keys.</li></ul><p><em>Mike Salari is a Staff Mobile Engineer &amp; Tech Lead with 15 years in mobile and 500+ technical interviews from the hiring side. He writes interview tools from the hiring side at </em><a href="https://salari.dev/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=swiftui-architecture-is-state-ownership&amp;utm_content=bio"><em>salari.dev</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=691e8e8627a3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mobile system design is not backend system design]]></title>
            <link>https://salari-dev.medium.com/mobile-system-design-is-not-backend-system-design-c52880bf0a4d?source=rss-a6212e16c88------2</link>
            <guid isPermaLink="false">https://medium.com/p/c52880bf0a4d</guid>
            <category><![CDATA[ios-development]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[system-design-interview]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[mobile]]></category>
            <dc:creator><![CDATA[Mike Salari]]></dc:creator>
            <pubDate>Thu, 22 Oct 2026 13:16:01 GMT</pubDate>
            <atom:updated>2026-10-22T13:16:01.491Z</atom:updated>
            <content:encoded><![CDATA[<h4>Why offline, device constraints, sync, app lifecycle and platform boundaries change the interview and the architecture.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*0KzAIueX17b3niL2.png" /></figure><p><em>Originally published on </em><a href="https://salari.dev/writing/mobile-system-design-is-not-backend-system-design/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=mobile-system-design-is-not-backend-system-design&amp;utm_content=top"><em>salari.dev</em></a><em>, where the full series on senior mobile interviews lives.</em></p><h3>The mistake</h3><p>A lot of engineers prepare for mobile system design interviews by memorizing backend system design templates. They learn how to talk about databases, queues, caches, sharding, load balancers and service boundaries. That knowledge is useful, but it is not enough for a senior iOS interview.</p><p>A senior mobile interview is usually not asking you to redesign Twitter’s backend with an iPhone screen attached. It is asking whether you understand the system that exists between the server and the user. That system includes the app process, local persistence, memory pressure, background execution, network failure, platform APIs, push delivery, release risk and the user’s trust.</p><p>If your answer jumps straight to microservices, you may sound technically fluent while missing the part the interviewer is actually testing.</p><h3>The backend template misses the hardest mobile constraints</h3><p>A backend system design answer usually starts with services, databases, queues and load balancers. A mobile system design answer starts somewhere less comfortable: an unreliable device in somebody’s pocket.</p><p>The app can be killed. The network can disappear. Storage can fill up. The user can perform actions offline, then expect the app to reconcile them later without losing trust. A screen can render with stale data, duplicate data, partial data or no data. A task can be cancelled because the user scrolled away. A background job can be delayed or denied by the operating system.</p><p>That changes the order of the answer. You still need to talk about APIs and backend contracts, but only after you explain state ownership, latency perception, local persistence, sync boundaries, app lifecycle, memory pressure, battery, privacy and release safety.</p><p>A mobile app is not a thin client. It is a distributed node with a UI, cache, persistence layer, sync engine, background task model, notification surface, feature flags, analytics, crash reporting and platform constraints. The system succeeds or fails on the device before the user ever thinks about the backend.</p><h3>The real design surface is state</h3><p>Most mobile system failures are state failures. A feed shows stale data. A sync engine overwrites the wrong value. A cache keeps the app fast until it quietly becomes wrong. A button looks enabled even though the operation already failed in the background.</p><p>For most mobile features, state exists in several layers at once: server state, local persisted state, in-memory state, UI state, optimistic state and derived state. Bugs happen when those layers are unnamed. Senior design means naming them, assigning ownership and describing how they converge after failure.</p><p>Take a feed. A weak answer says: fetch posts, paginate results, cache images. A stronger answer asks: what is the source of truth when the user is offline? How long can cached feed data be trusted? What happens when the server sends an updated item that conflicts with a local optimistic action? If the user likes a post offline, where is that pending mutation stored? How do we avoid showing duplicate items across pages? How do we cancel image work when cells disappear? How do we measure whether the feed is healthy in production?</p><p><em>The </em><a href="https://salari.dev/books/mobile-system-design-blueprint/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=mobile-system-design-is-not-backend-system-design&amp;utm_content=mid"><em>Mobile System Design Blueprint</em></a><em> turns this into practice: scenarios and exercises with answer keys.</em></p><h3>Offline-first is not just caching</h3><p>Caching answers the question: what can I show when I cannot reach the server? Offline-first answers a harder question: what can the user safely do when I cannot reach the server?</p><p>In an interview, say exactly what offline means for the feature. Can the user read existing data? Create new data? Edit existing data? Delete data? Queue actions? See pending status? Retry manually? Abandon failed work?</p><p>Then describe the pending operation model. Store mutations durably with stable IDs, timestamps, retry counts and idempotency keys. Make the UI show pending, failed and synced states. Make retries safe. Make conflicts visible when automatic resolution would damage trust.</p><p>Conflict policy should match the domain. Last-write-wins may be acceptable for low-value preferences. Server-wins may be right for permissioned data. Manual merge may be necessary for collaborative documents. Operation logs can preserve intent better than overwriting whole records.</p><h3>Lifecycle changes the architecture</h3><p>Backend services are usually designed to keep running. Mobile apps are designed to be interrupted. The user can background the app. The operating system can suspend it. Memory pressure can terminate it. A background task can be given a tiny execution window or no execution window at all.</p><p>That means important work needs a durable representation. If the app must eventually upload a draft, sync a note or complete a checkout step, that work cannot exist only as an in-memory Task. It needs to be stored, resumed and reconciled.</p><p>A vague answer says: we retry in the background. A senior answer says: we persist pending operations locally, include idempotency keys, resume sync on launch and foreground transitions, use platform background APIs where appropriate, handle expiration, and surface unresolved failures to the user instead of silently dropping work.</p><h3>What interviewers want to hear</h3><p>They want constraints before components. They want tradeoffs before patterns. They want to know whether you can design for battery, memory, app lifecycle, offline work, rollout risk and observability.</p><p>A good mobile system design answer says: here is the user promise, here are the device constraints, here is the state model, here is the data flow, here is what works offline, here is how failures recover, here is how the UI stays fast, and here is how we will know this works in production.</p><p>Backend architecture still matters. APIs, databases, queues and services matter. But in mobile system design, the phone is not just a client. It is the environment where the system is judged.</p><h3>Use the practical guide</h3><p><a href="https://salari.dev/interview-prep/mobile-system-design/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=mobile-system-design-is-not-backend-system-design&amp;utm_content=inline">Mobile system design interview preparation</a>: Prepare mobile system design interviews with a framework for offline data, synchronization, caching, performance, security and rollout.</p><p><a href="https://salari.dev/interview-prep/mobile-system-design/offline-first/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=mobile-system-design-is-not-backend-system-design&amp;utm_content=inline">Offline-first mobile architecture</a>: Design offline-first mobile apps with a local source of truth, pending writes, conflict handling, sync state and honest UX.</p><h3>Where to go from here</h3><p>I write these books from the same side of the table. Each has a free sample, and the price adjusts to your country at checkout.</p><ul><li><strong>Mobile system design:</strong> <a href="https://salari.dev/books/mobile-system-design-blueprint/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=mobile-system-design-is-not-backend-system-design&amp;utm_content=end">Mobile System Design Blueprint</a>, scenarios and exercises with answer keys.</li><li><strong>Senior to staff:</strong> <a href="https://salari.dev/books/altitude/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=mobile-system-design-is-not-backend-system-design&amp;utm_content=end">Altitude</a>, the systems side of staff mobile engineering.</li></ul><p><em>Mike Salari is a Staff Mobile Engineer &amp; Tech Lead with 15 years in mobile and 500+ technical interviews from the hiring side. He writes interview tools from the hiring side at </em><a href="https://salari.dev/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=mobile-system-design-is-not-backend-system-design&amp;utm_content=bio"><em>salari.dev</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c52880bf0a4d" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>