[{"content":" \u0026ldquo;Do you know what leadership means, Lord Snow? It means that the person in charge gets second-guessed by every clever little tw*t with a mouth. But if he starts second-guessing himself, that\u0026rsquo;s the end.\u0026rdquo; - Ser Alliser Thorne\n Before I became a senior software engineer I often thought my beloved company was terrible at their architecture. I questioned a lot of decisions. But decision making at scale is difficult.\nIt\u0026rsquo;s hard to come up with a perfect solution on the first try. The path to perfection isn\u0026rsquo;t linear. You have to iterate, trial and error to hopefully arrive at a desirable state.\n With more ownership, I\u0026rsquo;ve come to understand that good leadership is less about finding a perfect answer and more about choosing, committing, getting buy-ins and adjusting quickly for incremental improvement\n And even then, not every project succeeds. Often the reasons a project fails are well beyond your control. The priorities are shifted. Budget is cut. Bad information was given.\nDon\u0026rsquo;t blame yourself if that happens. Move on and keep going. Even great commanders can make the best decisions in a war and still lose. But wanna know what\u0026rsquo;s worse than that? A commander who can\u0026rsquo;t decide.\nOk enough intro. Here are guidelines that helped me make decisions faster:\nFrame it  Define your MVP. Otherwise you don\u0026rsquo;t know what problem you\u0026rsquo;re solving. Separate reversible from irreversible decisions. If easily reversible, decide within 10–60 minutes.  Timebox it  Use the 70% rule. If one option seems roughly 70% right, proceed. \u0026lt;\u0026ndash; Perfect is the enemy of good. Good is well enough. You can always optimize after delivering and gaining traction. Set a timer. When it ends, choose the best available option—no additional research. Pick a default. When options are close, choose the simpler, cheaper, or faster one.  Write it down  Document your thought process. Record reasons for alternatives you considered and why you avoided them. Record uncertainty instead of resolving it. Write: \u0026ldquo;Not sure what the latency of this feature will be at scale. Will revisit if latency goes above 200ms\u0026rdquo;. Getting it documented relieves you of its burden.  Evaluate  Get feedback on your final decision and final stamp of approval on your feedback. Even though you\u0026rsquo;re a lead, doesn\u0026rsquo;t mean the good and bad of it, is all to be attributed to you. Take it easy. Being mentally light-weighted allows you to move faster. Judge the quality of a decision by the information and reasoning you had at the time, not just by how it turned out. A good decision can still lead to a bad outcome because of luck or circumstances outside your control.  Commit to it  Write down the plan. Commit to it. If you commit to a plan while also pursuing plan B, then you really haven\u0026rsquo;t committed to your plan at all. Commit for a fixed period. Example: \u0026ldquo;I’ll use this architecture for two weeks before reconsidering.\u0026rdquo;  ","permalink":"https://mfaani.com/posts/career/a-framework-to-make-decisions-faster-as-a-lead-software-engineer/","summary":"\u0026ldquo;Do you know what leadership means, Lord Snow? It means that the person in charge gets second-guessed by every clever little tw*t with a mouth. But if he starts second-guessing himself, that\u0026rsquo;s the end.\u0026rdquo; - Ser Alliser Thorne\n Before I became a senior software engineer I often thought my beloved company was terrible at their architecture. I questioned a lot of decisions. But decision making at scale is difficult.","title":"A Framework to Make Decisions Faster as a Lead Software Engineer"},{"content":"Git Worktree Why would you want to use it? To be able to checkout multiple branches at the same time without having to clone your repo.\nCurrently if you run git worktree list you’d see that you only have one git-worktree. e.g. I get the following\n/Users/mfaani/Developer/project-iOS 0960933f [main] The commit and branch name show the current HEAD; if you were on another branch, the worktree would show that branch instead.\nSo if you wanted to add another worktree then you’d just have to do:\ngit worktree add ../myNewWorkTreePath \u0026lt;branch\u0026gt; NOTE: You’d definitely want to run the above command from the root directory of your project. Otherwise you’d be creating a directory that is within your git worktree and can get committed. You don’t want that. I usually use these steps:\n create a parent directory for your repo put the repo’s top level in that.   ATTENTION: if you copy and paste paths from Finder, make sure you include hidden items like the .git directory. Otherwise git will think you moved the entire project to a different folder and create stupid diff for you\n create the new worktree.  git worktree add ../new-worktree branch-name That way I have both worktrees at the same level again\nImportant Note: At any given moment, a branch can only be checked out in one worktree. If you try to checkout a branch that is already checked out on a different worktree then you’ll get\n fatal: ‘branchB’ is already checked out at ‘/path/to/oneLevelAboveRootProject/treeA’\n  each added work-tree has its own index and HEAD, the HEAD files wind up sharing the underlying branch pointers in the shared repository\n How does AI use worktrees? I recently asked Codex to run 4 tasks in parallel. Codex did that by creating 4 git worktrees.\nWhat was interesting was, Codex didn\u0026rsquo;t create the worktree next to my current directory. It created them under .codex directory instead. The output of running git worktree list was:\n/Users/mfaani/Developer/project-iOS 2696557 [main] /Users/mfaani/.codex/worktrees/036d/project-iOS 7db4714 (detached HEAD) /Users/mfaani/.codex/worktrees/14df/project-iOS e0cebf8 (detached HEAD) /Users/mfaani/.codex/worktrees/28a0/project-iOS 48b8289 (detached HEAD) /Users/mfaani/.codex/worktrees/6b54/project-iOS c8bc3e9 (detached HEAD) Once all tasks were finished, I used the original parent task and asked it to merge all the work that was done in the individual worktrees. It ended up create 4 commits.\nWhy is it using detached head? First know what a detached head is.\nBecause you can\u0026rsquo;t checkout your main branch into 4 worktrees, then you\u0026rsquo;re left with two choices:\n Create 4 branches Create 4 different worktrees pointing to a detached head.  Creating 4 branches is more intrusive. Because then if you run your git branch command, you\u0026rsquo;d end up seeing new branches that you probably don\u0026rsquo;t want to have.\nWhy not just use branches instead of worktrees? Each worktree has its own working tree for files, build artifacts, uncommitted changes, index, and checked-out branch, while still sharing the underlying Git repository/history.\nHad you used git branch for every task, then because you\u0026rsquo;re using a single working tree, the agent would have to constantly switch between branches to be able to commit changes and parallel building would be impossible.\nWhen does Codex remove the worktrees it created?   You can defer it to Codex to decide (default) or just ask Codex to delete the worktree for you. Codex \u0026raquo; Settings \u0026raquo; Worktrees:   You can remove it yourself. Follow steps from here.\n  References  3min video git worktree from Torek on Stack Overflow The Three trees  ","permalink":"https://mfaani.com/posts/ai/how-to-use-git-worktree-and-how-its-used-by-ai-agents/","summary":"Git Worktree Why would you want to use it? To be able to checkout multiple branches at the same time without having to clone your repo.\nCurrently if you run git worktree list you’d see that you only have one git-worktree. e.g. I get the following\n/Users/mfaani/Developer/project-iOS 0960933f [main] The commit and branch name show the current HEAD; if you were on another branch, the worktree would show that branch instead.","title":"How to Use Git Worktree? And How It's used by AI Agents?"},{"content":"How is AGENTS.override.md different from AGENTS.md? Why not just edit the current AGENTS.md file instead?\nOverride is usually for temporary or unique cases. Examples:\n Temporarily testing different agent behavior Override an inherited team file without deleting it. Could be for a developer-specific environment or local workflow. When certain files of a group of files require different instructions. So you create a new directory and add an override to be more explicit.  To say this differently, AGENTS.override.md is best when the override is specific to a directory, environment or process, while AGENTS.md remains the default shared config.\nExample MyApp/ ├── AGENTS.md # Still loaded └── Features/ └── ImageRendering/ ├── AGENTS.md # Ignored └── AGENTS.override.md # Loaded instead How to have multiple agents.md files? If you\u0026rsquo;re dropping specs into your repo and want to view / edit the specs from Xcode, then make sure the files are deselected from the target. Otherwise you end up having the compiler building multiple AGENTS.md files which cause an error in the compiler.\n Multiple commands produce \u0026lsquo;/Users/mfaani/Library/Developer/Xcode/DerivedData/MYAPP-kwlcavcsytcadfajdfslfgiebfljl/Build/Products/Debug-iphonesimulator/MYAPP.app/AGENTS.md\u0026rsquo;\n References Custom instructions with AGENTS.md\n","permalink":"https://mfaani.com/posts/ai/when-to-use-agents-override/","summary":"How is AGENTS.override.md different from AGENTS.md? Why not just edit the current AGENTS.md file instead?\nOverride is usually for temporary or unique cases. Examples:\n Temporarily testing different agent behavior Override an inherited team file without deleting it. Could be for a developer-specific environment or local workflow. When certain files of a group of files require different instructions. So you create a new directory and add an override to be more explicit.","title":"When to Use AGENTS.override.md?"},{"content":"I\u0026rsquo;m running into some issues with the project setup Claude came up for me and I\u0026rsquo;m triaging it now. It\u0026rsquo;s using xcodebuild which prompted me to write this post.\nPrerequisite Highly recommend to acquaint yourself with the following definitions:\n Project Target Scheme Action Destination Product  For that see my stackoverflow answer to: Xcode: What is a target and scheme in plain language?\nWhat is xcodebuild? It\u0026rsquo;s command line tool made by Apple. It allows you to build, test, archive your app. It operates on one or more targets contained in your project, or a scheme contained in your project or workspace\n Notes:\nYour action is either: build, test, profile or archive.\nYour operation is either defined through: a target or a scheme (which has associated targets).\nYour operation is selected from either: a project or workspace.\n Counter intuitively there is no support for running the app through xcodebuild. For that you\u0026rsquo;d have to use simctl to boot the sim, then install and launch the build into the sim. See here for more.\nI\u0026rsquo;m guessing this is because:\nA build is deterministic and stateless:\nproject + scheme + configuration + destination → build products But \u0026ldquo;Run\u0026rdquo; is stateful and interactive, in addition to building it requires a destination, installs, launches, attaches a debugger and maintains a session.\nThis creates ambiguities around targets, debugging, diagnostics, and process handling all (while not impossible yet) difficult for a CLI to manage.\nWhy it\u0026rsquo;s more important to understand xcodebuild now?  AI and agentic tools can build the same desired product through various permutations of target, scheme, configuration, platform, action and launch commands. When AI messes the piecing together of things, having a solid foundation to untangle things becomes critical. AI will mainly use xcodebuild cli to validate that it can build or that tests pass. For more see Building from the Command Line with Xcode FAQ  xcodebuild examples: To build\nxcodebuild -scheme \u0026lt;your_scheme_name\u0026gt; build xcodebuild -scheme tvOS build === BUILD TARGET tvOS OF PROJECT MyProject WITH CONFIGURATION Debug === ... To test\nxcodebuild test [-workspace \u0026lt;your_workspace_name\u0026gt;] [-project \u0026lt;your_project_name\u0026gt;] -scheme \u0026lt;your_scheme_name\u0026gt; -destination \u0026lt;destination-specifier\u0026gt; [-only-testing:\u0026lt;test-identifier\u0026gt;] [-skip-testing:\u0026lt;test-identifier\u0026gt;] xcodebuild defaults: It\u0026rsquo;s worth noting, xcodebuild has a lot of defaults for:\n Xcode version (set by xcode-select) target (first target listed in the project) action (build) configuration (Debug) destination. Note: The default only exists for the build action. For the test action you must specify the destination.  If you don\u0026rsquo;t specify them, then Xcode will just use the default or fail if the action you chose doesn\u0026rsquo;t have a default.\nTest Execution Granularity Agents frequently use xcodebuild test command to test an entire target, class or method.\n# Do not test iOSAppUITests on an iPhone. Uses the `skip-testing` flag xcodebuild test -workspace MyApplication.xcworkspace -scheme iOSApp -destination 'platform=iOS,name=iPhone' -skip-testing:iOSAppUITests # Only testing SecondTestClass' testExampleB in the iOSAppTests unit test. Passes desired scope using the `YourTestTargetName[/TestClass[/TestMethod]]` format. xcodebuild test -workspace MyApplication.xcworkspace -scheme iOSApp -destination 'platform=iOS,name=iPhone' -only-testing:iOSAppTests/SecondTestClass/testExampleB What else can be helpful to know how AI builds? Knowing how the swift command works is also helpful.\nswift build swift test swift test --filter ProfileRepositoryTests swift package resolve swift package update swift package show-dependencies swift package describe ","permalink":"https://mfaani.com/posts/ai/xcodebuild-for-ai/","summary":"I\u0026rsquo;m running into some issues with the project setup Claude came up for me and I\u0026rsquo;m triaging it now. It\u0026rsquo;s using xcodebuild which prompted me to write this post.\nPrerequisite Highly recommend to acquaint yourself with the following definitions:\n Project Target Scheme Action Destination Product  For that see my stackoverflow answer to: Xcode: What is a target and scheme in plain language?\nWhat is xcodebuild? It\u0026rsquo;s command line tool made by Apple.","title":"xcodebuild for AI"},{"content":"People vs 0s and 1s Despite being a fervent user of AI tools, I long struggled to figure out where AI hurts software engineers and where it helps.\nWhile I enjoy working from home and not having to commute, I also enjoy going to the office, seeing people, having lunch with them. Talking about sports, work, the future. Talk about myself. Get to know them and their family. Their culture, roots. I\u0026rsquo;m very much a people\u0026rsquo;s person. I enjoy having slack conversations. Sharing pictures, travel tips, shared passions or pain points about life, being a software engineer. How to raise kids, keep a well maintained house. etc. Even when it\u0026rsquo;s not in person, I prefer to see people\u0026rsquo;s face online. Or at worst at least see an actual profile photo. I also enjoy asking and answering questions on stackoverflow / reddit / slack. I feel more human and worthy when people or more so if a person who\u0026rsquo;s an iOS community leader, spends the time and answers my question and cherish the moment where years later I meet these people in real life for the first time. I take photos with them, hug them. All of it.\n To say this all differently, my mind interacts, perceives and appreciates the same content written by people much differently, naturally and better than one that\u0026rsquo;s written by AI. I guess after I am still human.\n My interactions with these people and more importantly the community is far bigger than just the sum of questions and answer with AI. The emoji reactions, gifs, jokes that AI never truly gets or means make the conversation less pleasant. The profile photo of the person. How that profile photo changes over time means something. A leaderboard, an online competition, a chat conversation amongst multiple people can never happen with AI.\nIt\u0026rsquo;s also that the placement of a conversation, when, who answered it, who commented, all gel better in our brains vs just asking AI. I memorize things a lot better if Matt Smollinger answers a question in #code-help of Philly Cocoa Heads\u0026rsquo;s Slack and then Kotaro Fujita leaves a comment, later Charles interacts with a ➕ or 💯 emoji and my colleague Ashley adds a new answer to it months later.\n  Ready Player One - 2018   An AI conversation is also for the time being is private and 1:1. Nobody sees my conversations, nobody comes back to it later other than myself nor upvotes or comments the question or answers. It feels less alive than real public conversations with multiple people that span a good period of over time. Yet a question asked online, gets a you a human response. Even when you search through Google, you\u0026rsquo;re ultimately land on a page written by a human where you can comment / interact with.\n Additionally with people it\u0026rsquo;s usually a give and get. With AI it\u0026rsquo;s just always get get get. The inferior feeling to AI just makes the relationship even less natural 🙃. And if any point you give back to the AI in the manner Meta employees are currently doing, then you may feel that you\u0026rsquo;re just feeding the monster.\nThe clearest negative example I’ve seen is onboarding. A new employee joining a team does not only need answers; they need people, context, trust, and a sense of belonging. When onboarding becomes mostly \u0026ldquo;ask AI and figure it out,\u0026rdquo; the company may reduce interruptions, but it also removes many of the small human interactions that help someone feel grounded in a new team.\nOur finite brains vs infinite There\u0026rsquo;s been so much happening in our world. We\u0026rsquo;re being pushed to the limits. Unlimited chaos and news. Unlimited friends. Unlimited Apps, Conversations, TV shows, Emails, Groups Ads. Unlimited breach of privacy. Unlimited areas to improve myself, workload and demand. Unlimited competition. And now unlimited (artificial) Intelligence.\nOur brains are not equipped for this sheer amount. We feel over burdened. The amount of context switching that has to happen is far too much. AI has intensified the effect. When I walk into a library, coffee shop or university study area, the number one website on laptops is ChatGPT. People feel frighten to ask, because they\u0026rsquo;ll immediately be asked \u0026ldquo;Did you ask ChatGPT\u0026rdquo;? Time will tell of its impact.\nThe fact that we can do more in shorter time, doesn\u0026rsquo;t necessarily make us smarter. Our brains can only learn at a certain pace. Beyond that it will overflow. I know in the pre-AI era, people copy pasted stuff from stackoverflow, but with AI the pace is 10x faster. We\u0026rsquo;re signing off more work that we don\u0026rsquo;t fully understand. Doing more stuff you don\u0026rsquo;t fully understand adds to your anxiety.\n If you constantly give an 8yr old answers to their math questions, they can\u0026rsquo;t achieve mastery. Mastery requires experimentation, try \u0026amp; error, conversation and patience. AI turns our brains into an ocean with a depth of just 1 inch.\n   BBC quoting referencing a Berkley study and an MIT scholar  A different kind of AI guardrail Not all AI guardrails should be about safety and privacy. Some should be about its emotional impact on humans and human interactions. A recent research by Harvard Kennedy School says:\n They found that daily or frequent use of AI is significantly associated with greater levels of depressive symptoms. The odds of reporting moderate depression were 30% higher among people who used AI each day. The researchers found similar patterns for symptoms of anxiety and irritability. The odds of reporting at least moderate depression were 50% greater for people aged 45-65 who used AI daily.\nThe researchers also found that frequent AI use is that more common among certain groups, including:\n Men Younger adults People in cities People with higher levels of education People with higher incomes   Software Engineers usually check all the above boxes.\nCyber Psychosis  Y Combinator CEO Garry Tan said at a SXSW event that he had \u0026ldquo;cyber psychosis\u0026rdquo; and sleeps four hours a night because he wants to manage the 10 agents he has working on three different projects.\n Agents were constantly running was his own brain 🫤\n  BBC - quoting a Google Employee  Attachment  In addition, AI chatbots and companions are increasingly configured to simulate empathy, offering users nonjudgmental responses and continual validation (Brandtzaeg, P. B., et al., Human Communication Research, Vol. 48, No. 3, 2022opens in new window). The more humanlike an AI appears in language, appearance, and behavior, the more users ascribe consciousness to it (Guingrich, R. E., \u0026amp; Graziano, M. S. A., Frontiers in Psychology, Vol. 15, 2024).\n Unlike a co-worker and similar to social media, usage of AI is engineered to be addictive.\nFinal Thoughts  Acknowledge the hidden problem and lack of a silver bullet. You\u0026rsquo;re not alone in your thoughts. With AI, now more than ever, push for slower pace of development. Otherwise burnouts will follow. Schedule time with your work friends. Have social hours, book clubs, group activities, competitions that aren\u0026rsquo;t just another hackathon. Culture matters more than ever. But then leave work at 5pm and spend time on yourself, family, meditation and exercise. Allow your colleagues to validate their findings with you with ease. Don\u0026rsquo;t just throw them to AI.    Remove noise and constant notifications and AI news checking. The good / important will find their way to you with some delay. As long as you know how to prompt, create a spec and skill, and wire things up then you\u0026rsquo;re mostly good Much like a parent who has lost the connection with their children, some people actually miss being asked about how to do things and how things work. More importantly they miss that human connection. We should feel confident to ask people things. They like providing you with answers and more importantly interacting with another human.  Counter Notes \u0026amp; Arguments 💡  I\u0026rsquo;ve experienced situations where I relayed what AI said to someone in a position of power. Then the next time I asked them a question, they either didn\u0026rsquo;t reply back at all (because they thought I had them replaced with an AI) or sarcastically said \u0026ldquo;why don\u0026rsquo;t you ask AI again?\u0026rdquo;\nWe should stop shaming people for using AI. In many domains — not all — it\u0026rsquo;s faster and less error-prone than most humans. The less we stigmatize it, the more naturally people can use it. I think we all used to be hesitant to say \u0026ldquo;I used AI to figure out the answer\u0026rdquo;. Now I notice the opposite: the best engineers are the ones who use it openly and confidently.\n AI is a lot more than a chatbot. It\u0026rsquo;s more fun when you use it as an agent that does tasks on your behalf, you\u0026rsquo;re just giving it execution / workflow instructions. For those, it\u0026rsquo;s awesome. Even more awesome when you connect to all sorts of other tools using an MCP.\n  From Chatting to Delegating: AI Use Over Time   If you\u0026rsquo;re new to a team or are constantly \u0026lsquo;discovering\u0026rsquo; things around the project using AI, then the AI mental overload will be gruesome. But as you learn about the project, you\u0026rsquo;ll find joy where the AI is no longer just answering questions but is instead \u0026lsquo;agentic\u0026rsquo; and doing the work for you based on your understandings or at other times specifically \u0026lsquo;anchoring\u0026rsquo; actions things based on specs.\n References \u0026amp; Further Research  AI chatbots and digital companions are reshaping emotional connection - Efua Andoh Explained: Generative AI’s environmental impact Forecasting the Economic Effects of AI (Electricity Consumption) Tech leaders say AI means less work - their staff say they work up to 90 hours a week - BBC  ","permalink":"https://mfaani.com/posts/ai/how-the-emotional-and-human-cost-of-ai-varies-based-on-time-and-type-of-usage/","summary":"People vs 0s and 1s Despite being a fervent user of AI tools, I long struggled to figure out where AI hurts software engineers and where it helps.\nWhile I enjoy working from home and not having to commute, I also enjoy going to the office, seeing people, having lunch with them. Talking about sports, work, the future. Talk about myself. Get to know them and their family. Their culture, roots.","title":"How the mental cost of AI can be reduced over time and type of usage?"},{"content":"Intro What triggered the writing of this post were two things:\n1. Prior Experience with State Machine\nWe\u0026rsquo;ve been using state machine for the past few years to simplify a complex onboarding flow. The complexity of our feature rises from:\n having too many conditions that determine the correct state having the conditions scattered.   Some conditions are known at the time of app launch, some are known at the time of onboarding flow commencing, but some others are only known after user identifies their device to us. This adds further complexity to our flow.\n When you\u0026rsquo;re onboarding devices, the conditions could be:\n Is the device owned or being rented? Is the a premium device or basic? How many devices are being onboarded? Is this re-onboarding an already onboarded device? (You can\u0026rsquo;t tell until user identifies their CMMac) Is this replacing a previously onboarded device? What equipment is hard-wired at this location? Can they onboard their device at this location? What instructions should we show to the user for this particular device at this particular location? How did the user identify their device to us? Is this user upgrading or downgrading their tier or keeping it as-is? Is this onboarding flow supported by the app version? Is this device currently owned by or associated to another account? Has the user paid whatever it is needed to pay for this tier of service? Or is the account is in bad state? When the onboarding flow fails, how is the trobleshooting of the flow different per user type, device type, etc.  2. Reading a Book\nRecently I was reading the highly recommended book: Mobile System Design - Manual Vicente Vivo (ByteByteGo). The book and its diagrams have given me a much better foundation on how SOLID principles, UDF, Navigation Layer and State Machine all work together to achieve a more scablable code-base.\nNow let\u0026rsquo;s see how traditional navigation can potentially become challenging:\nThe Problem: Direct View-to-View Navigation Most apps start with views directly pushing to other views: push(to: ViewB()). This seems simple at first, but quickly becomes a maintenance nightmare.\n  View to View Navigation  Cons of Direct Navigation  Tight coupling: ViewA must know how to create ViewB instance, including all its dependencies Scattered state: App state is spread across multiple viewModels, making state correctness nearly impossible Single responsibility violation: Each view handles both its own logic AND navigation logic Difficult testing: Views own both state and navigation, making isolated unit tests extremely hard Deeplink hell: No centralized place to handle deeplinks—logic gets duplicated everywhere Hard to understand flows: Navigation paths are scattered across the codebase. Understanding user journeys requires hunting through multiple files  Nor there\u0026rsquo;s any quick way to figure out: \u0026ldquo;After identifiying a premium rented device, which instruction (screen) does a premium account first see?\u0026rdquo;\n ❕ Unless I know the name of that exact swift file, then I have to trace the flow from its first screen, then look at all the conditions through all the view code i.e. find all the button taps, network callbacks and see which one of them navigates to second screen. And then do the same tracking from the second screen and so on, until I find the first instruction screen.\n The Solution: State Machine + Navigation Layer  💡 With a state maching approach, I can quickly just go to statemachine.swift, find the state, see what are all possible outgoing events from a given state or find all prior states of a given state, then look at my navigation layer and see the view that is associated with that state and find an answer.\nI could also quickly visualize the state, navigation from the very first state all the way to the last state, along with their branching within just two files as opposed to having the need to look through every view/viewModel of every screen in the flow.\n With this approach, instead of views directly navigating to other views, we separate concerns into three layers:\n State Machine: Single source of truth for app state Navigation Layer: Observes state changes and handles all navigation Views/ViewModels: Send events to state machine, display UI based on state    State Machine Based Navigation  All state is read-only outside the state machine. Changes happen only by sending events. The state machine processes events, updates state, and emits commands that the navigation layer reacts to.\nPros of This Approach  Centralized navigation: Read one file to understand all possible navigation paths—an x-ray view of your app\u0026rsquo;s architecture Decoupled views: Views don\u0026rsquo;t know what comes next. They just send events and display state Single source of truth: App state lives in one place, ensuring correctness Easy testing: Test ViewModels, State Machine, and Navigation Layer in complete isolation Trivial deeplinks: Start from any state: stateD instead of stateA Conditional flows: One event (eventR) can lead to different states (stateX or stateY) based on user type (basic vs premium) Simple changes: Modify navigation transitions in one centralized location   💡 This follows Unidirectional Data Flow (UDF): events flow in one direction only. State can\u0026rsquo;t be changed from anywhere—only through controlled events to the state machine. Navigation can\u0026rsquo;t be triggered from anywhere—only through the navigation layer reacting to state.\n Common Pitfalls  Bypassing the state machine: Views directly changing app state instead of sending events breaks the single source of truth Direct navigation: Views calling navigation methods directly instead of letting the navigation layer react to state changes Missing events: Views not sending events on user interactions breaks the unidirectional flow  Example For a high level example see Part 2 - State Driven Navigation: Tutorial\nShout out Special shout out to Tom Insam for helping me understand these concepts better and be able to articulate them.\nReferences  Unidirectional Data Flow | Livefront Talks 2023 Unidirectional Data Flow | Google Developer North America Mobile System Design - Manual Vicente Vivo (ByteByteGo)  ","permalink":"https://mfaani.com/posts/architecture/1-statedriven-navigation-overview/","summary":"Intro What triggered the writing of this post were two things:\n1. Prior Experience with State Machine\nWe\u0026rsquo;ve been using state machine for the past few years to simplify a complex onboarding flow. The complexity of our feature rises from:\n having too many conditions that determine the correct state having the conditions scattered.   Some conditions are known at the time of app launch, some are known at the time of onboarding flow commencing, but some others are only known after user identifies their device to us.","title":"Part 1 - State Drive Nagiation: Overview"},{"content":" 💡 Read Part 1 first to understand the concept. This post shows you exactly how to implement it.\n What We\u0026rsquo;re Building A simple 3-screen onboarding flow:\n Account Loader Screen → Loads the user\u0026rsquo;s account in a loader screen Welcome Screen → user taps \u0026ldquo;Get Started\u0026rdquo; Device Identification Screen → user enters their devices CMMac  Let\u0026rsquo;s build this with state-driven navigation, step by step.\n1 - States States represent every screen your user can be on:\nenum OnboardingState { case accountLoader case welcome case deviceIdentification } Three screens = three states.\n2 - Events Events are things that happen in your app:\nenum OnboardingEvent { case accountLoaderFinished case startFlowTapped case deviceWasIdentified }  Events could be triggered by user actions or network calls or some other app event. Note: Your events aren\u0026rsquo;t necessarily network call finished. It\u0026rsquo;s more of \u0026ldquo;Is the screen tied to this state done?\u0026rdquo;. You may have multiple network calls that need finishing. In that case your state only changes when all network calls are finished.\n 3 - State Machine State machine is responsible for:\n Hold the current state Process events and update state  class OnboardingStateMachine: ObservableObject { @Published private(set) var currentState: OnboardingState = .accountLoader func handleEvent(_ event: OnboardingEvent) { switch (currentState, event) { case (.accountLoader, .accountLoaderFinished): currentState = .welcome case (.welcome, .startFlowTapped): currentState = .deviceIdentification case (.deviceIdentification, .deviceWasIdentified): // this state isn\u0026#39;t handled in this post.  // currentState = .performDeviceChecks default: print(\u0026#34;⚠️ Invalid event \\(event)for state \\(currentState)\u0026#34;) } } }  💡 Only the state machine can change currentState. It\u0026rsquo;s private(set). Everyone else just reads it.\n 4 - Navigation Layer The navigator is subscribed to the state machine and shows the right view:\nstruct OnboardingNavigator: View { @ObservedObject var stateMachine: OnboardingStateMachine var body: some View { ZStack { switch stateMachine.currentState { case .accountLoader: AccountLoaderView(stateMachine: stateMachine) .transition(.opacity) case .welcome: WelcomeView(stateMachine: stateMachine) .transition(.move(edge: .trailing)) case .deviceIdentification: DeviceIdentification(stateMachine: stateMachine) .transition(.move(edge: .trailing)) } } .animation(.easeInOut, value: stateMachine.currentState) } } The navigator doesn\u0026rsquo;t decide what to show. It just reacts to state changes. That\u0026rsquo;s the magic.\n5 - Views Views are dumb. They just:\n Display UI Send events when things happen  AccountLoaderView:\nstruct AccountLoaderView: View { let stateMachine: OnboardingStateMachine var body: some View { VStack { Text(\u0026#34;Loader\u0026#34;) } .task { // Simulate loading await getAccount() stateMachine.handleEvent(.accountLoaderFinished) } } } WelcomeView:\nstruct WelcomeView: View { let stateMachine: OnboardingStateMachine var body: some View { VStack { Text(\u0026#34;Welcome!\u0026#34;) Button(\u0026#34;Start Flow\u0026#34;) { stateMachine.handleEvent(.startFlowTapped) } } } }  💡 Notice: Views never navigate directly. They just send events. The state machine decides what happens next.\n Zero direct navigation. Zero coupling. Each piece does one job.\nDeeplinks You just have to initialize the state machine with that state:\nlet stateMachine = OnboardingStateMachine() stateMachine.currentState = .deviceIdentification // Start at login This would be really difficult with a traditional view to view architecture.\nTesting Each piece can be tested in complete isolation:\nTest State Machine:\nfunc testAccountLoaderToWelcomeScreen() { let stateMachine = OnboardingStateMachine() stateMachine.currentState = .accountLoader stateMachine.handleEvent(.accountLoaderFinished) XCTAssertEqual(stateMachine.currentState, .welcome) } Test View Logic:\nfunc testWelcomeViewSendsEvent() { let stateMachine = MockStateMachine() let view = WelcomeView(stateMachine: stateMachine) // Simulate button tap view.getStartedButton.tap() XCTAssertTrue(stateMachine.receivedEvent(.startFlowTapped)) } No need to actually navigate. Just verify events are sent and state changes correctly.\nHow to make this more real? This post is intentionally simple. Real apps add:\n ViewModels for business logic Async operations (network calls, etc.) Conditional flows (different paths for different users tiers) Errors (.loading, .error(message))  But the pattern stays the same: Events → State Machine → Navigation Layer → Views.\n 💡 Errors are often best handled within the viewModels. Example, errors within AccountLoader state, should just get handled within the AccountLoaderViewModel, unless it\u0026rsquo;s something that effects the entire app state like:\n access token being revoked and getting logged out network connectivity being down. servers being down.   Shout outs Shout outs to Jack Wright for helping me figure out a lot of nuances about State Machines and how to build this at scale.\n","permalink":"https://mfaani.com/posts/architecture/2-statedriven-navigation-tutorial/","summary":"💡 Read Part 1 first to understand the concept. This post shows you exactly how to implement it.\n What We\u0026rsquo;re Building A simple 3-screen onboarding flow:\n Account Loader Screen → Loads the user\u0026rsquo;s account in a loader screen Welcome Screen → user taps \u0026ldquo;Get Started\u0026rdquo; Device Identification Screen → user enters their devices CMMac  Let\u0026rsquo;s build this with state-driven navigation, step by step.\n1 - States States represent every screen your user can be on:","title":"Part 2 - State Driven Navigation: Tutorial"},{"content":"If you captured any state before doing some async work in your actor, then by the time your task is resumed, your captured state may be stale. This is what actor reentrancy is about. Because of this, you should avoid capturing things that are subject to change before your task is suspended. Instead, only retrieve values after your task is resumed.\nThe term \u0026ldquo;reentrant\u0026rdquo; literally means \u0026ldquo;able to be entered again.\u0026rdquo; When an actor method suspends at an await point, the actor isn\u0026rsquo;t locked, other methods can enter and execute, potentially modifying the actor\u0026rsquo;s state. When your original method resumes, it has \u0026ldquo;reentered\u0026rdquo; an actor that may now be in a different state than when it left.\nCode Let\u0026rsquo;s just dive into code samples:\nExample 1 - No Async work import Foundation protocol Incrementer { // increments the value of a dictionary.  func increment(into key: String) async } actor Cacher: Incrementer { var cache: [String: Int] = [:] { didSet { print(cache) } } func increment(into key: String) { let currentValue = cache[key] ?? 0 cache[key] = currentValue + 1 } } /// runs 10 concurrent threads against the `increment` function func runConcurrently(incrementer: Incrementer) { DispatchQueue.concurrentPerform(iterations: 10) { _ in Task { await incrementer.increment(into: \u0026#34;key\u0026#34;) } } } runConcurrently(incrementer: Cacher()) /* [\u0026#34;key\u0026#34;: 1] [\u0026#34;key\u0026#34;: 2] [\u0026#34;key\u0026#34;: 3] [\u0026#34;key\u0026#34;: 4] [\u0026#34;key\u0026#34;: 5] [\u0026#34;key\u0026#34;: 6] [\u0026#34;key\u0026#34;: 7] [\u0026#34;key\u0026#34;: 8] [\u0026#34;key\u0026#34;: 9] [\u0026#34;key\u0026#34;: 10] */ Example 2 - Async work with assumptions actor AsyncCacher: Incrementer { var cache: [String: Int] = [:] { didSet { print(cache) } } // Adding an artificial delay to make it async func increment(into key: String) async { /// ⚠️ assumption. We\u0026#39;re assuming `currentValue` won\u0026#39;t change when the task is suspended.  let currentValue = cache[key] ?? 0 /// Whenever an `await` occurs, it means that the function can be suspended at this point. /// It gives up its CPU so _other_ code in the program can execute, which affects the overall program state. /// **Carryying assumptions** about state \u0026#39;across an await\u0026#39; can have your code end up with a potential bug. try? await Task.sleep(nanoseconds: 1_000_000) cache[key] = currentValue + 1 } } runConcurrently(incrementer: AsyncCacher()) /* [\u0026#34;key\u0026#34;: 1] [\u0026#34;key\u0026#34;: 1] [\u0026#34;key\u0026#34;: 1] [\u0026#34;key\u0026#34;: 1] [\u0026#34;key\u0026#34;: 1] [\u0026#34;key\u0026#34;: 1] [\u0026#34;key\u0026#34;: 1] [\u0026#34;key\u0026#34;: 1] [\u0026#34;key\u0026#34;: 1] [\u0026#34;key\u0026#34;: 1] */ Example 3 - Async work without any assumptions actor GoodAsyncCacher: Incrementer { var cache: [String: Int] = [:] { didSet { print(cache) } } // Adding an artificial delay to make it async func increment(into key: String) async { /// 👌 No assumption was _carried_ from before the await to after it.  try? await Task.sleep(nanoseconds: 1_000_000_000) let currentValue = cache[key] ?? 0 cache[key] = currentValue + 1 } } runConcurrently(incrementer: GoodAsyncCacher()) /* [\u0026#34;key\u0026#34;: 1] [\u0026#34;key\u0026#34;: 2] [\u0026#34;key\u0026#34;: 3] [\u0026#34;key\u0026#34;: 4] [\u0026#34;key\u0026#34;: 5] [\u0026#34;key\u0026#34;: 6] [\u0026#34;key\u0026#34;: 7] [\u0026#34;key\u0026#34;: 8] [\u0026#34;key\u0026#34;: 9] [\u0026#34;key\u0026#34;: 10] */ // Required so asynchronous code is ran in Playgrounds RunLoop.main.run() Aren\u0026rsquo;t classes also reentrant? Why is reentrancy only being discussed for actors and not classes? Good question. Both Swift actors and classes can indeed handle multiple invocations, but there\u0026rsquo;s an important distinction in how they manage concurrency.\nIn Swift, actors are explicitly designed to be reentrant - they can have multiple function calls in progress at once, but with an important safeguard: these calls are serialized through the actor\u0026rsquo;s executor. This means the actor processes one task at a time on its isolated state, preventing data races while allowing reentrancy. Classes, on the other hand, are technically reentrant in the sense that multiple threads can call methods on the same class instance simultaneously. However, classes provide no built-in protection against concurrent access to their state. This means that while you can reenter a class from multiple threads, you need to manually implement synchronization mechanisms (like locks or dispatch queues) to make this safe, otherwise it will lead to crashes 💥.\nSo while both can be considered \u0026ldquo;reentrant\u0026rdquo; in some sense, actors provide automatic serialization of access to mutable state, making safe reentrancy their default behavior, whereas classes require explicit synchronization code to achieve safe reentrancy in multithreaded environments.\nSummary  Example 1 has nothing async. It\u0026rsquo;s simple and correct. Example 2 has an async func + carrying assumption across the await. This is bad. Example 3 has an async func + is not carrying an assumption across the await. This is good.   Basically if await is used in your actors, then the actor may experience reentrancy.\nTo work around reentrancy avoid carrying any assumptions from before an await (suspension) to after it.\n  Classes are also reentrant, but not serialized like how actors are. This can lead to crashes with classes. For actors crashes won\u0026rsquo;t occur, just that data before an await may have gone stale once you\u0026rsquo;ve resumed.  References This is explained in depth in:\n WWDC 2021 - Protect mutable state with Swift actors Donnay Walls - Actor reentrancy in Swift explained  Apple - Suspention Points  ","permalink":"https://mfaani.com/posts/swift/concurrency/what-does-actor-reentrancy-mean/","summary":"If you captured any state before doing some async work in your actor, then by the time your task is resumed, your captured state may be stale. This is what actor reentrancy is about. Because of this, you should avoid capturing things that are subject to change before your task is suspended. Instead, only retrieve values after your task is resumed.\nThe term \u0026ldquo;reentrant\u0026rdquo; literally means \u0026ldquo;able to be entered again.","title":"What does actor reentrancy mean?"},{"content":"Cursor-Based Pagination: Handling Deletions, Collisions \u0026amp; Secure Cursors Pagination requires subtle implementation for a smooth infinite scroll. In this post I’ll share what I learned while reading on cursor‑based pagination for a real‑time feed – how it differs from the classic offset/limit approach, how it deals with deletions and timestamp collisions, what goes into a cursor (spoiler: it’s not a random token), why you should (or shouldn’t) protect cursors from tampering, what to cache and how to manage stale data.\nWhy ditch offset/limit? The traditional approach to pagination is to supply a page and limit (or offset and limit) and let the database do the counting:\n-- Page 1: first 10 entries SELECT * FROM entries ORDER BY id ASC LIMIT 10 OFFSET 0; -- Page 100: entries 991–1000 SELECT * FROM entries ORDER BY id ASC LIMIT 10 OFFSET 990; Offset pagination is easy to understand and works well for small, mostly static datasets (Ex 30 countries is a query which its result doesn\u0026rsquo;t change every day).\nAs your table grows, performance degrades: each new page requires the database to read and discard all rows before the offset. Even worse, if new items are inserted or deleted between page requests, users will see duplicates or skipped rows.\n Cursor‑based pagination avoids these problems by using a pointer rather than an offset.\n Cursor‑based pagination in a nutshell A cursor represents the last item of the previous page. When the client asks for the next page, it sends back that cursor. The server uses the cursor to continue the WHERE clause instead of calculating an offset. The result is consistent performance and stable ordering even when data changes. Here’s a basic example using a composite cursor (created_at, id):\n-- First page SELECT * FROM entries ORDER BY created_at, id LIMIT 20; -- Next page (client sends cursor values from the last item) SELECT * FROM entries WHERE (created_at, id) \u0026gt; (\u0026#39;2025‑01‑01T10:00:00Z\u0026#39;, 12345) 👈👈👈 You\u0026#39;re querying for anything that\u0026#39;s its created_at is greater than the targeted date. The targeted date is derived from the cursor. ORDER BY created_at, id LIMIT 20; This technique is sometimes called keyset pagination. Because the database seeks directly to the start point instead of scanning thousands of rows, it scales much better than offset pagination. Platforms such as GitHub, Twitter and Facebook use variants of this approach for their APIs.\nDoesn\u0026rsquo;t size effect WHERE (created_at, id) \u0026gt; ('2025‑01‑01T10:00:00Z', 12345 ? It does. But not in a linear fashion. It\u0026rsquo;s because it\u0026rsquo;s using an Index. Indexes are implemented using B Trees. Finding items in a B Tree has a time complexitiy of log(n)\nWhat happens if the cursor’s row is deleted? One of the first questions I had was what happens if the item the cursor refers to gets deleted between page requests. With offset pagination this may break if the server calculates the next page based on the row’s position. With cursor pagination the answer is simple: nothing breaks. When the server builds the next page it uses the sort key values contained in the cursor, not the original row. If the row has been deleted, the WHERE clause still compares those values and starts from the next available record.\n If the cursor points to a deleted record, \u0026ldquo;the pagination continues normally – the query WHERE id \u0026gt; deleted_id will start from the next available record\u0026rdquo; from — Uptrace\n Handling timestamp collisions Real‑world feeds often sort by a timestamp plus a tie‑breaker. Multiple items can share the same created_at down to the microsecond, especially at Facebook‑scale. If you only sort on created_at, the database has no deterministic way to order rows with identical timestamps and you may miss or duplicate items between pages. The fix is to add a unique tie‑breaker, typically the primary key.\n Instead of WHERE created_at \u0026gt; …, use WHERE (created_at, id) \u0026gt; ('2023‑01‑01', 12345). Because Date isn’t unique we should use another field to handle ties so that \u0026ldquo;we don’t miss or duplicate items when paginating\u0026rdquo; In practice the cursor stores both the timestamp and the ID, and the ORDER BY clause uses both fields. This ensures a strict total ordering even when timestamps collide. - Upgrate\n What goes into a cursor? A common misconception is that a cursor is just an opaque random token. It\u0026rsquo;s not random. In reality, a cursor usually encodes the sort values of the last item on the previous page. Stainless’s API design guide states that\n a cursor is typically a \u0026ldquo;base64‑encoded value representing the sort key and unique ID of the last item on the previous page\u0026rdquo;.\n For example, a cursor could contain { \u0026quot;created_at\u0026quot;: \u0026quot;2024‑01‑01T12:00:00Z\u0026quot;, \u0026quot;id\u0026quot;: 123 } encoded in Base64. When the client requests the next page it sends back this encoded string; the server decodes it to obtain the original sort values and uses them in the WHERE clause.\nBenefits of Encoding and signing cursors  Most APIs treat the cursor as opaque by Base64‑encoding the JSON and sometimes adding a signature to prevent tampering. Keeps the API flexible (you can change the internal structure without breaking clients) Avoids exposing implementation details. The client simply stores the cursor string and sends it back verbatim. Makes it URL‑safe You can optionally sign or encrypt the encoded cursor for extra security. Signing the cursor (for example, using an HMAC with a secret key) allows the server to detect if a client has modified the cursor’s content. If the signature doesn’t match, the server can reject the request.   A signed cursor helps prevent a determined client from jumping arbitrarily forward or backward in your feed. Without a signature, a client could decode the cursor, tweak the id or created_at, re‑encode it and request any slice of your data. This defeats rate‑limiting and makes scraping much easier. Signing forces clients to use only cursors signed by your server. If you don’t care about protecting your data or the cost of random access, you might skip signing – but be aware that cursor pagination then offers no more security than offset pagination.\n Why protect your cursors? There are several reasons to validate or sign cursors instead of letting clients freely modify them:\n Prevent deep scraping: Without protection, a client can iteratively adjust the cursor values and scrape your entire dataset quickly. By signing the cursor, the server ensures the client can only follow the legitimate path through the feed. Enforce rate limits: A signed cursor forces users to page sequentially. If a user tries to jump to page 5,000 by guessing a cursor value, the signature will be invalid. Hide internal implementation: Encoding the cursor hides internal fields (such as ranking scores, user IDs or scores) so clients can’t reverse‑engineer your ranking algorithm or infer sensitive data. Ensure data integrity: When the cursor includes a signature, the server can detect if a client has tampered with the sort values and reject the request. You may decide not to sign cursors if you operate in a trusted environment or the data isn’t sensitive. In such cases, cursor pagination behaves similarly to offset pagination – clients can jump around freely by modifying the cursor values. The main benefit you still get is performance.  What does base-64 encoding mean? You serialize a small object -\u0026gt; turn it into bytes -\u0026gt; then Base64-encode those bytes -\u0026gt; producing one opaque string. Just as a demonstration:\n{\u0026quot;t\u0026quot;:\u0026quot;2025-11-06T17:00:00.000Z\u0026quot;,\u0026quot;id\u0026quot;:902} can get converted to:\neyJ0IjoiMjAyNS0xMS0wNlQxNzowMDowMC4wMDBaIiwiaWQiOjkwMn0 So client is to treat it as opaque because, when you give a client:\nafter=1730929182 They’ll try to guess other values, poke around, hack pagination. But if you give them:\nafter=eyJpZCI6OTAyLCJjcmVhdGVkQXQiOiIyMDI1LTExLTA2In0= Clients naturally treat it as a black box. This restricsts clients from mucking with the cursor.\nFor a Facebook feed, should I cache the page information? How about each item in the feed? I\u0026rsquo;m asking because text, images, like count may all change. Tbh I\u0026rsquo;m not sure. But I\u0026rsquo;d say you\u0026rsquo;d cache page respones for any cursor. The only response you shouldn\u0026rsquo;t cache is the resonse for when you don\u0026rsquo;t pass a cursor (to query the lastest feed for now) Then for each item, you\u0026rsquo;d still cache them. Then refresh them whenver your product team decides you to refresh.\n Caching can be done for either purposes:\n To avoid querying the data until cache is expired again. Ex: You won\u0026rsquo;t refresh until cache is expired in 20 days To have something to show before you update the data again. Ex: You don\u0026rsquo;t have a specific date in mind. Just based on certain user actions / product decisions, you refresh the cache.   Other than that, it\u0026rsquo;s best if you decouple \u0026ldquo;showing a view\u0026rdquo; from \u0026ldquo;when query the data associated to that view\u0026rdquo;\nI know you typically query data just before showing a view, you should still do that, but your architecture should allow other events to trigger a fetching of data. And your view to be responsive to when other data flows in.\n Basically a screen is always subscribed to changes of the cache. Whenever a network request returns, cache is updated. Network request can happen from any where at any time. Examples:\n1. user opened new page or some pre-fetching (upon flow or app start up).\n2. user pulled down to refresh or tapped on some button.\n3. some timer fired and it causes an data refresh.\n4. etc.  Got it. What happens when user had the app last opened 6 days ago? Basically what do you do when your data is stale? Great question. So at this point the user\u0026rsquo;s next cursor is pointing to something that was from six days or more.\nThe app at this point must not pass any cursors and just fetch the latest. Remember fetching the latest is done by not passing any cursor.\nSo suppose most recent page that was retrieved by the app was cursorF from page6 which was retreived six days ago. PageA - PageE haven\u0026rsquo;t been retrieved by the app yet.\nPageA PageB PageC PageD PageE PageF (6 days ago) Here\u0026rsquo;s what happens then:\n User fetches PageA. Puts that in its local storage. User may still keep PageF in its storage. It depends on your eviction policy and what the app considers as acceptable feed. For our sake let\u0026rsquo;s say the app keeps it. So now your dataStore = [PageA, PageF]. This does NOT mean the app will show PageA and then PageF. The app will not have a gap. If user doesn\u0026rsquo;t scroll further down from PageA, then things are all good. If user scrolls down past the last item within PageA, then the app will query pageB. At the point the user will see a loading state for anything that\u0026rsquo;s from pageB.   💡 You need some mechanism to know that next cursor should be fetched when you reach at the end of your current cursor and NOT at the end of all items.\n To achieve that you could either do model it either:\nA - Index Based allItems = [itemA1, itemA2, itemA3, itemF1, itemF2, itemF3] indexOfLastItemfromLastCursor = 2 (pointing to itemA3) B - Alternate datasource allItems = [itemA1, itemA2, itemA3, itemF1, itemF2, itemF3] currentCursorItems = [itemA1, itemA2, itemA3] // just fetch next cursor as soon as itemA3 is about to be viewed.  C - Item based allItems = [itemA1, itemA2, itemA3, itemF1, itemF2, itemF3] lastItemFromCursor = itemA3 // more simpler approach D - Remove old items. Refetch them again if needed. (not efficient) allItems = [itemA1, itemA2, itemA3] All approaches can work, but generally speaking ID based approaches are safer vs index based approaches. Because if something in the indexing is changed, then you can easily become off by 1 or so.\nAnd let\u0026rsquo;s just say for the past 5 days, there were 46 new items in the feed. Each page has a size of 10. When you pull down pageA, pageB, pageC, pageD, you get 10 items for each.\nFor pageE you also get 10 items. The server won\u0026rsquo;t be smart and only return 6 items. It doesn\u0026rsquo;t care if you have previously downloaded 4 of the items, it\u0026rsquo;s not worth the book-keeping. The 4 items that you previously downloaded should then just get retrieved from cache.\n 💡 At any point of time, your items array can be stale, even if you just opened the app and scrolled down and 30 seconds passed then. If you\u0026rsquo;re following 3000 users and they make 10 actions a day then there\u0026rsquo;s an action happening every 864000 / 30000 (28.8 seconds) then just after 30 seconds it\u0026rsquo;s very likely that the tip of the feed has already changed. tldr your feed gets stale a lot quicker than 6 weeks!\n I also must admit, stale data can mean either:\n This message that you have, isn\u0026rsquo;t the latest. The message was changed. \u0026lt;\u0026ndash; When users update their post The stream of data that you have is valid. No single item of the feed has changed. It\u0026rsquo;s just that there are lots of new items added to the feed. \u0026lt;\u0026ndash; when there are new items in the feed.  When reading things online about handling caching / stale data it\u0026rsquo;s good to know which of the above the post is focused on. They\u0026rsquo;re similar but not the same.\nImage loading and Fast Scrolling Build some mechanism in place that cancels image loading if the user was doing a fast scrolling and passing through rows quickly.\nPutting it all together To build a robust cursor‑based pagination API:\n Define a stable sort order. Use immutable columns for ordering. If the primary column isn’t unique, include a secondary key like id. Construct the cursor from the last item’s sort values. Serialize them (e.g., as JSON) and Base64‑encode to produce an opaque string. Include a signature if you need tamper protection. Use the cursor in your next query. Decode the cursor, extract the sort values, and build the WHERE clause using (sort_key1, sort_key2) \u0026gt; (cursor_key1, cursor_key2) semantics. Handle deletions gracefully. If the row referenced by the cursor is deleted, the WHERE clause still works because it uses only the sort values; the next page starts from the first record greater than the deleted one. Validate incoming cursors. Reject malformed or even expired cursors and return an appropriate error or start from the beginning. If the last fetched cursor is old, fetch the latest feed without passing any cursor, then resume normal pagination as the user scrolls. Id based pointers are safer than index based pointers. Caching is done for either purpose not making the same request again or for having a filler of old data until you get the latest. Views should observe changes to cached. Caches should get updated whenever a network call happens. Network calls can have various triggers (other than page loading).  Conclusion Cursor‑based pagination isn’t magic, but it can make your APIs faster and more consistent when dealing with large, mutable datasets. The key is understanding what the cursor actually represents. A good cursor encodes the exact position in your ordered data (including a tie‑breaker) and optionally carries a signature so only the server can issue valid cursors. By handling deletions gracefully, using composite keys to avoid timestamp collisions, and protecting your cursors when necessary, you can build pagination that scales nicely.\nHave you implemented cursor‑based pagination in your projects? What challenges did you face? Feel free to share your experience or questions – I’d love to hear from you.\nReferences   Cursor Pagination for PostgreSQL \u0026amp; MySQL: Complete Developer Guide 2025\n  Understanding Cursor Pagination and Why It\u0026rsquo;s So Fast (Deep Dive)\n  Stainless - How to Implement REST API Pagination: Offset, Cursor, Keyset\n  How To Implement Offset and Cursor-Based Pagination in EF Core - DEV Community\n  Mobile System Design - Manual Vicente Vivo (ByteByteGo)\n  ","permalink":"https://mfaani.com/posts/interviewing/system-design/pagination/","summary":"Cursor-Based Pagination: Handling Deletions, Collisions \u0026amp; Secure Cursors Pagination requires subtle implementation for a smooth infinite scroll. In this post I’ll share what I learned while reading on cursor‑based pagination for a real‑time feed – how it differs from the classic offset/limit approach, how it deals with deletions and timestamp collisions, what goes into a cursor (spoiler: it’s not a random token), why you should (or shouldn’t) protect cursors from tampering, what to cache and how to manage stale data.","title":"Systems Design - Pagination"},{"content":"I used the following tutorial series from Apple on \u0026lsquo;Capturing and Displaying Photos\u0026rsquo;. They\u0026rsquo;re great. Just that it took me a bit to be able to piece together how components from AVFoundation work together.\nWorking with AVFoundation isn\u0026rsquo;t really the kind where you read documentation and then can piece things together. It\u0026rsquo;s complicated. You might understand an individual step, but won\u0026rsquo;t understand when it should be done. There\u0026rsquo;s just too many components and while it\u0026rsquo;s not low-level, it\u0026rsquo;s certainly a different kind of iOS. This is why I think using good tutorials are crucial.\nHigh Level Anatomy of a camera capturing session.  Input: The device that can give you video, photo or audio. Examples: Rear-facing camera, front-facing camera or built-in mic. Output: The actual output from the device. Can be a photo, video stored to disk. Or it can be something that you process against. Examples of processing are: detecting faces, scanning barcodes, or applying filters. The output also provides methods to capture photos or record videos. Preview: A preview layer that is a mirror of the camera input. This is also known as the viewfinder. Stream: The output of the camera isn\u0026rsquo;t a singular image. It\u0026rsquo;s a stream of frames. You usually capture a single frame of it as a photo or a series of it as video. Though modern cameras often merge frames because of low light, or to reduce noise, etc. The stream is also provided to the preview so you can see. Session: The object which gives you an interface to add/remove inputs or outputs and setup configuration of the session. Start session or end it, etc.   [ Camera Device (Input) ] │ ▼ ┌────────────────────────┐ │ AVCaptureSession │ ←─ central controller └────────────────────────┘ │ (Stream of Frames) │ ┌──────────┼───────────┐ ▼ ▼ [ AVCaptureVideoPreviewLayer ] [ AVCaptureOutput ] (Live Preview UI) │ ▼ ┌───────────────────────────────────────────┐ │ Choose one or more outputs: │ │ │ │ ▸ AVCaptureMovieFileOutput (record) │ │ ▸ AVCaptureVideoDataOutput (process) │ │ ▸ AVCapturePhotoOutput (take photo)│ └───────────────────────────────────────────┘ Some interesting notes from docs about capturing a photo:\n 💡 When you take a photo, you want to capture an image with the highest possible resolution. This contrasts with the preview images, which tend to have a lower resolution to facilitate rapidly updating previews.\n  💡You might wonder why capturePhoto doesn’t just return the photo. That’s because capturing a photo takes time: the camera may need to focus, or wait for the flash, and then there’s the exposure time. The capturePhoto method is asynchronous, with the captured photo typically arriving a short time after you tap or click the shutter button.\n There were two other similar components that took me a bit to distinguish:\n   Role Scope Examples     Capture Settings Changes how the photo is captured. Should use flashlight, photo quality, codec type, etc.   Stream Settings Changes how the stream is provided from the input to the output video orientation, whether the video should be mirrored (for front-facing cameras), and stabilization settings, can also disable microphone    In short:\n   Component Role     AVCaptureSession Central pipeline that connects inputs, outputs, and preview layers   AVCaptureDeviceInput Camera (or mic) source input   AVCaptureVideoPreviewLayer Visual live feed for user-facing preview   AVCaptureOutput Output target for the capture session   AVCapturePhotoSettings Changes how the photo is taken   AVCaptureConnection Changes how the stream is provided from the input to the output    To take this up a notch:\n  From Apple Docs - The photo nicely highlights how you can combine inputs to create different outputs.  Congratulations. Now you know how a photo a taken. But that\u0026rsquo;s not enough for you to be able to show it in Swift. You need to jump through a few more hoops before you can render it in an image. Continue reading\nAVCapturePhoto vs CGImage vs Image vs PHAsset  AVCapturePhoto is the raw output from the camera. It\u0026rsquo;s not displayable. PHAsset: A Photos framework object that represents an image or video in the user’s Photos library. It’s just a reference — it doesn’t contain pixel data. You fetch the actual bitmap/video using PHImageManager or PHAssetResourceManager. CGImage: Is the bit map. You can draw it in a context, or pass it to SwiftUI’s Image. Image (SwiftUI): This is just a view. It’s the UI representation of something visual, like a CGImage, or a named asset. An Image doesn’t store pixel data — it simply renders what you give it in SwiftUI.  The photo (raw model) is something with an encoding stored in disk along with some association to the Photos library (manged by PHAsset), the SwiftUI Image is a view in your app that can\u0026rsquo;t process the AVCapturePhoto, it needs the photo in the form of a CGImage before it can process it.\nTypical flow: AVCapturePhoto -\u0026gt; (decode) -\u0026gt; CGImage -\u0026gt; Image (SwiftUI) -\u0026gt; display, or save to Photos -\u0026gt; results in a PHAsset for later retrieval.\nOther Notes Viewfinder Also the term Viewfinder is used for the component of a camera where the photographer looks through to view a scene.\nThis was interesting to know because otherwise you might be asking yourself, what is there to find? Then I realized it\u0026rsquo;s a common photography term.\n  An Optical View Finder is used to help the photographer view a scene  Can I test camera using the simulator? Yes/No.\n For the iOS simulator you can\u0026rsquo;t — unless you use Rocketsim.app For the macOS, you\u0026rsquo;re building the app straight into your mac. This allows you to access the front camera.  ⚡️ Antoine from Rocketsim.app sent me this email. This is fantastic. Check his article!\n  You can use RocketSim to connect to a camera while using Simulator. The feature is free for now...  Apple\u0026rsquo;s sample code can be a bit overwhelming While I did say the sample code was great, often times the sample code does \u0026lsquo;everything\u0026rsquo;. This means the sample code may overdo things while you just wanna learn the basics. The sample code may also not explain why it does everything. Also they may miss a thing or two or have mistakes in it. The worst ones are code path that you can\u0026rsquo;t hit. You start to think why did Apple add this. My guess is that it\u0026rsquo;s their way of documenting things. It doesn\u0026rsquo;t matter to them if the code path isn\u0026rsquo;t hit.\nExample: This code never gets hit when I capture a photo. I\u0026rsquo;m guessing it\u0026rsquo;s not being hit because the album I used is smartAlbumUserLibrary which already adds every image taken. ‌But I\u0026rsquo;m not sure.\nif let albumChangeRequest = PHAssetCollectionChangeRequest(for: assetCollection), assetCollection.canPerform(.addContent) { let fastEnumeration = NSArray(array: [assetPlaceholder]) albumChangeRequest.addAssets(fastEnumeration) } ","permalink":"https://mfaani.com/posts/ios/swiftui-camera-learnings/","summary":"I used the following tutorial series from Apple on \u0026lsquo;Capturing and Displaying Photos\u0026rsquo;. They\u0026rsquo;re great. Just that it took me a bit to be able to piece together how components from AVFoundation work together.\nWorking with AVFoundation isn\u0026rsquo;t really the kind where you read documentation and then can piece things together. It\u0026rsquo;s complicated. You might understand an individual step, but won\u0026rsquo;t understand when it should be done. There\u0026rsquo;s just too many components and while it\u0026rsquo;s not low-level, it\u0026rsquo;s certainly a different kind of iOS.","title":"High Level Anatomy of a Camera Capturing Session"},{"content":"A suspension point doesn\u0026rsquo;t mean the current thread is blocked. Nor it means the current actor is blocked. All it means is that the current task / function is blocked / suspended / waiting for an asynchronous task to finish.\n@MainActor func foo() async { doA() let b = await doB() doC() } When you get to doB(), then the MainActor / Main Thread are not suspended. Other tasks can be enqueued and executed on the main thread.\nIt\u0026rsquo;s just the foo function that gets suspended. Once doB() is complete, then the foo function / task is resumed.\n","permalink":"https://mfaani.com/posts/swift/concurrency/what-is-a-suspension-point/","summary":"A suspension point doesn\u0026rsquo;t mean the current thread is blocked. Nor it means the current actor is blocked. All it means is that the current task / function is blocked / suspended / waiting for an asynchronous task to finish.\n@MainActor func foo() async { doA() let b = await doB() doC() } When you get to doB(), then the MainActor / Main Thread are not suspended. Other tasks can be enqueued and executed on the main thread.","title":"What Is a Suspension Point In Swift?"},{"content":"I recently went on a quest for Live Activities. There\u0026rsquo;s a ton of gotchas or subtle notes that aren\u0026rsquo;t clearly mentioned. Think of the series as an unofficial rfc.\n Intro Example app - wwdc code - docs confusion points I saw Apple\u0026rsquo;s videos and docs on ActivityKit. They do a decent job of showing you 80% of the API and how Live Activity works. The remaining 20% is an enigma though.\n How to dismiss a live activity How to properly update a live activity in a timely manner How to update a live activity that wasn\u0026rsquo;t started from the app How to show a live activity while the app is in foreground How to properly handle observations once app is launched into the background If Background Tasks aren\u0026rsquo;t immediate then why does Apple Recommend using Background Tasks for updating something categorized as live / getting updated in real time? How to present a live activity once a live activity payload is sent How to use tokens to start vs update a live activity. Why has Apple not just re-used the existing APNs token for live activities What\u0026rsquo;s the difference between Live Activity and regular Push Notification? How and which parts of code should I share between Targets etc.  The whole problem was a bit exacerbated because:\n When Apple first introduced ActivityKit, the ability to begin live activities from APNs was not added. It only came later. As a result their sessions and example app don\u0026rsquo;t use \u0026lsquo;push-to-start\u0026rsquo; tokens. You have to find information about that from docs and forums. This also caused some duality in documentations and their sessions. As of Mar 24 2025 docs on How to begin a live activity from Push Notification are INCORRECT🤬🤬🤬 Update after my bug report on March 10 2025, Apple finally fixed it on Jun 11 2025 While the EmojiRanger sample app has a lot of good stuff, it\u0026rsquo;s app UI is a bit weird. The health of the characters don\u0026rsquo;t update as time progresses. The app has a lot of focus on how to update the in-app ui or live activities from the app. But then it\u0026rsquo;s totally mute on:  How to update the app while the app is backgrounded. Having knowledge of how Background Tasks work is implied and required but the example project offers no code on that. Perhaps it\u0026rsquo;s the right choice, but then I had to go finding how that works elsewhere. How to start / update / end a live activity from server.   App Extensions, Push Notifications, Background Operations and Live Activities (and potentially more) introduce significant architectural complexity. You no longer have a single token across the app, but instead need to maintain a correlation between each activity and its token, and handle app launching into the background. All architecturally complex endeavors. Understanding how certain features (or how things get updated) of the app work require you to also have some understanding on how App Intent works. Which requires more learnings. Some of the code in the example app are not for the iOS app but are instead for for the watchOS app / target. It\u0026rsquo;s not very clear. I don\u0026rsquo;t know own an Apple Watch. Nor was able to get the app build with Xcode 16.1 into the watchOS simulator. It failed to run. Giving me this error Since it\u0026rsquo;s a new API that has lots of interactions with other things of the OS, ChatGPT or other LLMs don\u0026rsquo;t provide empirical nor correct information.   App Intent is out of scope for this series. Though that also might be reason for some of my confusions. 🤷\n All these confusions led me do watch and re-watch a lot of the referenced videos.\nThe series assumes some understanding of the ActivityKit API, but less understanding of its life-cycle and overall architecture with regards to the app and apns.\nReferences Also don\u0026rsquo;t forgot to check out some less used references:\n WWDC 2023 - Meet Activity WWDC 2023 - Update Live Activities with push notifications WWDC 2023 - Design dynamic Live Activities WWDC 2023 - Bring widgets to life. I have to watch this again. WWDC 2019 - Advances in App Background Execution Human Interface Guidelines 10 questions with the Live Activities team. This was written at a time where you couldn\u0026rsquo;t use push notifications to start a live activity. As a result some of its notes are outdated. Felipe Espinoza YouTube Video - Design and develop Live Activities with ActivityKit for iOS 18. The video is great! Braze - Live Activities for Swift. I found after writing my posts :D. It\u0026rsquo;s extremely well written.  👉 Next Post - Live Activities - The missing doc\n","permalink":"https://mfaani.com/posts/liveactivities/1-intro/","summary":"I recently went on a quest for Live Activities. There\u0026rsquo;s a ton of gotchas or subtle notes that aren\u0026rsquo;t clearly mentioned. Think of the series as an unofficial rfc.\n Intro Example app - wwdc code - docs confusion points I saw Apple\u0026rsquo;s videos and docs on ActivityKit. They do a decent job of showing you 80% of the API and how Live Activity works. The remaining 20% is an enigma though.","title":"Live Activities Part 1 - Problem Statement"},{"content":"How do I begin development for Live Activities? Follow Apple\u0026rsquo;s docs:\n Add a new† (widget) target to your app. Make sure you select \u0026lsquo;Live Activities\u0026rsquo;. Add the necessary plist items. Add push notification capability. Only needed if you want to start / update from server. If everything is done from within the app then you don\u0026rsquo;t need this. Example \u0026lsquo;Clock\u0026rsquo; App on iPhone adds timers to your phone without any server interaction. Not sure why but by adding the widget, Xcode automatically adds a TimelineProvider. It\u0026rsquo;s useless for Live Activities. You can delete it.  Note: A widget is a specific kind of app extension.\nDoes killing the app stop an active Live Activity? No. For me it remained in Dynamic Island and Lock screen\nDoes killing the app cause live activities not to be delivered? My live activity was able to get started. I haven\u0026rsquo;t tested yet if my app gets callbacks or not\u0026hellip;\nWhat\u0026rsquo;s the feature difference between a Live Activity and a Regular Push Notification? Notifications have a fixed layout, with no ability to animate nor buttons to interact with.\nLive Activities have custom UI and are far richer. Can be animated up to two seconds (with some exceptions. See here). Can also have buttons added onto them. Live Activity is also positioned at where the dynamic island is at. It persists on the screen and morphs into different presentations\nWhat\u0026rsquo;s the difference between Live Activity and Dynamic Island?  Dynamic Island is a physical location of certain newer models. First introduced in iPhone 14 Pro, and then on every model since then. Live Activity is an Apple feature. Think of it as a shape shifting notification. The Live Activity appears in the dynamic island when the phone is unlocked. If a phone doesn\u0026rsquo;t have a live activity then it appears as a notification  What forms does the Live Activity take? Apple calls the \u0026lsquo;presentations\u0026rsquo;. The Live Activity can appear in multiple presentations:\n Dynamic Island  Compact Expanded Minimal (attached or detached)   Lock Screen  For an in-app UI, see Does the Live Activity appear when app is in foreground? Apple requires you to have all presentations added. You can\u0026rsquo;t skip any of them.\nHow do I create an Expanded view? The expanded view is made up of four sections.\n  Expanded View  You can share the leading / Trailing views between the expanded and compact. See here\nNote:\n A Live Activity that\u0026rsquo;s started from a push Notification is immediately expanded upon its arrival. Then within a few seconds it shrinks down to a compact presentation. While a live activity that\u0026rsquo;s started from the app isn\u0026rsquo;t shown immediately at all. It\u0026rsquo;s starts showing in compact presentation after app is backgrounded.  Does the Live Activity appear when app is in foreground? Yes/No. It does not show anything. The sound within the payload also gets dropped.\nFor in-app experience:\n You need your own UI. Figure out where / when you should or not show it. The app does callbacks when either in foreground or background. You can rely on the ActivityUpdates for changes in content and use that to update the in-app UI.  How do live activities and regular notifications stack against each other on lock screen? Live activities sit on top of regular notifications.\nHow long can a Live Activity remain on the screen?  Maximum of 8 hours on dynamic island Maximum of 12 hours on lock screen. or sooner if user dismisses it.  Can I use a start token to update an existing Live Activity? No. You need to use an update token to update an existing live activity. When I tried that the Console logged:\nUnsupported push notification event type for pushToStart subscription: update I was actually surprised that the push notification made to the OS. I would have thought that APNS would have gated it, but perhaps their tokens are opaque enough that APNS just blindly passes them to the OS. Not sure\u0026hellip;\nHow can I remove a Live Activity from the screen?  Dismissing a live activity should not cancel the activity.\n There are different ways to dismiss:\n From the compact presentation, you can swipe left on its center. You can swipe AGAIN to bring it back ‼️ From the expanded presentation, you can\u0026rsquo;t swipe left nor dismiss it. From the lock screen presentation, you can dismiss it like regular notifications. You can NOT swipe again to bring it back.   ⚠️ - Swiping from the compact presentation, only hides the Live Activity. The Live Activity will still remain on the lock while swiping on the lock screen will dismiss the Live Activity. Neither swipes should end your activity. That should be done through some explicit action.\n So how can I cancel and remove an activity? In the Apple Clock app, dismissing a Live Activity timer does NOT stop the timer. It just removes it from your dynamic island. Similarly, if you dismiss the live activity of an Uber trip, then it does NOT cancel the trip. Apple just removes the live activity from your dynamic island / screen.\nIf you needed to cancel an activity, then you can either add cancel buttons onto the live activity, and then allow users to cancel the timer using certain actions associated with the notification or allow users to tap on the live activity, open the app and then end it from within the app.\n Cancelling should only be done upon an explicit action which should then dismiss the Live Activity for you as well.\n   Apple Clock App canceling with an App Intent Action  Will the user get prompted to give access for tokens? YES/NO. The start token is issued provisionally.\nIf your Live Activity is started by the _server then:\nThe update token though requires explicit user permission from the lock screen.\nHowever, if your Live Activity is started by the app then:\nWhile you still see the OS prompt to \u0026ldquo;Don\u0026rsquo;t Allow\u0026rdquo; / \u0026ldquo;Allow\u0026rdquo; the Live Activity Updates, the update token is immediately issued — regardless of user hitting Allow or not.\n This inconsistent behavior and lack of ability to update Live Activities is huge source of frustration amongst engineers and product managers. This problem is also mentioned in the forums and here\n I plan on filing a radar and opening a DTS ticket for this.\n❕❕❕ The dilemma and proper user education If you the duration of the activity is short and the event is started from the server, then your users will have a very short window of time to:\n Lock the phone Hit \u0026ldquo;Allow\u0026rdquo;. A user may actually think hitting Allow isn\u0026rsquo;t necessary, because in the case of client-started live activity, it\u0026rsquo;s not required.  If your users are slow to engage or aren\u0026rsquo;t aware of the importance to engage or simply put don\u0026rsquo;t care, then you\u0026rsquo;d experience a lot of stale activities.\nWorkarounds A. Do product education for users. If you detected that the Live Activity has started then maybe you could say \u0026ldquo;You can background the app. Just make sure you hit \u0026ldquo;Allow\u0026rdquo; on the lock screen\u0026rdquo; B. Do a few Live Activities before. Especially ones that are longer. If from previous Live Activities, the user hits \u0026ldquo;Allow\u0026rdquo; and then ultimately hits \u0026ldquo;Always Allow\u0026rdquo; then the OS will grant update tokens in perpetuity. If your app intends to use Live Activities numerous times, then you should be playing the long run and not assume investment in its infrastructure isn\u0026rsquo;t worth it. C. Start Live Activity from app. This could be advantageous because update tokens are then issued immediately. This is only a good idea if the event that starts the Live Activity is triggered by the app. Often Live Activities are triggered by some external event and so you can\u0026rsquo;t rely on the app to trigger the event. Examples:\n Arrival Events. Ex: Amazon Starting the last 5 miles towards your home, Technician en route to come and fix your internet. An ioT device event. Laundry in progress, Coffee is brewing, Robot is vacuuming the house, Modem is rebooting, EV Charging, etc.  When is Live Activity not a good choice?  If the user has reasons to keep the app open during the time the live activity is active, then the user won\u0026rsquo;t be seeing it. For most cases you can\u0026rsquo;t predict users keeping the app open. If the event is coming immediately after the initial app installation and the live activity must be started from the server, and the duration of the live activity is just a couple minutes, then it may not be a good fit — because the app hasn\u0026rsquo;t yet built a proper baseline with the OS to allow Live Activities in perpetuity.  👉 Next Post - Live Activities - Development\n","permalink":"https://mfaani.com/posts/liveactivities/2-the-missing-doc/","summary":"How do I begin development for Live Activities? Follow Apple\u0026rsquo;s docs:\n Add a new† (widget) target to your app. Make sure you select \u0026lsquo;Live Activities\u0026rsquo;. Add the necessary plist items. Add push notification capability. Only needed if you want to start / update from server. If everything is done from within the app then you don\u0026rsquo;t need this. Example \u0026lsquo;Clock\u0026rsquo; App on iPhone adds timers to your phone without any server interaction.","title":"Live Activities Part 2 - The missing doc"},{"content":"Development How can I share classes between widget and app? Models must be shared across targets.\n App needs the model to manage its lifecycle (start, update, end, handling dismissed / stale activities). Widget needs the model to be able to present it.  Each View should only get added to targets that needs them.\n Your widget must create views based on the models for all Live Activity presentations. Your app may need views if it has an in-app view related to the live activity.  Note: You can\u0026rsquo;t import / link libraries that use shared or libraries that use certain APIs. For a workaround see here\nDo I need to do anything after a payload is delivered?  The Live Activity will get automatically updated. So nothing needed to do for that. The in-app representation of the activity, will need to get updated either by app querying the server or reacting to callbacks from the updates.  If you need to log events, then this is your chance to log.    Previews are a huge pain! I constantly had to check the project build logs and the preview error. Also the app target seems to be built even when I though it\u0026rsquo;s not a dependency.\nCheck out this list for ways to fix SwiftUI previews\nWhat are different ways to start a live activity?  Creating a request from the main app while app is in foreground. Creating a start event from server. Starting from an App Intent.  Token Token variances and how to acquire them. Frequency of change    Token Usage / Scope     regular push notification tokens Unique per app   push-to-start token (for Live Activity) Unique per app. Frequently invalidated   update token (for Live Activity) Unique per activity instance    They are NOT interchangeable. For more on that see here\nWhy can\u0026rsquo;t we just use the regular apns token?  Apple wants to control the frequency of start / updates and the lifecycle of the live activity. Apple wants to control it at the APNs level, not at the iOS level. As a result the client won\u0026rsquo;t have to waste any of its networking, battery to receive a notification only to have to then not present it to the user. Instead the server would just protect the device from such waste.  My guess is that the OS communicates with Apple’s servers, saying things like: \u0026ldquo;Hey, my battery is low, hold off on sending notifications\u0026rdquo; and later, \u0026ldquo;Battery’s good again, you can resume.\u0026rdquo;\nSubscription for push-to-start token updates  Observe changes on the static pushToStartTokenUpdates asynchronous sequence.  Subscription for push-update token updates Can be done in two ways:\n Activity Started Locally: Retrieve the activity instance upon creation. Subscribe to the activity\u0026rsquo;s pushTokenUpdates  // You can find similar example from Apple Example App: https://developer.apple.com/documentation/widgetkit/emoji-rangers-supporting-live-activities-interactivity-and-animations // Uses https://developer.apple.com/documentation/activitykit/activity/pushtokenupdates-swift.property func observeActivity(activity: Activity\u0026lt;OrderAttributes\u0026gt;) { Task { await withTaskGroup(of: Void.self) { group in ... group.addTask { @MainActor in for await pushToken in activity.pushTokenUpdates { let pushToStartTokenString = pushToken.reduce(\u0026#34;\u0026#34;) { $0 + String(format: \u0026#34;%02x\u0026#34;, $1) } print(\u0026#34;pushToStartTokenUpdates token: \\(pushToStartTokenString)\u0026#34;) // Note: Each token is associated with a single activity try await self.sendPushToUpdateToken(order: hotdogOrder, pushTokenString: pushToStartTokenString) } } } } } ... Activity Started remotely: Listen to updates on the type (not instance).   Not mentioned in example app or wwdc sessions\n // uses https://developer.apple.com/documentation/activitykit/activity/activityupdates-swift.type.property Task { // Listen for any updates from a Live Activity with  for await activityData in Activity\u0026lt;OrderAttributes\u0026gt;.activityUpdates { for await tokenData in activityData.pushTokenUpdates { let pushToStartTokenString = pushToken.reduce(\u0026#34;\u0026#34;) { $0 + String(format: \u0026#34;%02x\u0026#34;, $1) } print(\u0026#34;pushToStartTokenUpdates token: \\(pushToStartTokenString)\u0026#34;) // Note: Each token is associated with a single activity try await self.sendPushToUpdateToken(order: hotdogOrder, pushTokenString: pushToStartTokenString) } ... } } Changes to react to  Start Event: setup subscriptions Token updates: send tokens to server Permission changes in regards to Live Activity Content updates: update your in-app UI Other Life cycle changes: update your in-app UI  active dismissed staled ended    Where should you place code that reacts to (token) updates? So all items mentioned in the previous section are items that can occur when app is either in background / suspended or terminated.\n If app is backgrounded / suspended then subscriptions to updates are already placed in memory and will get callbacks immediately. If app is terminated then subscriptions are gone into the either. You have to redo all your subscriptions.  Note: Because you\u0026rsquo;re subscribed to token updates, then the app will get launched into the background. You need to make sure app properly resubscribes to all subscriptions after it\u0026rsquo;s launched.\nSomething like\nfunc didFinishLaunching () { // ALWAYS handleGeneralAppLaunchFlow() /// sends push-to-start tokens to server observerPushToStartTokenUpdates_sendTokenUpdatesToServer() /// sends update tokens to server /// Also updates app\u0026#39;s internal storage so once app is foregrounded the app\u0026#39;s in-app UI can reflect the latest of the activity.  observeActivityUpdates_sendTokenUpdatesToServer_updateLocalStorageOfActivityToBeAbleToUpdateInAppUI() // OTHER setupOtherSubscriptions() // ONLY IF FOREGROUNDED if UIApplication.shared.applicationState == .active { setupUtilitiesNeededForForeground() } } func setupOtherSubscriptions() { // some examples setupBluetoothDiscovery() setupLocationTracking() setupPushNotificationDelegate() other() } The key note is: Once your user needs to subscribe, then from there on, you want your subscription to carry on indefinitely. To carry subscriptions you have to re-do it upon any app launch (into foreground or into background). If you some functions / utilities that shouldn\u0026rsquo;t get launched until app is foregrounded then you can gate them as shown above.\nOr as Quinn has put it:\n The ‘obvious’ one is that your works needs to have some sort of checkpoint mechanism. That is, you need to periodically write your current progress to persistent storage so that, when you\u0026rsquo;re relaunched after a termination, you can continue your work [setup things as normal. Without this checkpoint, if app is launched into the background to code-paths that are different from the user-originated flows, then you\u0026rsquo;re not maintaining the app\u0026rsquo;s previous subscriptions. Your launch into background becomes broken due to a lack of proper subscription to token / activity changes] from there.\n How do I update the entire experience? What architecture should I have in place if for when notifications aren\u0026rsquo;t delivered or if app has bad internet and is not getting updated? The Live Activity (notification) itself gets updated automatically. But for the in-app experience, you can either:\n Not rely on Live Activity updates and just poll the server directly Rely on Live Activity updates, get a hold of the activity, and upon changes, update your in-app UI. If your info is stale (due to network issues or just not receiving updates / notifications), then:  Render a staled UI. You can use the isStale property to drive UI logic. Your app would get callbacks so you can update your in-app UI. Then when a stale state is detected: query the latest with a regular HTTP Request    While Apple says:\n The system will use this date [stateDate set in your payload] to decide when to render your stale view. from WWDC 2023 - Update Live Activities with push notifications - 16:44\n Also from docs:\n While setting the staleDate is optional, it’s helpful when you want to ensure your Live Activity doesn’t display outdated content. At the specified date, the activityState changes to ActivityState.stale and isStale changes to true. Access isStale to monitor the activity state and respond to outdated Live Activities that haven’t received updates.\nFor example, while a person has network connectivity, a sports app could update the Live Activity with the latest game information and advance the stale date. If a person enters an area without network connectivity, the app can’t update the Live Activity with new information and an advanced stale date. Eventually, the Live Activity becomes stale and displays text to indicate that the displayed information is outdated. On the next app launch or when [if] it performs background tasks, the app can also respond to the ActivityState.stale state.\n To get the callbacks, you must be subscribed to activityStateUpdates\n ❕ The UI will get re-rendered to your desired stale state. HOWEVER to avoid abuse or hacks, Apple doesn\u0026rsquo;t grant you callbacks until app is foregrounded.\n How to update a live activity while app is in background?    Approach Pros Cons       beginBackgroundTask(expirationHandler:) Immediate Only good for 30 seconds   BGProcessingTask Will be given enough time Time of execution is unknown and not immediate. It could be in next 30 minutes or end of day when user is charging their phone.   Sending Push Notifications Processing is all done by server. Doesn\u0026rsquo;t require any app background processing Requires server integration and token management.     Note: Using BGProcessingTask doesn\u0026rsquo;t align well with the naming of \u0026lsquo;Live Activity\u0026rsquo;. Users and Developers perceive that as something that is updated in real time.\n Live Activities can be used for two kinds of activities:\n Immediate: Uber ride Deferred: Syncing photos with the cloud for Google Photos  BackgroundTasks would be a terrible choice for an Uber app. Less terrible for a photo syncing app. Though your messaging has to properly match the deferred nature of the activity. Example:\n User starts a sync operation in foreground. The sync task is a 10 minute long task. User backgrounds the app right away. Live activity shows as “syncing paused” / “syncing paused - for immediate syncing open the app otherwise we’ll complete the sync by EOD” It should just act as a nudge to the user. But also if somehow the app did get some background operation time, then it will get processed.  Also see Background Task heuristics - how to increase chances of getting background time and BackgroundTasks testing from the forums.\n All that said, I still can\u0026rsquo;t fully understand or recommend a proper use case for Background Tasks for Live Activity. It could be that the heuristics of the iOS aren\u0026rsquo;t great, but other devices that use macOS or iPadOS offer a better (more immediate) background task mechanism. Not sure.\n What would the architecture look for storing the push-to-start token? What if I had multiple live activities that needed to start at the same time? This is a very good.\nThe server should maintain a single push-to-start token for entire app.\nType here means, the concrete type that conforms to the ActivityAttributes protocol. Example a sports app can have the following type:\n GameScoreAttributes: Brazil vs France game started GameScoreAttributes: Spain vs Argentina game started PlayerAttributes: Ronaldo signs new partnership with Adidas worth 40M a year. TeamAttributes: Arsenal has signed Messi  App should would have a single push-to-start tokens at any given time.\nSo for example, if user was only subscribed for a single match between two teams, then as soon as the app / OS realizes that the token is used for the GameScoreAttributes related activity, then it will issue a new one.\n push-to-start token should be stored per app. Based on my tests it was then refreshed as soon as its used (or other reasons for refresh). But I still recommend that you do NOT invalidate the start token unless either:  apns told your server the token is invalid. apns issued your app a new token, which then your app should send it to server. It\u0026rsquo;s also possible that Apple would allow a push to start token to be used for an extended period of time.   push update token is per activity  So if you have 5 live activities starting together, Like 5 soccer matches all starting at 12pm then:\n The server already has a single push-to-start token for the first game. It starts it. The OS may invalidate the push-to-start token by issuing a new. In my experience the push-to-start token was only used once, but you should not make any assumptions. App is then backgrounded (either launched or just un-suspended) OR if app was in foreground already then no app activation. App receives the new token. Must send the new push-to-start token to its server. Note this only works if you properly setup subscriptions upon launching into background. Steps 2-5 will need to get repeated if the push-to-start token gets refreshed / invalidated.  There’s obviously a budget for the tokens. I don\u0026rsquo;t know what the budget is. But it’s just always seems a good idea to also have NSSupportsLiveActivitiesFrequentUpdates added to your plist. In a slack conversation a colleague said the plist only affects budget for updating live activity, not starting live activity from server.\n ‼️ Hitting \u0026ldquo;Allow\u0026rdquo; will signal to the OS to give you update tokens. Without hitting \u0026ldquo;Allow\u0026rdquo;, update tokens are never emitted.\n If the user was also subscribed to a PlayerAttributes, then the app would have a secondary push-to-start token awaiting to begin as well.\nThe above is incorrect. Based on Apple\u0026rsquo;s own admission their API is misleading. The push-to-start token is in fact shared across different Activity types. It\u0026rsquo;s singular per the entire app. See [this](https://developer.apple.com/forums/thread/785281 from the forums.\nHow do you get the pushToken that is specific to the new activity that was started from a push-to-start token? I\u0026rsquo;m still actively developing this. But it should be either of the two choices. Not sure.\nfunc subscribe() { Task { for await activity in Activity\u0026lt;AdventureAttributes\u0026gt;.activityUpdates { for await token in activity.pushTokenUpdates { 🅰: sendToServer(token.hexadecimalString, for: activity.id) // this requires that apns to give you the activity ID upon getting the token on client or using the token with apns. Once you have that id, then you can associate things with it. I don\u0026#39;t think either do that. But I could be totally wrong. Not sure.  🅱: sendToServer(token.hexadecimalString, for: activity.attributes.hero.name) // this requires your own API to have a field for the key. In the EmojiRanger example, `hero.name` is the key. Obviously it could be something more suitable like `hero.id` and then everything is based off that. This is very doable and doesn\u0026#39;t require APNS to help you with identity. } } } } For any ongoing activity, whatever that is *originating* the activity and managing its lifecycle should be able to create and associate an ID with it from its start til finish. Use the ID to zip all the changes together. Because of that I think approach `B` is a much more reliable approach. Example: 1. Your server starts an Uber ride. Creates and associates an ID with it. 2. Client uses that ID to pass back the *update* token to the server 3. Your service needs to associate all update tokens with its activity. Improvements Tips for reusing views Make your Leading / Trailing views customizable. Example:\nstruct LeadingView: View { var isCompact: Bool let imageName: String var body: some View { Image(isCompact ? \u0026#34;compactImage\u0026#34; : \u0026#34;expandedImage\u0026#34;) // You can obviously adjust the frame as well. This is just some simplification. } } Animations See here for tips. For the most part, I\u0026rsquo;m leaving animations out of scope. With exceptions, there is a maximum 2 second duration for animations. Exceptions are if: Animation is done indefinitely for you when you use the following APIs / Frameworks:\n Animate countdowns by using Text countdown API    Automatic animations without restrictions when using SwiftUI\u0026#39;s `Text` countdown API   Animate Audio bars by using CallKit (phone / voip calls) + MediaPlayer (playing audio / video) which use the \u0026ldquo;Now Playing\u0026rdquo; feature. For more on that see here    Automatic animations without restrictions when using `MediaPlayer` framework  Note: Apps that have extended background operation (Location Tracking, Audio Playing) can obviously update the app more frequently. But that doesn\u0026rsquo;t mean the animation duration will be different.\nTicking between activities Apple suggested that there\u0026rsquo;s a way that you can tick (switch) between your live activities, but I wasn\u0026rsquo;t able to figure out. So I asked about it in the forums.\n Hi, the word tick is misleading here. The only way to actually do something like this is via APNs, even then, you will get throttled trying to change the activity less than every 15 seconds.\n Actions  You can add actions with App Intent You can deeplinks using widgetURL or Link. For more on that see Linking to specific app scenes from your widget or Live Activity  How to end a live activity? end vs dismiss The docs are tricky on this.\n   Event Visibility on Dynamic Island Visibility on Lock Screen Triggered by     end Immediately removes it Will keep it on the Lock Screen person or the system   dismissed Removes it Is also removed from Lock Screen person, the app, or the system     With the default dismissal policy, the system keeps a Live Activity that ended on the Lock Screen for up to four hours after it ends or a person removes it. The ActivityState doesn’t change to ActivityState.dismissed until a person or the system removes the Live Activity user interface.\nFrom Docs\n Basically the Dynamic Island is only a place for an active Live Activity.\nAs soon as you’re done with a Live Activity, it gets removed from the Dynamic Island. Yet still available from the Lock Screen — until its specified dismissal-date.\nAt first I thought my payload was incorrect and it\u0026rsquo;s the reason Live Activities are suddenly removed. This was more difficult to notice as I was only looking at my unlocked screen. Luckily after a few retries I noticed that on locked screens the Live Activity is properly ended and remains on the screen. This led me to re-read docs and be able to properly distinguish between dismiss and end events.\n👉 Next Post - Live Activities - Debugging\n","permalink":"https://mfaani.com/posts/liveactivities/3-development/","summary":"Development How can I share classes between widget and app? Models must be shared across targets.\n App needs the model to manage its lifecycle (start, update, end, handling dismissed / stale activities). Widget needs the model to be able to present it.  Each View should only get added to targets that needs them.\n Your widget must create views based on the models for all Live Activity presentations. Your app may need views if it has an in-app view related to the live activity.","title":"Live Activities Part 3 - Development"},{"content":"Debugging Notification delivery issues Can you send Live Activity notifications to simulator? Yes. The token created by the simulator works. Just don\u0026rsquo;t use the simulator for testing application lifecycle behavior or background tasks as a may not properly simulate OS restrictions.\nCan you drag and drop payloads into the simulator? That didn\u0026rsquo;t work for me.\nTiming of \u0026lsquo;Token Registration\u0026rsquo; vs \u0026lsquo;Payload Sending\u0026rsquo; Make sure the tokens are stored in your servers before sending the payload. This must be done in a timely manner.\n You should not attempt to request/store token just before you want to send a payload.\n Bad: You decide to begin a live activity. Then request start token. Then wait till it\u0026rsquo;s stored. Then start the live activity. Good: You store a start token as soon as app launches — irrespective of needing to fire a live activity event. Then whenever needed, you just start the live activity without caring if the token is stored or not.\n Your token registration on the server must be decoupled from the firing of the events. Otherwise your server will be sending events where tokens may not be stored / is just about to get stored. It\u0026rsquo;s the same case for update tokens or even regular apns tokens.\n Validate that your app has \u0026lsquo;Live Activity\u0026rsquo; enabled Settings \u0026raquo; Apps \u0026raquo; Your_App \u0026raquo; Live Activities \u0026raquo; Validate\nIf you can\u0026rsquo;t see \u0026lsquo;Live Activities\u0026rsquo; then it means something is misconfigured.\nHow to correctly validate your json? When you\u0026rsquo;re using Apple Push Notification Console, make sure you\u0026rsquo;re not using a timestamp far in the past. 1-10 seconds is ok, but 50-200 seconds or more may cause the notification to not get sent.\n  Attention: The console takes 2-3 seconds to figure out that your json is invalid. If you hit send before it errors out, then it would just send the last valid json. Or if you just pop back to the \u0026lsquo;fields view\u0026rsquo; (as opposed to the json view) then it would NOT give you an error that the json was incorrect. You would be hitting send, thinking the json was updated, but it just defaults back to the last valid payload.   tldr make sure the json is correctly formed.\nWhich fields are necessary for a json payload to be valid? For a start event you need the following items:\n Set the value of the event field to \u0026quot;start\u0026quot;. Include the attributes-type and attributes keys along with their necessary values. This is so that your app would know which type it must use to decode the payload. Include an alert in the JSON payload. The alert portion has multiple purposes:  A payload with an alert, will have a sound and present the live activity in expanded mode. Its value are used for:  Older iPhones that don\u0026rsquo;t have Live Activity feature. Apple Watches. See here for how that works.      Note: The alert isn\u0026rsquo;t a fallback for devices that know which have disabled Live Activities. Because once Live Activity is disabled, you won\u0026rsquo;t be getting start,update tokens any more. Whatever tokens you had would become invalid.\n This is why it\u0026rsquo;s critical for your servers to know if a user has Live Activity Enabled or not. If they don\u0026rsquo;t then you should fallback to regular notifications.\n  The Apple Docs\u0026rsquo;s sample payload uses an incorrect payload. Its \u0026ldquo;attribute\u0026rdquo; field should match the static portion of the \u0026ldquo;\u0026ldquo;ActivityAttributes\u0026rdquo;\u0026quot; Update: Apple fixed this on June 2025 after I filed a bug report.\n And like any other live activity payload you also need:\n The contentState The timestamp  Also note, I mainly used the Push Notification Console. Didn\u0026rsquo;t send jsons from command line or actual server. There are more nuances if you do it that way.\nHow can I be sure my encoding is done correct? If your encoding is incorrect then the notification won\u0026rsquo;t get delivered.\nOpen a playground and just create the encoded json. Don\u0026rsquo;t try to do it manually by yourself. I was using an enum that had an associated value, the encoded wasn\u0026rsquo;t what I expected. For more on that see here\nYou can use the following wwdc code to get the json:\n/// - Note: initialize your content accordingly. let contentState = AdventureAttributes.ContentState( currentHealthLevel: 0.941, eventDescription: \u0026#34;Power Panda found a sword!\u0026#34; ) let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted let json = try! encoder.encode(contentState) print(\u0026#34;\\(String(data: json, encoding: .utf8)!)\u0026#34;) 💡 Some common mistakes and solutions 💡 Assuming that the Apple Push Notification Console said the notification was delivered then there can be:\nMismatch at Apple\u0026rsquo;s Live Activity Schema Example you may be missing timestamp or contentState or attributes, some necessary headers, etc.\nMismatch at your own definition under attributes and content-state Main filters to find reasons for failure under Console app are:\n The name of your app\u0026rsquo;s type that conforms to ActivityAttributes. This is because the OS attempts to decode to that type. liveactivities set messageType to error. But not ever error is flagged as an error. So you might want to remove this has remaining budget for pushToStart. Example I got: \u0026ldquo;Topic com.companyName.LATest.push-type.liveactivity has remaining budget for pushToStart of 8\u0026rdquo;  Try some combination of the above to narrow things down.\nSome errors I found when I forgot to include a field named trackerId under my attributes  💡 \u0026ldquo;Received Event for unknown activity\u0026rdquo;\n  💡 Task [22] [com.company.productName::com.company.productName.MyCoolLiveActivity:Attributes type: MyCoolLiveActivityAttributes:BD56CBD6-D99B-4067-A610-961EBB7CB1E2] Encountered missing entry\n  💡 MyCoolLiveActivityAttributes:C38D2A41-A899-4046-B154-EFEC66C504B8], contentIdentifer: [w:fix-365.00-h:dyn-64.00-160.00-cr:23.5-s:1.0.fam:medium], destinationURL: nil, underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=4865 \u0026ldquo;No value associated with key CodingKeys(stringValue: \u0026ldquo;trackerId\u0026rdquo;, intValue: nil) (\u0026ldquo;trackerId\u0026rdquo;).\u0026rdquo; UserInfo={NSDebugDescription=No value associated with key CodingKeys(stringValue: \u0026ldquo;trackerId\u0026rdquo;, intValue: nil) (\u0026ldquo;trackerId\u0026rdquo;\n   Even though I had the type included, because it wasn\u0026#39;t the correct type, the OS marked it as missing.   Passing an epoch with the type of String for dismissal-date / stale-date instead of an Integer. For those the notification still arrives. Only that their values have no effect. As if they\u0026rsquo;re not included.  Note: It\u0026rsquo;s better to avoid redirecting the structure of ActivityAttributes or ContentState to another object. It\u0026rsquo;s unneeded and potentially confusing. Example:\n  The structure on the right, nicely aligns your `attributes` and `content-state`. You don\u0026#39;t have to think of an additional field names similar to the left  Is there a limit to how many events I can send? Yes. There\u0026rsquo;s a fixed amount budget that your app gets. You\u0026rsquo;ll get throttled if you don\u0026rsquo;t have any budget left.\nQuoting from WWDC2023 - Update Live Activities with push notifications:\n Using correct priority [for the apns-priority header] ensures you have budget left when you need it.\n  The priority you should always consider using first is low priority. Low priority updates are delivered opportunistically, which lowers the impact on the user\u0026rsquo;s battery life. However, this means the Live Activities might not be updated immediately when the push request is sent. So you should use low priority for updates that are less time-sensitive. Another benefit of using low priority is that there is no limit on how many updates you can send. In order to take advantage of this, you should be using low priority for the majority of your Live Activity updates.\n  On the other hand, certain updates require the user\u0026rsquo;s immediate attention, in these cases, choose high priority updates. High priority updates are delivered immediately. That\u0026rsquo;s why they\u0026rsquo;re perfect for time-sensitive updates. However, due to their impact on the user\u0026rsquo;s battery life, the system imposes a budget depending on the device condition. [To be able to inspect the true impact of notifications on your budget, try testing mac os console wirelessly and with a low battery or maybe even with \u0026ldquo;low power mode\u0026rdquo;.]without having the device plugged in\nIf your app exceeds its budget, the system will throttle your push updates, and it will dramatically impact your user experience. If you need the server to send high-priority pushes frequently to keep it up to date then add the NSSupportsLiveActivitiesFrequentUpdates flag to your plist with a YES value. Users can disable frequent updates independently of Live Activities in Settings. So you can must detect the status of the frequent updates feature by accessing the ActivityAuthorizationInfo frequentPushesEnabled property.\n OS suppressing due to budget (or other) reasons If all that didn\u0026rsquo;t work and you still need to log at other issues then, see Apple Developer\u0026rsquo;s Answer - Rico\nConnect your phone to the macOS and sift through the logs by:\n Filter by Budget and if you see : priority(0), budget(0) or running-not-visible, the system has decided to not show your activity. Some reasons why this may occur:\n low battery mode bad data in the push(es) too many pushes in a short period of time   To fix the budgeting issue, you may be able to remedy this by using NSSupportsLiveActivitiesFrequentUpdates which then gives you more budget.\nValidate that your app has \u0026lsquo;Live Activity\u0026rsquo; enabled Settings \u0026raquo; Apps \u0026raquo; Your_App \u0026raquo; Live Activities \u0026raquo; Validate\nIf you can\u0026rsquo;t see \u0026lsquo;Live Activities\u0026rsquo; then it means something is misconfigured.\nGetting more information from the macOS Console. You can debug much of this using Console.app. The behavior is not entirely consistent across OS versions but nonetheless you can debug to see if your process has lost budget or something else leading to your token not being sent.\nA good way to start would be with filtering by the BundleID of the target and / or the target name itself (the process name).\nAlso be sure to check the system processes for additional information. This will vary based on what and where you are looking but some examples:\n To debug APNS you\u0026rsquo;ll want to monitor apnd along with other relevant processes. To debug WidgetKit, LiveActivities, Dynamic Island you\u0026rsquo;ll want to monitor springboardd, liveactivitiesd along with other relevant processes.  A Daemon is a background Process without any user interface. To find a comprehensive list of Apple Daemons see this unofficial list.\nNotification delivery in Debug builds Debug builds may tamper the delivery of notifications.\n the debugger loads with a development profile and is not exactly the same, particularly with notification delivery.\nfrom forums\n I\u0026rsquo;ve experienced scenarios were push notifications didn\u0026rsquo;t deliver and I had to switch from WiFi to Cellular or restart my iPhone or changes networks. Not sure if the root cause was that I was using a debug build or not 🤷.\nCode after for loop await may never run! As someone who\u0026rsquo;s not super savvy with async await I learned that:\nfor await ptsToken in Activity\u0026lt;DeviceActivationAttributes\u0026gt;.pushToStartTokenUpdates { ... } doSomething() In this setup, doSomething() is never called.\nA friend explained that this is because pushToStartTokenUpdates conforms to AsyncSequence. An AsyncSequence provides an AsyncIterator, which exposes a next() method that asynchronously returns the next element in the sequence as an optional (Element?). The loop continues until next() returns nil, signaling the end of the sequence. Since the for await loop waits for the sequence to finish, and this particular sequence never ends, execution never reaches doSomething().\nThis Avander Lee article describes how that works with some more detail.\nFor what it\u0026rsquo;s worth, even if the sequence emits a finite number of values, it can still take a very long time to complete. For example, if new values are emitted every 30 minutes, it would take 2.5 hours for the loop to finish processing 5 values.\nIn the case of push-to-start token updates or token updates stream, the sequence emits an indefinite number of values, meaning it never ends. As a result, your for await loop will keep running indefinitely. This also means that token updates can trigger your app to launch or resume in the background at unpredictable times, depending on when those updates occur.\nOther notes How can you extract the key from the p8 file? It\u0026rsquo;s just plain text. There\u0026rsquo;s no decryption required. You can open it with TextEdit app.\nWhat should I do if my images don\u0026rsquo;t appear in the leading / trailing views?  Images have to be less than 4kb and shouldn\u0026rsquo;t exceed 45x36.67 points. Images can often not be hard to see, because of their color. Had to do something like:  Image(contentState.imageName) .renderingMode(.template) // this was critical for resizing .resizable() .frame(width: 40, height: 40) .foregroundStyle(.yellow) I can\u0026rsquo;t see logs for the widget. What can I do? Use OSLog and sift through the logs.\nGenerally I have a hard time getting logs from Xcode for App Extensions. But easy to use from macos Console app.\nI\u0026rsquo;m struggling up to come up with a good design. What should I do? In short Apple suggests that the design should be bold, unique and glanceable. The - WWDC 2023 - Design dynamic Live Activities is a fantastic talk. If you have doubts, then try to take inspiration from Apple Clock, Uber, Apple Maps or Google Maps. You can find a nice variation of design among them.\nI can\u0026rsquo;t find the answer I\u0026rsquo;m looking for. What should I do? There\u0026rsquo;s lot of other stuff mentioned in the forums. You really have to go digging. I\u0026rsquo;ve also even gone far as to open a DTS ticket. Getting support from an Apple Engineer for $70 is great value for your money. Especially that you can send them code where they can take a look at a later time and get back to you. Only note is, each time it takes 4-7 days for them to get back to you. So make sure you include all the necessary details.\nShout outs Special shout out to Kevin Lee who reviewed the series, recommended that I upload my blog to iOS Dev Weekly to boost my reach and has always been helping me push forward. I appreciate his kindness, energy and good vibes.\nSeries Summary Understanding the architecture is possibly the most complex. To know how to react to callbacks in a way that works for when the app is launched into background vs how to react to callbacks that just resume the app after it was suspended. Also figuring how the server architecture is a challenging task for engineers. Hopefully the series gave you some ideas on how to tackle these problems.\n","permalink":"https://mfaani.com/posts/liveactivities/4-debugging/","summary":"Debugging Notification delivery issues Can you send Live Activity notifications to simulator? Yes. The token created by the simulator works. Just don\u0026rsquo;t use the simulator for testing application lifecycle behavior or background tasks as a may not properly simulate OS restrictions.\nCan you drag and drop payloads into the simulator? That didn\u0026rsquo;t work for me.\nTiming of \u0026lsquo;Token Registration\u0026rsquo; vs \u0026lsquo;Payload Sending\u0026rsquo; Make sure the tokens are stored in your servers before sending the payload.","title":"Live Activities Part 4 - Debugging"},{"content":"So your infrastructure and all is in place. But you have one problem. The OS isn\u0026rsquo;t issuing the \u0026lsquo;update token\u0026rsquo; to you. This post walks you through when the problem occurs and when it doesn\u0026rsquo;t occur. Attempts I made to fix the issue, how I failed, how different use cases of Live Activity can have different out comes in terms of Completion Rate and final take.\nProblem ​WHEN\n​When a Live Activity is started from the server, the OS doesn\u0026rsquo;t issue an update token until user locks the screen and then hits Allow.\n​USER IMPACT ​This is detrimental to the user experience. Makes all live Activities go stale because user may not lock their device in time or just assume that the Live Activity experience is identical to live activity experience from other cases where updating a live activity is allowed to happen without explicit user engagement\n​Other Live Activity cases: ​- When Live Activity is started from the app ​- When Live Activity is started from a broadcasting Live Activity\n​DEVELOPER IMPACT\n​This difference is also very confusing to developers since it\u0026rsquo;s not documented that it works differently.\n​REFERENCES ​The following links all mention this issue:\n​https://developer.apple.com/forums/thread/800669 ​https://developer.apple.com/forums/thread/807976 ​https://canopas.com/integrating-live-activity-and-dynamic-island-in-i-os-a-complete-guide-part-2\nAttempts I first opened a question in the developer forums. Got no response back.\nLater I filed a feedback to Apple. FB21607043. Got no response back.\nI finally opened a DTS ticket. The outcome was:\n You are correct in your finding. Until the user explicitly hits \u0026lsquo;Allow\u0026rsquo;, the app won\u0026rsquo;t be granted the update token. This is so Apple can control background time of apps and control battery life. You could instead start the Live Activity from the app. That way the update token is guaranteed. But that means having the app open, which was against what our product wanted. You could instead start the Live Activity from the app — through a silent push. That way the update token is guaranteed. But silent pushes aren\u0026rsquo;t meant to be delivered immediately. 💡💡💡 They also said tapping and engaging with the live activity should implicitly grant you the update token. This is not documented anywhere in the docs. So I added widgetURL and App Intent to the Live Activity in my sample app. Tapping on neither generated the update token. After multiple emails back and forth, Apple asked me to upload a sysdiagnose to the Feedback. They were thinking that it\u0026rsquo;s due to budget issues. I also tried with a fresh bundle id; same issue. At the end I uploaded: Sample Project Sysdiagnose Video Recording  Waiting to hear back from Apple.\nWhere this limitation can have a more negative impact on the overall Live Activity Completion Rate† The following factors increase / decrease the chances of a user seeing the Live Activity OS Permission Prompt and acting upon int.\nInitiated from Server: Is the Live Activity started from the server (via an APNS payload) rather than from the app itself?\n ❕ If it\u0026rsquo;s initiated from the app, then you don\u0026rsquo;t have an update token issue. The OS will grant the Update Token as soon as the live activity begins. This is done because it\u0026rsquo;s an explicit user interaction.\n But if you plan to start the Live Activity from the server, then the following criteria affect the success rate of your feature.\nCriteria How soon after first time app launch: Does it happen in the first day or two after app is installed vs happening weeks after user onboarding? Because the more engagement with an app your user has had, then it\u0026rsquo;s more likely that they\u0026rsquo;ve locked their device and been prompted with the OS permission and acted upon it. But if it\u0026rsquo;s just 20 minutes after installation then it\u0026rsquo;s far less likely.\nDuration: The longer a Live Activity takes, the higher the chance the user has to lock the device and hit allow. If a Live Activity ends in 5 minutes, then the window for user to 1. lock device 2. hit allow is very short.\nFrequency: Happening 4 times a month vs a major event that user does once every a year.\nDoes it \u0026lsquo;end\u0026rsquo; at a pre-set time? If end time is determined before, then you can hack˚ the UI update without needing an update token. That is, you can rely on the stale UI re-rendering to update the UI.\nRequires Update: If it doesn\u0026rsquo;t, then you don\u0026rsquo;t need the update token, as long as your end time is fixed and you are comfortable with using a hack.\n NOTE: The OS will re-render your stale UI immediately. However you won\u0026rsquo;t get a callback until the app is foregrounded. This means you can not expect a callback to query your server — while app is still backgrounded.\n 1. Flash Sale countdown Title: Black Friday discounts end in 5 hours\nDescription: Limited-time voucher redemption for online checkout.\nHow soon after first time app launch: Requires user to navigate into the app an opt into following count downs for discounts.\nDuration: 5 hrs\nFrequency: Dozens of times a year\nRequires Update: No\nDoes it \u0026lsquo;end\u0026rsquo; at a pre-set time? Yes\nIs the end-state UI varied? No. It just ends; there\u0026rsquo;s no success or failure state.\n 2. Activating a Device Title: Activating the internet of your house for a specific CMMac as soon as you plug in the router to a coax outlet.\nHow soon after first time app launch: Happens as soon as the user plugs in their router, which is often one of the first actions a user may take.\nDuration: Could take 5 minutes or 15 minutes. Depends on network health, device model, and number of activation steps.\nFrequency: 1-2 times a year\nRequires Update: Depends. You may want to give the user more information about the activation steps.\nDoes it \u0026lsquo;end\u0026rsquo; at a pre-set time? No\nIs the UI of end state varied? Yes. It could end with either success of failure.\nConclusion with Product Considerations In the \u0026lsquo;Activating a Device\u0026rsquo; example, because it could happen after the user has just got a router, then they may not have the app, or just not be logged in. Or not care about using the app. Or care, but just didn\u0026rsquo;t get to lock their device while a live activity from your app was present, hence never saw the OS prompt to hit \u0026lsquo;Allow\u0026rsquo; on the lock screen.\nAs a result the app isn\u0026rsquo;t granted the \u0026lsquo;Update Token\u0026rsquo;.\n Final nail in the coffin: Since the duration is short, has a dynamic duration and final state, then you also can\u0026rsquo;t end the live activity at pre-determined time with a pre-defined UI. This means you MUST have the update token and can\u0026rsquo;t come up with any hacks. Be careful in your product design with this.\n Foot Note †: Live Activity Completion Rate: The rate of the app\u0026rsquo;s ability to start and successfully finish a live activity from the server. ˚: Hack: Use the Stale Re-rendering to show the final end UI instead of the actual intended stale UI.\n","permalink":"https://mfaani.com/posts/liveactivities/5-product-decisions/","summary":"So your infrastructure and all is in place. But you have one problem. The OS isn\u0026rsquo;t issuing the \u0026lsquo;update token\u0026rsquo; to you. This post walks you through when the problem occurs and when it doesn\u0026rsquo;t occur. Attempts I made to fix the issue, how I failed, how different use cases of Live Activity can have different out comes in terms of Completion Rate and final take.\nProblem ​WHEN\n​When a Live Activity is started from the server, the OS doesn\u0026rsquo;t issue an update token until user locks the screen and then hits Allow.","title":"Live Activities Part 5 - The Update Token Problem"},{"content":"This post isn\u0026rsquo;t meant to explain how actors work. Rather just elaborate a few tiny details.\nI\u0026rsquo;m often curious about the origins of things. How people met, dated or how they began their careers or founded their companies. Similarly about namings, understanding the context often gives you a foundational understanding. I give feedback in Pull Requests about variable and type names or seek advice through code-review channels. First time it occurred was when Michael Mayer at our Philly CocoaHeads (Pro Swift) Book club explained that the reduce function means \u0026ldquo;how you jam and reduce an entire array into a single value\u0026rdquo;. See here for other posts by where I discuss the naming of something.\nThe origin of this post was due to my curiosity about the term \u0026ldquo;actor\u0026rdquo; in programming. It originates from the seminal 1973 paper by Carl Hewitt, Peter Bishop, and Richard Steiger, a work that predates my own creation.\nWhy is it named actor? The term isn\u0026rsquo;t meant to fully match the meaning of actor as we perceive it to be in movies, but that\u0026rsquo;s the origin of it.\n Intuitively, an ACTOR is an active agent which plays a role on cue according to a script.\n This is the core metaphor: just like a stage actor waits for a cue, then performs a role, a computational actor waits for a message, then performs a behavior. So the naming is deliberately theatrical.\nPotential Renames of actor?  ThreadSafe: Although actors may use threads, they\u0026rsquo;re not threads themselves. GuardedClass / ProtectedClass / SafeClass: It’s ambagious what\u0026rsquo;s being guarded protected. ConcurrentActor: You can create an actor with a custom executor that\u0026rsquo;s serial.  Similar to how the names of struct, class, protocol, etc don\u0026rsquo;t fully convey all the semantics that they carry, the name actor doesn\u0026rsquo;t carry all its semantics. The renames fail to clarify while also creating other problems. Given the complexity of actors, choosing a vanilla name will give it room to be later explained in depth. So try to not read too much into the name. Instead go watch the WWDC 2021 - Protect mutable state with Swift actors and play around with it.\nIn programming world, why does code become asynchronous? Functions can be asynchronous because:\n1 - Asynchronous through their own execution\nThe task you\u0026rsquo;re doing takes a while to finish.\nfunc downloadImage() async -\u0026gt; Image { // takes 2 seconds to download } 2 - Asynchronous to protect state\nYou want to safely access mutable state or as Apple puts it:\n you use await to mark the potential suspension point.\n Actors help you with the 2nd kind of asynchronous operations.\nactor EventStorage { var events: [String] = [] func add(event: String) { events.append(event) } } let storage = EventStorage() print(await storage.events) // if adds are happening when you\u0026#39;re accessing `events` then you get suspended until the `add` finishes. Otherwise events property is immediately returned to you. For more on that see docs from here.\nSo what\u0026rsquo;s special about @MainActor? Is it also an actor? It\u0026rsquo;s an actor. But with the addition that it\u0026rsquo;s globally accessible from anywhere in your app. It\u0026rsquo;s a shared singleton. For more on that see Global actors in Swift - Swift with Majid\nBased on docs, the executor of @MainActor is equivalent to the main dispatch queue. The main dispatch queue, and the MainActor share some common characteristic:\n each are a single, unique instance, with DispatchQueue.main being a class variable and MainActor a singleton. both are mostly invoked asynchronously.  Any difference between the two?\n Actors and therefore @MainActor offer compile time checks if something isn\u0026rsquo;t properly awaited. It will clearly show you were your call site and destination context don\u0026rsquo;t match. DispatchQueue at best offers run time checks.  Can I annotate an actor with @MainActor?  You can\u0026rsquo;t annotate an actor itself with @MainActor or another global actor. It\u0026rsquo;s because the compiler can\u0026rsquo;t decide if you want to isolated data to the global actor\u0026rsquo;s context or the actor\u0026rsquo;s. An actor\u0026rsquo;s data (its properties) is either nonisolated, or isolated to a single actor. It can\u0026rsquo;t ever be isolated to multiple actors, that just defeats the whole purpose of isolation. Examples:\n @MainActor // ERROR: Actor \u0026#39;Person\u0026#39; cannot have a global actor actor Person {} Annotating a function with a global actor depends:\nactor Person { var age: Int = 0 @MainActor // ALLOWED. Because the function isn\u0026#39;t accessing data isolated to another actor. func log() { print(\u0026#34;hi\u0026#34;) } @MainActor func increment() { age += 1 // ERROR Actor-isolated property \u0026#39;age\u0026#39; can not be mutated from the main actor } } Can I opt of out an actor\u0026rsquo;s isolation? Yes. There are two ways to do that:\n Annotate properties / functions with another global actor. This is only allowed if there\u0026rsquo;s no shared data between the two actors. See the example above. Annotate properties / functions with nonisolated.  At the end you can mix-and-match actors but only if all reads and writes against a property are isolated to a single actor.\nShould I be using actors everywhere? You don\u0026rsquo;t start with actors. Use them only if you must. So far I\u0026rsquo;ve only used them by using MainActor. I haven\u0026rsquo;t created my own yet.\nA good use case is if you have to collect and combine multiple data streams into one, then using actors will help you significantly. Examples:\n  You need to store events (logs) that happen in your app. Then send them to server at a later time. Events may need to simultaneously get stored because they can get triggered by:\n user interaction callbacks from network operations on the screen background operations originated from elsewhere in the app (a timer, an OS event, push notification, device orientation change, location / bluetooth event)    A chat app\u0026rsquo;s MessageManager object which needs to manage a screen messages, uploading media, making calls, adding stickers in realtime.\n  If you need to download and store the media of a large list of rows in parallel. The rows are began in order, but won\u0026rsquo;t finish in order, and you may want to add a progress bar if each row is large (like a full episode).\n  To come at it from a different angle: if you\u0026rsquo;re protecting state using serial queues or barrier async then that\u0026rsquo;s a hint to use actors instead\u0026hellip;\nJargon Isolation An actor has its own state and that state is isolated from the rest of the program + synchronized access to that data is ensured. If you\u0026rsquo;re accessing an actor, then an actor gets you isolated in a way that you won\u0026rsquo;t ever get a crash due to multi-threading access. Also as mentioned before the data isolation provided happens at compile-time.\nContext At any given time your either:\n in the context of an actor in a nonisolated context. (FYI Anything marked with @preconcurrency has no actor context.)  Global Actor An actor that\u0026rsquo;s a singleton and accessible through out your app. You can just annotate types, functions with it just like how you do that with @MainActor.\nExecutor An abstraction to run jobs.\nProgressive disclosure It just means that Apple is slowly and iteratively introducing new Swift Concurrency to your codebase. You can disclose a type, a function to Swift Concurrency or entire module without having to rid of completionHandlers and DispatchQueue all at once, you can do that over time. Though Swift 6 will mean less progressive disclosure and more of an \u0026lsquo;immediate disclosure\u0026rsquo;.\nBoundary Crossing a boundary: Moving values into or out of an isolation domain is known as crossing an isolation boundary. This is because actors have to communicate and coordinate data.\nCooperative Thread Pool A pool of threads that play nice with the CPU. \u0026lsquo;Cooperative\u0026rsquo; here means they place nice with the CPU and don\u0026rsquo;t choke it.\n The new thread pool will only spawn as many threads as there are CPU cores, thereby making sure not to overcommit the system. Unlike GCD\u0026rsquo;s concurrent queues, which will spawn more threads when work items block, with Swift threads can always make forward progress. Therefore, the default runtime can be judicious about controlling how many threads are spawned. This lets us give your applications the concurrency you need while making sure to avoid the known pitfalls of excessive concurrency.\n From WWDC 2021 - Swift concurrency: Behind the scenes. Emphasis mine.\nForward Progress The Swift concurrency model is designed so that threads can efficiently handle both blocked and runnable tasks. When a Swift task needs to wait for something (like async I/O), it can yield control of its thread without blocking it completely. The thread can then be used to execute other ready tasks. This ability to switch between tasks on the same thread means that \u0026ldquo;forward progress\u0026rdquo; can always be made - the system can keep doing useful work without needing to create more threads!\nOld vs New  Prior to Actors and Swift Concurrency we used to say: This block is executed / dispatched on to the main Queue. Now when using actors we say:\nThis task is isolated to the main actor\n actor Person { var age = 10 // the age parameter is isolated to the `Person` actor instance. func increment() { // the increment method is isolated to the `Person` actor instance. age += 1 } } My other posts where I discuss naming of things  Swift existentials iOS Cold Start vs Warm Start What are development pods What is it named dSYM and DWARF What does the term \u0026lsquo;Mocking\u0026rsquo; mean Why is it named unowned? Why is it named CurrentValueSubject?  Acknowledgements and References Special shout out to Michael Mayer who imbued us all during our book club sessions in Philly CocoaHeads. He was patient, experienced and most of all, eager to teach and inspire others. I was so in debt to Philly CocoaHeads that I felt eager to give back in whatever I\u0026rsquo;m able to.\n A Universal Modular ACTOR Formalism for Artificial Intelligence - Carl Hewitt, Peter Bishop, Richard Steiger Migrating to Swift 6 - Data Race Safety. Make sure you check this out. It\u0026rsquo;s very wholistic, but surprisingly not something that you\u0026rsquo;d find easily from Google. Swift Programming Language - Actors  ","permalink":"https://mfaani.com/posts/swift/concurrency/swift-actors-a-few-random-notes/","summary":"This post isn\u0026rsquo;t meant to explain how actors work. Rather just elaborate a few tiny details.\nI\u0026rsquo;m often curious about the origins of things. How people met, dated or how they began their careers or founded their companies. Similarly about namings, understanding the context often gives you a foundational understanding. I give feedback in Pull Requests about variable and type names or seek advice through code-review channels. First time it occurred was when Michael Mayer at our Philly CocoaHeads (Pro Swift) Book club explained that the reduce function means \u0026ldquo;how you jam and reduce an entire array into a single value\u0026rdquo;.","title":"Swift Actors: few random notes"},{"content":"After going through a final-round interviews as a senior iOS engineer, I\u0026rsquo;ve learned that technical skills alone aren\u0026rsquo;t enough. The best candidates excel across multiple dimensions: system design thinking, behavioral storytelling, code review acumen, and interview presence. This post captures the hard-won lessons from my recent interview experiences—what worked, what didn\u0026rsquo;t, and what I\u0026rsquo;d do differently next time. These aren\u0026rsquo;t theoretical tips, but practical insights that came from real feedback and reflection.\nGeneral Preparation Often the final round is 4 rounds of interview. Ask if you can do it in two days. Gives you a proper breather in between.\nPrepare a nice introduction for yourself and your main career accomplishments. Within it mention where you\u0026rsquo;re trying to get to next (in your career).\nAsk the recruiter which of interviewers are from the team you\u0026rsquo;re interviewing for. Know who\u0026rsquo;s the Hiring Manager. That way you can tailor your questions at the end better or possibly ask role-specific questions.\nWherever you\u0026rsquo;re interviewing at, you must know the CEO\u0026rsquo;s name. Know the company\u0026rsquo;s mission statement. Don\u0026rsquo;t need to study it. Buy maybe just spend 10 minutes to get a baseline. In my interview, the interviewer was saying \u0026ldquo;Alex said\u0026rdquo;, and I was like \u0026ldquo;who the hell is Alex\u0026rdquo;. Then I realized \u0026ldquo;Alex\u0026rdquo; is their CEO. Interviewing isn\u0026rsquo;t about acing it. It\u0026rsquo;s about being better than all others. It\u0026rsquo;s about marginal gains. And just knowing a thing or two about the CEO and their mission statement might make you stand out.\nMake Chrome your default browser. It works better for certain online coding platforms. It integrates better with Zoom as well. Also make sure you\u0026rsquo;ve given permission to Zoom for code-sharing, microphone, camera.\nStop doing everything 30 minutes before. Have water and snacks by your side. Comb your hair. Maybe even brush your teeth. Have pen and paper. Make sure you have battery. Stop doing work at least an hour before. Do some light stretches. Meditate if you can. Try to get yourself relaxed and in the zone.\nHuge preference that you use your own personal Mac.\n It doesn\u0026rsquo;t make you look professional. Work computers often have restrictions for websites and stuff. Work computers will log your actions. You don\u0026rsquo;t want that.  Systems Design  Your overall approach with your interviewer should be to maximize breadth. Try to cover as many aspects of the design as possible. Yet don\u0026rsquo;t get bogged down in one area unless your interviewer explicitly wants you to. Like mention just mention CDN when needed, but don\u0026rsquo;t go deep into it unless asked. tldr system designs are intended to move quickly. At every moment you need to stay focused on a given scope. But then move off that scope quickly\u0026hellip;\n In System Design. Explain your thought process when there are multiple choices. Example: If you\u0026rsquo;re asked \u0026ldquo;how would your API contract be between client and server for tracking different stock prices\u0026rdquo;, your answer shouldn\u0026rsquo;t just be \u0026ldquo;websockets\u0026rdquo; It should explain why regular http request or polling is bad and why sockets is good.\nThere are a lot of obvious stuff that you\u0026rsquo;ll forget or ignore to talk about in a system design interview just because you think it\u0026rsquo;s not worth talking about or you in your mind just consider it out of scope. Ex: you might just think authentication isn\u0026rsquo;t worth discussing since you\u0026rsquo;re assuming you\u0026rsquo;re always logged in, but: 1. That\u0026rsquo;s not always true. 2. Talking about it, helps your interviewer better understand your thought process. 3. Occasionally just by thinking about Authentication, then in addition to thinking about a simple login, your brain might actually realize that you forgot to talk about API Gateway and its benefits. Which is very important. This was my case.\nKeep a living system design cheat sheet that you refine over time—reference it regularly until these patterns become second nature. Like for an app that tracks stock prices, your cheat sheet might include:\n websockets, caching as backup, authentication, API Gateway, batching websocket messages, offline support, rate limiting, data compression, pagination, push notifications for critical updates.  Other Tips for System Design If you\u0026rsquo;re interviewing for senior / staff position then don\u0026rsquo;t spend time into writing all fields of a model and its type being String, Integer, etc. It\u0026rsquo;s a waste of time. Focus on the more important stuff\u0026hellip;\nThe book: Mobile System Design - Manual Vicente Vivo (ByteByteGo) was phenomenal. Worth its weight in gold.\nIf you\u0026rsquo;re also ever trying to make sure you understanding of APIs are correct. Example you want to know how Google Drive upload, YouTube video meta data retrieving, Facebook feed pagination works, then you can either read about it from books and engineering blogs or you can\n Look into HTTP requests and the responses and content it\u0026rsquo;s getting. Like scroll down and then see how pagination works. Or clip into a certain video in Youtube, see how things start. Or upload a large file and see what traffic goes out.\n Behavioral Design Some random notes on behavioral interviews.\nAfter watching this I realized how key behavioral interview is and how severely I was lacking. It doesn\u0026rsquo;t take a lot of planning but it has huge impact.\nYou typically explain things in a STAR format. My interviewer was far less focused on the context and far more focused on my actions and their measurable results. I struggled to highlight results clearly. Results aren’t only numbers — they can be reputation changes, operational gains, or strategic impact. Examples include:\nQuantitative Results\n Success rate improved from 80% -\u0026gt; 88%. App size reduced by 20%, which increased install-through-rate and decreased uninstall likelihood. Feature introduction usually drives 10–15% engagement lift, translating into about 1% revenue growth. Architecture improvements enabled the team to ship new features 3× faster due to code reuse and simplified planning. Reduced API error rates by 40% after restructuring request logic. Increased test coverage from 50% -\u0026gt; 85%, reducing regressions by ~25%.  Reputation \u0026amp; Influence Results\n Became known as someone who can lead work across 4+ teams and align cross-functional initiatives. Director and peer teams now reach out proactively for architecture reviews, planning, and guidance. Became my manager’s go-to person for initiating new tasks or de-risking ambiguous projects. Was nominated for promotion due to consistent delivery and leadership. Became regarded as the person who \u0026ldquo;unblocks complicated work\u0026rdquo; or \u0026ldquo;owns delivery\u0026rdquo;, which increased trust in my execution.  Team \u0026amp; Organizational Results\n Unblocked a failing initiative and allowed the business to hit its Q3 milestone after being at risk. Enabled another team to deliver 2–3 weeks faster because my code or documentation removed dependencies. Established a reusable pattern that is now used by 3 other teams. Improved onboarding by writing clearer docs, reducing ramp-up time for new engineers from 4 weeks -\u0026gt; 2 weeks.  Product \u0026amp; Customer Results\n Reduced crash rate by 15%, improving customer experience and App Store ratings. Improved login flow performance by 40%, reducing drop-offs. Introduced a Live Activity/feature that improved user satisfaction and retention metrics.  Role Clarity\nBe explicit about your individual contribution. Don’t be vague. If you architected something and wrote the code, say:\n\u0026ldquo;I was the architect and the primary implementer. I designed the flow, wrote the core components, and coordinated with backend + Product to ship it.\u0026rdquo;\nClarity on your role helps interviewers understand ownership, leadership, and execution ability.\n 💡 Match your examples to the role level. When answering conflict resolution questions, be mindful of the seniority signal your story sends. If you\u0026rsquo;re interviewing for a senior role, sharing how you resolved a disagreement with a junior teammate might inadvertently suggest limited scope. Instead, choose examples involving peer seniors, managers, or cross-team stakeholders—situations that demonstrate navigating complexity, organizational influence, and high-impact problem-solving appropriate for the level you\u0026rsquo;re targeting.\n I HIGHLY recommend reading Senior vs Staff Engineer answers for behavioral interviews\nSome questions and short answers   Team member not pulling their weight -\u0026gt; \u0026ldquo;I had a private 1:1 to understand blockers, offered support, set clear expectations with a timeline, and escalated to my manager when improvements didn\u0026rsquo;t materialize.\u0026rdquo;\n  Manage 3-4 big items across engineers -\u0026gt; \u0026ldquo;I broke down work into milestones with clear owners, held weekly syncs to unblock issues, tracked progress in a shared doc, cloned myself through architecture documents, run books, dashboards that can create an easy to follow narrative or alerts that automate the look out, prioritized ruthlessly when conflicts arose, and escalated risks early to leadership.\u0026rdquo;\n  How do you manage a project with large scope -\u0026gt; I set up milestones. Prioritize items. Parallelize to a certain doable extent. And try to get feedback early on or have things go through QA.\n  Changes to ship on time -\u0026gt; \u0026ldquo;I cut scope by deferring nice-to-have features, parallelized work across team members (through quick task / ticket defining), broker tasks into smaller chunks and worked with PM to adjust acceptance criteria while maintaining core value.\u0026rdquo;\n  Resolve conflict on team -\u0026gt; \u0026ldquo;I facilitated a meeting where both sides presented their concerns, identified shared goals, tabulated trade-offs for more robust decision making, proposed a compromise that addressed key concerns, and documented the decision to prevent future confusion.\u0026rdquo;\n  Convince team about technical decision -\u0026gt; \u0026ldquo;I created a document outlining pros/cons, shared concrete examples of where the approach succeeded elsewhere, ran a proof-of-concept to address concerns, and presented it in a team meeting for consensus.\u0026rdquo;\n  Mentor someone -\u0026gt; \u0026ldquo;I paired with them on complex tasks, provided constructive code review feedback, shared relevant documentation, and set up regular check-ins to track their progress and answer questions.\u0026rdquo;\n  Tell us about a time where you had an unpopular idea -\u0026gt;\n  Learn something new quickly -\u0026gt; \u0026ldquo;I identified the core concepts needed, consumed focused documentation/tutorials, built a small prototype to validate understanding, and asked senior engineers for guidance on edge cases.\u0026rdquo;\n  Made a mistake -\u0026gt; \u0026ldquo;I discovered a bug I introduced in production, immediately notified stakeholders, deployed a hotfix, conducted a post-mortem to identify root cause, and implemented safeguards (tests/monitoring) to prevent recurrence.\u0026rdquo;\n  Deal with ambiguity -\u0026gt; \u0026ldquo;I gathered all available information, identified key unknowns, made reasonable assumptions with stakeholder alignment, started with an MVP approach, and iterated based on feedback.\u0026rdquo;\n  Influence without authority -\u0026gt; \u0026ldquo;I built consensus (you need consensus when you don\u0026rsquo;t have authority) by demonstrating value through a proof-of-concept, showed how it solved their pain points, addressed concerns proactively, and earned buy-in through results rather than mandate.\u0026rdquo;\n  Difficult stakeholder -\u0026gt; \u0026ldquo;You should always leave room on the table as you may not know, but you\u0026rsquo;re the difficult one yourself, or you lack of knowledge is a hidden impediment towards getting the milestones. I scheduled 1:1 time to understand their concerns, listened actively without being defensive (I made it about understanding them as opposed to them understanding me), found common ground, provided frequent updates to build trust, and adjusted my approach based on their feedback style.\u0026rdquo;\n  Dealing with a difficult coworker -\u0026gt; We just chat. Try to understand that everyone\u0026rsquo;s different. I don\u0026rsquo;t what they did wrong. Instead mention general exepctation for all of us. Usually I own a portion of it too. In this very instance had a colleague that was who wasn\u0026rsquo;t readily responsive on slack. After we chated, they said, they\u0026rsquo;re much more\n  Technical Challenge When asked to review a piece of code, then in addition to saying \u0026ldquo;This variable / function should be private, we should do dependency injection, we need documentation\u0026rdquo;, you should also be on the lookout for business inconsistencies:\n Often there\u0026rsquo;s some business logic that\u0026rsquo;s incorrect in the code. Ex: if you\u0026rsquo;re asked to generate history of a user at time X, then you shouldn\u0026rsquo;t create a history off of events within last 5 seconds. It\u0026rsquo;s better if it\u0026rsquo;s off of last 5 events within a maximum window of time. What happens if user backgrounds when polling and comes back?  Questions to ask Interviewer Have reasonable questions to ask from interviewers.\nProduct  Where is the team headed in next 6-18 months? How does this team and its product position itself relative to the rest of the company and its product? Is this team growing in importance? Can you provide some examples why you think its growing?  Company  What is that you like or don\u0026rsquo;t like about the company? Are people appreciated / promoted in reasonable time? What does your company do that makes you think the company is in a stable market position for the next decade?  Team  What\u0026rsquo;s the team size? What\u0026rsquo;s the mix of employee vs contractor in the team. Employees must make up at least 50% for it to mean this team and its product are stable and important. Who would I be working under me and next to me? To ask the question differently, you can ask, how many years has this team existed? and how the team size has grown?  Role  For this role, are there any technical or soft skills that are unique to your company and required? As a hiring manager, what are the main criteria of success for this role? Could be things like (leadership, ownership, on time delivery, technical acumen, working with the team and moving everyone ahead.)  Feedback  If you think you and your interviewer have connected, then maybe at the end ask them \u0026ldquo;Do you have any feedback on my answers. I\u0026rsquo;ll promise to not tackle your feedback. I honestly want to know where my answers lacked as I learn the most from my failures not my successes. I always cherish these feedbacks. It would mean a world to me.\u0026rdquo;   What\u0026rsquo;s interesting is, while most interviewers shouldn\u0026rsquo;t give you feedback right there and then, I\u0026rsquo;ve had good success with this.\n Other Notes Write down the questions you were asked (perhaps you can do this during the interview on paper. As often you forget the questions). Also write down your answers. Review your answers later with yourself and friends. You learn a lot from doing that.\nIf you are intrigued by a question in a way that it\u0026rsquo;s novel, then just say \u0026ldquo;Hah. This is a very though provoking question. I like it. It\u0026rsquo;s refreshing\u0026rdquo;. It just shows that you appreciate the game. Makes you stand out as a better opponent, much like how two players in a fierce match show each other professional respect.\n  ","permalink":"https://mfaani.com/posts/interviewing/final-round-learning/","summary":"After going through a final-round interviews as a senior iOS engineer, I\u0026rsquo;ve learned that technical skills alone aren\u0026rsquo;t enough. The best candidates excel across multiple dimensions: system design thinking, behavioral storytelling, code review acumen, and interview presence. This post captures the hard-won lessons from my recent interview experiences—what worked, what didn\u0026rsquo;t, and what I\u0026rsquo;d do differently next time. These aren\u0026rsquo;t theoretical tips, but practical insights that came from real feedback and reflection.","title":"Interviewing - Final Round Learnings"},{"content":"Leetcode considers this question to be easy, but it was a bit more complicated than that at first for me. Let\u0026rsquo;s try solving it three different ways:\nWhenever I can, I always try to do BFS. It seems much more natural.\nBFS - 1st solution func isSymmetricBFS(_ root: TreeNode?) -\u0026gt; Bool { var queue: [TreeNode?] = [] queue.append(root) while !queue.isEmpty { let temp = queue queue.removeAll() // 🔑 to make sure you don\u0026#39;t expand on `nil` nodes — otherwise it would be endless for node in temp where node != nil { queue.append(node?.left) queue.append(node?.right) } if isSymmetric(temp) == false { return false } } return true } BFS feels very natural because you traverse layer by layer. Here we have the logic broken down nicely:\n Add a layer into the queue. Then just check if each layer is palindrome.  DFS func isSymmetricDFS(_ root: TreeNode?) -\u0026gt; Bool { return isSymmetric(root?.left, root?.right) } func isSymmetric(_ rootA: TreeNode?, rootB: TreeNode?) -\u0026gt; Bool { // - IDEA // All \u0026#34;subtrees\u0026#34; must be matching symmteric // STEPS // 1. PROCESS CURRENT STRATE // Current state is maded up from TWO subtrees.  // 1.1 process base case: exit if rootA?.val == nil \u0026amp;\u0026amp; rootB?.val == nil { return true } // 1.2 process another base case: exit if rootA?.val != rootB?.val { return false } // 2. RECURSE // Each Level doubles the number of comparisons. return isSymmetric(rootA?.left, rootB?.right) \u0026amp;\u0026amp; isSymmetric(rootA?.right, rootB?.left) } A more sophisticated BFS approach:\nBFS - 2nd solution func isSymmetric(_ root: TreeNode?) -\u0026gt; Bool { guard let root = root else { return true } var queue: [(TreeNode?, TreeNode?)] = [(root.left, root.right)] while !queue.isEmpty { let (left, right) = queue.removeFirst() // 1. PROCESS CURRENT STATE // early exits  guard left?.val == right?.val else { return false } // not a base case, but a stop on traversal \u0026lt;-- this bit of a unique question since the algorithm adds in pairs — which restricts you from _not_ adding nil nodes. Hence the extra check. Typically you don\u0026#39;t add nil nodes and don\u0026#39;t have to do this check.  if left?.val == nil { continue } // could have just checked against the right node. Doesn\u0026#39;t matter since the previous line checks if thye\u0026#39;re identical.  // 2. RECURSE // every two nodes you compare, will have a max of 4 children. you must add them _symmterically_ into your queue. Then compare them  queue.append((left?.left, right?.right)) queue.append((left?.right, right?.left)) } return true } ","permalink":"https://mfaani.com/posts/interviewing/trees/symmetric-tree/","summary":"Leetcode considers this question to be easy, but it was a bit more complicated than that at first for me. Let\u0026rsquo;s try solving it three different ways:\nWhenever I can, I always try to do BFS. It seems much more natural.\nBFS - 1st solution func isSymmetricBFS(_ root: TreeNode?) -\u0026gt; Bool { var queue: [TreeNode?] = [] queue.append(root) while !queue.isEmpty { let temp = queue queue.removeAll() // 🔑 to make sure you don\u0026#39;t expand on `nil` nodes — otherwise it would be endless for node in temp where node !","title":"Symmetric Tree"},{"content":"Pros Performance  Company keeps adding more monitoring. Your MacBook will only get slower. You will get annoyed by this.    Mike Zorn\u0026#39;s comment about this post   Apple is finding more ways to restrict access to surveillance. This forces companies to restrict access to tools further because they lack the visibility into things. You pay the price\u0026hellip; Often developers get even restricted from using tools like Charles Proxy / Proxyman, because they require adding a new root certificate which then interfere with company\u0026rsquo;s monitoring tools. You can leave your work laptop at work. Never carry it back and forth with yourself. Go easy on your back. Read a book, breath fresh air, make a friend. I find this to be significantly refreshing. I don\u0026rsquo;t get when people carry heavy laptops back and forth with zero usage at home. Your office should be treated as a safe as your home. There\u0026rsquo;s a ton of stuff that you 1) you must use a laptop. Can\u0026rsquo;t use your iPhone for 2) absolutely don\u0026rsquo;t want done with your work laptop.  Doing your taxes Applying and interviewing at other companies Screen Recording and sharing. You don\u0026rsquo;t want to be accidentally sharing something that was your company\u0026rsquo;s sensitive data.     Your company\u0026rsquo;s intent might just be compliance. But the affect on you is the same\n Apple Integration across all devices  You can share your Wallet, Photos, Contacts, Notes, Reminders, Keychains. You now longer need to have separate developer configurations for work and personal on the same machine. Example:  Configuring multiple GitHub accounts and then making sure every commit is using the correct name and email can be done incorrectly without anyone noticing it.   You can use Apple Handoff between your persona MacBook and iPhone i.e. you can start work on one device, then switch to another nearby device and pick up where you left off. Other things you can do are: iPhone Mirroring, copy/paste across, answer phone calls on MacBooks, easier Hotspot sharing etc.  That said someone gave feedback that he gets things across devices by:\n Separate iCloud accounts and purchases are shared via family sharing\n So maybe you can get around this by creating a dummy child account\u0026hellip;\nPrivacy Assume the worst!\n 99% of what you do is likely recorded. Your email user/password all get logged and stored for however long your company\u0026rsquo;s complain policy requires and more. While most companies don\u0026rsquo;t have keylogger, your user / pass is likely still exposed, because your company has root certificates installed and everything you send to the internet gets signed with it and therefore visible. Having an HTTPS or End to End encryption will not protect you from this, because their root certificate becomes part of the root chain. Unlikely, but company can profile you based on your network activity. Less stress when leaving company. Because you don\u0026rsquo;t have anything personal on it to transfer or lose. Last thing you want is a weirdo across the planet having hold of your family photos.   You never know when they’ll lay you off and you lose access 😔.\n  IT may have remote access capabilities to view your screen or take control without notification File sync services (Dropbox, Google Drive etc.) could potentially expose personal files to company monitoring or you may just be completely banned from using.  Freedom  Ability to work on side projects (or actual read personal projects). Your own app, blog or business. Not having your laptop can be extremely limiting. You can customize the specs and color however you like. You no longer need to select from company storage. There are no travel regulations for personal laptops. You can update to the latest Operating Systems or use Beta versions. You can install any app you want. At work, I once created a fresh \u0026ldquo;Command Line Tool\u0026rdquo; app using Xcode. It was nothing but a dummy print \u0026ldquo;Hello World\u0026rdquo;. The app crashed on launch because it was flagged by IT. No Risk of Company Wiping Data. Freedom from Work-Required Apps. No need to change / set your password based on company\u0026rsquo;s policy If you\u0026rsquo;re a gamer then you can\u0026rsquo;t install games on it. Access to all the free internet. Some companies have banned access to ChatGPT or certain other websites. Your favorites folder, the main apps you use can be very different across the two laptops.  Focus  It makes it very difficult to focus on life while at work or vice versa. Having two laptops makes focusing a lot easier. Once you allow receiving iMessages, emails on your work Laptop, then you\u0026rsquo;ve opened the Pandora\u0026rsquo;s box. It just psychologically reinforce boundaries, signaling to your brain when to work and when to relax. Getting away from work truly happens when the laptop you\u0026rsquo;re using after hours isn\u0026rsquo;t your work device. Having separate devices helps maintain different browser bookmarks/history which aids context switching. Your work laptop could have a YouTube feed that\u0026rsquo;s all about Technology, while your personal one is all about news, food, movies. When you go on a vacation then you\u0026rsquo;d only be taking your personal laptop and ditch work entirely.  Clarity and Vision Before getting my personal MacBook, my thoughts were consumed by work. Now, I feel free, unclipped and unchained. My perspective has completely shifted.\n It’s like owning a bike, you rarely consider traveling to a different state for fun. But with a car and a tent, you suddenly imagine endless possibilities and adventures.\n 💡 What kind of personal laptop should I get? After almost a year of owning a high end MacBook Pro. I can tell you that I haven\u0026rsquo;t had the chance to use it as much as I thought I would. While I do a lot browsing, blogging, general use, I do very light weight iOS development on my personal Mac. I could have easily gotten away with a MacBook Air M2 with 16GB RAM and 512GB storage and saved myself $800 and some weight.\nHow to get the most out of your personal laptop? You can\u0026rsquo;t just sit on the couch and do personal work. It would be a disservice to your investment. You have to spend a bit on a monitor, desk, chair, ergonomic keyboard and mouse. The vibe around your personal laptop has to match with the quality of your work station, otherwise using your personal laptop may not appeal to you as enough.\nWhen I bought my MacBook Pro, I thought I would have more time to myself to do iOS development and maybe pick up some learning with LLMs and use them locally, but I\u0026rsquo;m not doing any of that.\nCons  Extra cost of device and purchasable apps. On trips you may need to carry two devices. I\u0026rsquo;m using 14\u0026quot; so it\u0026rsquo;s easier for me. Need to re-install and re-configure all your terminal aliases, git aliases, computer settings, IDE settings, SSH keys and access tokens, Networking configurations  ","permalink":"https://mfaani.com/posts/career/why-you-should-not-use-companys-macbook-for-personal-usage/","summary":"Pros Performance  Company keeps adding more monitoring. Your MacBook will only get slower. You will get annoyed by this.    Mike Zorn\u0026#39;s comment about this post   Apple is finding more ways to restrict access to surveillance. This forces companies to restrict access to tools further because they lack the visibility into things. You pay the price\u0026hellip; Often developers get even restricted from using tools like Charles Proxy / Proxyman, because they require adding a new root certificate which then interfere with company\u0026rsquo;s monitoring tools.","title":"Separate Devices, Better Life: Why Personal Freedom Beats Using Your Work MacBook"},{"content":"This post is based on my personal experience and is a bit oversimplified for brevity.\nUpdate: Added some of the feedback that I got from some readers.\nIs Software Engineering Still the Golden Ticket? What About Medicine… or Trades? A few years ago, if you asked anyone, \u0026ldquo;What should I study to make good money?\u0026rdquo; the answer was almost always: programming. Fast forward to today, and the answer isn\u0026rsquo;t so clear-cut.\n A tech degree is no longer a guarantee for a job. This recent WSJ article highlights some harsh realities. I\u0026rsquo;ve seen similar patterns with students reaching out to me. Not long ago, even an average CS graduate from Berkeley could count on multiple solid job offers—good pay, great location, and reputable companies. Now, even top-tier students with perfect GPAs are struggling to get any offer. This isn\u0026rsquo;t just a temporary glitch; I suspect it\u0026rsquo;s part of a broader and likely irreversible employment trend.\n Medicine: A Long Road, but a Clear Reward Medicine requires:\n 7+ years of education and training, where you\u0026rsquo;re making very little money. An average $200K in debt before you even start your career.  That’s roughly $900K+ in missed income and loans combined if you compare it to someone who gets a head start in another field. But once you’re in, you’re earning $150K-$300K/year on average—often higher than a typical software engineer.\nSoftware Engineering: Faster, Cheaper, but Changing The beauty of software engineering is the low cost of entry:\n No need for hundreds of thousands in debt. You can make money while you\u0026rsquo;re still learning or often before you start school.  But like medicine, there’s a spectrum:\n Your average programmer might hit a ceiling sooner. Specialists, like AI Engineers are earning 20%-500% (yes 5X) more than typical developers.  Trades: The Unexpected Millionaires Here’s a curveball: skilled trades are booming. A recent WSJ article shows how plumbers, HVAC technicians, and other trade entrepreneurs are turning into millionaires.\n There’s a huge demand for tradespeople. Less competition compared to software engineering. Less chances of being replaced by an AI bot. These days you can speak to AI, have it examine your MRIs, blood tests and give you advice.  But let’s be real: trades are a grind. They’re hard, physical work, and that’s part of why they pay so well. Even if you invest gradually there’s still a higher entry cost compared to software:\n Licensure and certification requirements. Bonding, insurance, and union fees. Tools, equipment, and vehicles—often big upfront costs.  However, trades offer something unique: if you’re entrepreneurial, you can split expenses across partners and scale up. Yes, there’s significant investment, but like any business, the costs can turn into profits once you establish yourself.\nIn a way, the recurring costs for trades mirror other fields like medicine. Doctors need malpractice insurance, medical equipment, and offices; trade entrepreneurs need tools, trucks, and employees. Both can be expensive to start—but both can be highly rewarding for those who persevere.\nAI: Hot and Harder If you\u0026rsquo;re eyeing AI, be ready for the challenge. It’s not your \u0026ldquo;just learn to code\u0026rdquo; pathway:\n You’ll need a solid foundation in linear algebra, calculus, probability, statistics, and a deeper understanding of algorithms, data structures, and machine learning models. AI jobs demand more brainpower, more effort, and arguably more talent than regular programming roles—but they’re hiring, and the pay reflects that.  Updated - Comments I got for this post:  I am not sharing this feedback on your post publicly for reasons. To start, the post is good. But I have had such difficulty in job interviews and such difficulty spending the time necessary to succeed in those interviews that I would not recommend this career to anyone. Getting a job is not about being a good developer. It’s about doing an insane amount of interview prep and surviving pressure-cooker interviews. Here is a translation of a typical interview prompt: “Code this app from scratch in 45 minutes. If you fail to implement any requirements, you fail.”\n  👆 was a comment an experienced iOS engineer shared privately with me on Slack Another person said:\n Entering the market NOW as a junior is so disconnected with how it was when I entered. Hard to have complete empathy for what they deal with. It’s awful.\n TL;DR Medicine pays off in the long run but takes time and money. Software engineering remains far less accessible than before nor is the easy-money path it once was. It\u0026rsquo;s partly due to over-supply and shift of demand towards AI.\nSkilled trades like plumbing and HVAC? They’re quietly turning people into millionaires. AI? It’s hot, it’s hiring, and it pays very well — but only if you’re ready to go the extra mile.\n The remainder of this post, assumes that you\u0026rsquo;re aiming to get into the Tech World.\nBut I\u0026rsquo;m not smart and don\u0026rsquo;t have a computer science degree. This was exactly my situation. From outside and without experience, if you see some code then it feels like you\u0026rsquo;re reading Chinese. I had that same feeling. I was stressed. But once you understand the basics of programming you slowly learn how to read code and most of all, learn how to compose questions and find answers to it, then it\u0026rsquo;s just about committing yourself to get your destination.\n Learning how to learn is like knowing how to use GPS. But you still have to put in the gas, look at the signs and not get distracted and stay awake.\n Also what most people don\u0026rsquo;t realize is the difference of what you\u0026rsquo;re taught at university vs how much of it is useful at your actual job.\n In university you\u0026rsquo;re taught only a whole lot of breadth without much depth. At work you actually need depth without much breadth.\n For example, to become an iOS engineer you don\u0026rsquo;t to take all the following courses:\n Android Web Development Database Data Science Networking Server Security Operating Systems UI \u0026amp; UX AI Data Structure and Algorithms Mathematics Compilers Computer Graphics Hardware, CPU, etc.   In a four year course the university teaches you lots of stuff, they rather be safe than sorry and have you absorb 100X more than what you actually need so it may increase your chances of landing a job. They also get you spending at the university for four years rather than one or two.\n While it\u0026rsquo;s also true that you need to have \u0026lsquo;some\u0026rsquo; understanding of most topics of, you can learn them along them as you go. Never learn for the sake of just learning. Your learning should be targeted and focus. Learning about Databases has almost as little use to me as learning Portuguese. If I never to learn about it, then I\u0026rsquo;ll open YouTube, Stackoverflow and a few blogs and find my answer. I\u0026rsquo;ll be done in 1-48hrs. I don\u0026rsquo;t need a 3 unit university course to tackle the issue. In fact by the time I need to know about it, I might have it all forgotten or the technology may have changed or just not be the exact kind we use at work\u0026hellip;\nFor anyone who already has a degree in something, it\u0026rsquo;s recommended that you just focus on the new domain that you want and learn that, don\u0026rsquo;t go out and get a four year degree from scratch.\nFor example in order to become an iOS developer you have to learn:\n Swift (and general programming concepts) SwiftUI (how to create views) Networking Working with the Developer Tools (Xcode) Some more intricate subjects of Swift Programming. Understanding the main Apple Frameworks / Libraries (built-in functionality of different systems of the Apple Operating System)    Being a jack of all trades is only needed in environments (such as startups) where there\u0026#39;s limited human resources, otherwise mastery in one domain is all that\u0026#39;s necessary  Unless you\u0026rsquo;re joining a startup where you need to be a jack of all trades, you do not need to learn much about the rest that is taught in a four year Computer Science Program. As an iOS developer, I\u0026rsquo;ve never been asked to write code for Android, Server, Security etc.\n I\u0026rsquo;m a senior iOS developer. Some of my skills are easily transferrable to other domains and but most are not. I have zero experience with Android, Web, OS, Security, Hardware, AI, etc. Yet I\u0026rsquo;m very successful at the work I do.\nYou\u0026rsquo;ll be fine if you\u0026rsquo;re great in one domain and clueless in others. This knowledge can save you a lot of time and anxiety.\n How do I pick the domain I like? Ask ChatGPT to ask you the following question:  Can You ask me 10 questions and then help me figure out which field (iOS, web, Android, Security, Networking, web, DB, AI, Compilers, data structures and algorithms, framework development, server, etc) of Computer science is best for me?\n It will help you get a good start.\nPick domains that you have access to mentors  Would you rather go to the east without any tour guide or go to the west with a tour guide?\n People usually go with the journey where they have a trusted guide. It\u0026rsquo;s the same when it comes to making a career for yourself in tech. Having a good mentor is critical, especially in the early stages. If you were undecided between which domain to pick, then try picking one that you can find mentors easier. There\u0026rsquo;s lots of things that you\u0026rsquo;re either utterly clueless about or just don\u0026rsquo;t know that you don\u0026rsquo;t know. Only way to resolve them is if you have a mentor or a support group or waste 10X more hours figuring things on your own\nBut strangers don\u0026rsquo;t answer my questions online They do. They do that a LOT!\n Answering questions of a newbie is easy. Takes almost no effort. Some people actually really love and enjoy helping others.  All you have to do is write a well crafted question. Get it to the right audience and wait for folks to come out and share their experience and wisdom. It\u0026rsquo;s both refreshing and easy for a seasoned engineer to share answers.\nI vividly remember my first encounters with Stackoverflow.com\n In 2012 I asked a question. It (rightfully) got marked as duplicate and closed. I couldn\u0026rsquo;t get answers to it. Back then I was mad and sad. Put away learning and didn\u0026rsquo;t come back to the site for two years later. The website is rigorous about duplicate (or low quality questions) because they don\u0026rsquo;t want to answer the same question again and again. In 2014 I asked a well crafted question. In less than an hour I got detailed answers from three very experienced engineers. I\u0026rsquo;ve often got answers from people working at Apple, Google or other places. People who literally wrote books, protocols and stuff. The internet is an amazing place!  How can I find such strangers to answer my questions?  Stackoverflow.com (The world\u0026rsquo;s most commonly used question-and-answer website for computer programmers) ChatGPT (The AI that can answer your questions) Medium.com (A social publishing platform where users can share stories, ideas, and perspectives) Meetup.com and their associated Slack (a communication platform very similar to Discord) groups. Meetup is a platform that assists groups of like-minded people meet in person on online. Slack is an app that helps teams collaborate and communicate with great ease online.:  The last one is key. People tend to care a bit more when its someone from their own town, neighborhood. Most Meetup groups these days have their own Slack group where you could remain in contact through out the week / month.\nI learned a ton from my local Slack group. It was named PhillyCocoaHeads. I got to ask a lot about basic Programming concepts from people that I never met in person. Without their help, I would not be where I am today.\n  This Slack has a \u0026#39;code-help\u0026#39; channel where people can ask their questions and get answers. Some of the other channels are \u0026#39;book-club\u0026#39; and \u0026#39;today-i-learned\u0026#39;  Stackoverflow.com\u0026rsquo;s main benefit is that it\u0026rsquo;s open to public and conversations are forever available. Though the UI of the site and its culture need some love. Slack may have a time limit for how long it keeps the history of conversations (typically 180 days) and is only available to the members of its Slack organization. Yet it\u0026rsquo;s in real time and often with people that you actually know.\nMedium.com is public but it is not meant to be a Q\u0026amp;A place or where discussions happen. It\u0026rsquo;s more of a fancy micro blog platform. ChatGPT (and don\u0026rsquo;t forget claude.ai) is getting better and better at answering and having conversations, but often there\u0026rsquo;s some tribal knowledge or lack of a human element that makes understanding directly from ChatGPT difficult.\nNowadays if I need something, I search for it or ask ChatGPT. If I can\u0026rsquo;t figure things out (or need a bit more depth) then depending on the factors listed below, I decide to take it to to either Stackoverflow or Slack\n The type of question. How experienced and responsive the Slack vs Stackoverflow folks are on that subject. When was the last time I asked a question. I don\u0026rsquo;t want to be asking too many questions from the same people.  Example:\n  On Stackoverflow, due to its requirements that questions must have straightforward answers, you can not ask an open ended question of: \u0026ldquo;Is using Java better or Kotlin to make a server? Why\u0026rdquo;. So Slack is your only choice.\n  Everyone you know in your Slack group has knowledge on iOS, no one knows anything about Network Security. So your only choice is to ask the question on Stackoverflow.\n  Should I learn on my own or join a BootCamp or get an associate degree? The path to success is anything between 6-24 months. There are no shortcuts. No Program is a silver bullet.\nThe programs are meant to show you the path and help you stay on the path. Do not think by joining a university, bootcamp, the job is done. You are the one that has to spend time experimenting, finding yourself mentors, attending meetups, do networking, figuring out bugs, practicing, preparing for interviews, writing your resume, setting up sample or real (but small) projects / apps, etc.\nWith the right dedication, the right person, and a bit of luck and support, any of these approaches can work. The advantage of bootcamps is their laser focus on a single domain, unlike universities.\nSelf-learning vs Instructor / Professor / Mentor Bootcamps jump start you a whole lot easier than self-learning. In a bootcamp, you have your peers and a mentor that helps with you on a daily basis.\nWith self-learning you often might get stuck on a bug and be clueless for hours or days whereas a mentor / professor can help resolve it in 5 minutes. Or may be looking at a tutorial and not realizing that the tutorial is no longer applicable or that it no longer compiles, etc.\nIf you have took a class or two on general programming then self-learning or if you have a mentor that you can reach out to 2-4 times a week (especially in the early stages) then self-learning becomes a lot easier. Otherwise bootcamps (associate degree programs) are better.\nWhen is it beneficial to get an undergrad or masters degree?  When you have no degrees at all. When you want to go all in and you have time for it. Some of the top Machine Learning, AI experts at Apple, Google, Microsoft have done extensive research with truly experienced professors during their Masters or PhD programs enabling them to innovate in ways that others can\u0026rsquo;t. You\u0026rsquo;re attending an Ivy League university that has great professors. The university cultivates building networks with your peers and has a great connection with the local companies and is great at helping you land your first job. This is less of an issue if you have a good amount of friends and family that can give you referrals. Universities may also have certain budgets to undertake certain high risk, high reward projects.  Some Top Free Courses  CS50 - Harvard. World Class. Lots of assignments per lecture. Covers basics. Might be too difficult for a beginner. The course has huge online presence across social media. Swift and iOS Learning - Apple. There are lots of fun and extremely beginner friendly tutorials there. The app is is great on iPads but also works for MacBooks. It\u0026rsquo;s only helpful if you want to learn iOS Programming. CodeAcademy. Lots of free courses. They make it easy to track your progress. All you need a is browser. I\u0026rsquo;m specifically leaving out introductory courses for learning Machine Learning / AI. Because I haven\u0026rsquo;t took any myself, nor you can start learning AI without having a decent background in a programming language and some other concepts.  I\u0026rsquo;ve gone through a bootcamp, done my own self-learning. How can I stand out? It\u0026rsquo;s tough. A lot tougher than it was in 2020. Have something small but working that\u0026rsquo;s already being used by more than 100 people.\nBest examples I can think of are:\n A well crafted website that shows your portfolio. We had an engineer at work who at the time of joining was a relatively junior engineer, but he already had an app in the App Store. It didn\u0026rsquo;t matter how many people were using his app. It just meant that he was a doer. His app was a simple but extremely functional app for a Train Time Table. He identified a problem: It was difficult for commuters to to look into small font pdfs and try to find the time of the next train. He turned into a very easy to read app. At a conference, an engineer I met talked about an app he owns. The app would just scan a banana and tell you if the banana was ripe or not. I asked him why did he do the app, he said:  There are some color blind people that can\u0026rsquo;t tell. My ignorance made me first think that the app was made just for run. I was intrigued at the idea that you can build apps that you never end up using yourself. I can speak about my own app experience during interviews. I got to try out new Apple code for Machine Learning. The app was super simple to build, it had only 2 - 3 screens. But I did it good. It made me stand out in a lot of conversations.   But overall if you show you can create, do and demonstrate a persistent sense of ownership and create (don\u0026rsquo;t just seek jobs but rather create them too), then you have higher chances.   I tend to overlook resumes overloaded with flashy keywords. I prefer the underdog who is consistently building and seeking to create a job on their own at a steady pace, even if it’s small and unpolished.\n How Important is Networking? And when should I start? Don\u0026rsquo;t start networking when your resume is ready. Start it as soon you\u0026rsquo;re learning. It\u0026rsquo;s just that the manner and intensity of your networking should be different. If you\u0026rsquo;re new then networking for you just means:\n just have a profile on Stackoverflow, on your local slack group and show up in meetups. People are more willing to spend time, give you referrals if they see you making progress.\n Referrals are super critical to your early success. So the better your network is, the more chances that the referrals will help your resume get to the right person.\nA Late Lesson on Risk and Reward   Tuan Champman\u0026#39;s note about his regret  Taun\u0026rsquo;s note aligns with my own learnings too. Ten years ago I was thinking of doing a master in MBA. I didn\u0026rsquo;t. Didn\u0026rsquo;t want to commit to 150k in debts + interest. I\u0026rsquo;m happy with my decision. Had I tried doing Master in CS, then things might have been differently. If I did AI/ML then yes. Otherwise pure Software engineering would have not mounted to crazy financial success. But then again who would have been able to predict the future?\nMy take on this is: going to a university and learning something that\u0026rsquo;s truly innovative and futuristic is always great in the long run. Going to university for the sake of network and learning how to learn may also make it worthwhile. But if you\u0026rsquo;re going to university and getting yourself into 150-250k debt (+ interest) to learn what can be taught in a boot camp for $200 online courses then it may not be worth it. Things have changed in last 20 years. There weren\u0026rsquo;t this many online tutorials / YouTube videos. The downside of attending university was never so as it is now:\n Crazy tuitions and fees High interest Reduced number of hiring for Software Engineering roles. Much quicker and cheaper paths to getting your foot through the world of Software Engineering.  tldr if you can see the future and believe in it, then the cost offsets for sure.\nSummary Find a mentor or join platforms and groups where you can quickly ask questions and get answers. A four-year degree isn’t always necessary, as most jobs focus on specific tasks. However, if you aim to be among the best, a degree can help. Whether you choose a bootcamp or self-study, having a mentor is key. Ultimately, success comes down to the effort, networking and the hard work you put in.\n","permalink":"https://mfaani.com/posts/career/how-to-get-into-software-engineering/","summary":"This post is based on my personal experience and is a bit oversimplified for brevity.\nUpdate: Added some of the feedback that I got from some readers.\nIs Software Engineering Still the Golden Ticket? What About Medicine… or Trades? A few years ago, if you asked anyone, \u0026ldquo;What should I study to make good money?\u0026rdquo; the answer was almost always: programming. Fast forward to today, and the answer isn\u0026rsquo;t so clear-cut.","title":"How to Get Into Software Engineering"},{"content":"This is a follow up from my previous post: The power and expressiveness of Swift ranges.\nFor a Character Range:\ncontain works fine let numericalRange = 1...10 numericalRange.contains(8) // true let a: Character = \u0026#34;a\u0026#34; let z: Character = \u0026#34;z\u0026#34; let alphabeticalRange = a...z alphabeticalRange.contains(\u0026#34;k\u0026#34;) // true for-loop and count don\u0026rsquo;t work print(numericalRange.count) // 10 print(alphabeticalRange.count) // ❌ Referencing property \u0026#39;count\u0026#39; on \u0026#39;ClosedRange\u0026#39; requires that \u0026#39;Character\u0026#39; conform to \u0026#39;Strideable\u0026#39; for num in numericalRange { print(num) // 1 2 3 4 5 6 7 8 9 10 } for char in alphabeticalRange { // ❌ Referencing instance method \u0026#39;next()\u0026#39; on \u0026#39;ClosedRange\u0026#39; requires that \u0026#39;Character\u0026#39; conform to \u0026#39;Strideable\u0026#39; print(char) }  What both of those errors mean is that Swift can\u0026rsquo;t figure out what the next character is for a given character. The folks who implemented Character chose not to conform it to Strideable.\n I\u0026rsquo;m confused! Aren\u0026rsquo;t Characters just some number in a table? Can\u0026rsquo;t Swift know that after \u0026ldquo;a\u0026rdquo; there will be \u0026ldquo;b\u0026rdquo;, then \u0026ldquo;c\u0026rdquo; and so on? You\u0026rsquo;re asking a very good question. Swift does know that. Yet since Characters are for every possible value in Unicode, and not just simple ascii characters (like \u0026ldquo;a\u0026rdquo; and \u0026ldquo;z\u0026rdquo;), then things can get a bit tricky. Let\u0026rsquo;s explain how it can get tricky:\n1 - Unicode Sorting is different based on locale Certain languages put any character that has a diacritics (Ex: à, ë, etc) after \u0026quot;z\u0026quot; while other languages place them before a, e, o, i, u\nimport Foundation let words = [\u0026#34;zebra\u0026#34;, \u0026#34;apple\u0026#34;, \u0026#34;éclair\u0026#34;, \u0026#34;elephant\u0026#34;, \u0026#34;åland\u0026#34;] // Function to sort and print results for a given locale func sortAndPrint(words: [String], localeIdentifier: String) { let sortedWords = words.sorted { $0.compare($1, locale: Locale(identifier: localeIdentifier)) == .orderedAscending } print(\u0026#34;\\(localeIdentifier)Sorting: \\(sortedWords)\u0026#34;) } // Sort using different locales sortAndPrint(words: words, localeIdentifier: \u0026#34;en_US\u0026#34;) // English (US) sortAndPrint(words: words, localeIdentifier: \u0026#34;sv_SE\u0026#34;) // Swedish sortAndPrint(words: words, localeIdentifier: \u0026#34;fr_FR\u0026#34;) // French Output en_US Sorting: [\u0026quot;åland\u0026quot;, \u0026quot;apple\u0026quot;, \u0026quot;éclair\u0026quot;, \u0026quot;elephant\u0026quot;, \u0026quot;zebra\u0026quot;] sv_SE Sorting: [\u0026quot;apple\u0026quot;, \u0026quot;éclair\u0026quot;, \u0026quot;elephant\u0026quot;, \u0026quot;zebra\u0026quot;, \u0026quot;åland\u0026quot;] fr_FR Sorting: [\u0026quot;åland\u0026quot;, \u0026quot;apple\u0026quot;, \u0026quot;éclair\u0026quot;, \u0026quot;elephant\u0026quot;, \u0026quot;zebra\u0026quot;] As you can see the sorting is different.\nThis means users of different countries could end up having different understanding of:\nlet range = \u0026quot;z\u0026quot;...\u0026quot;à\u0026quot; To an English and Swedish user that range would make sense, while to a French user it wouldn\u0026rsquo;t. The Swift language can\u0026rsquo;t truly tell you what\u0026rsquo;s next in a for loop.\n Also note: if you don\u0026rsquo;t have a locale set then it will just go based on the code points order.\n 2 - New versions of Unicode have new characters https://www.unicode.org/versions/\nCurrently we\u0026rsquo;re at version 16.0.0\nOk I get it. There are new characters. Why is that a problem? Isn\u0026rsquo;t it that the new characters go to the end of the full Unicode standard?\nNo. They don\u0026rsquo;t go to the end. Unicode is made up of blocks with pre-defined ranges.\n The number of items within the range can vary depending on the version/date of unicode being used.\n Example: The Hebrew Language\u0026rsquo;s Unicode Block is in the range of: U+0590-U+05FF i.e. it has a space for 256 code points / characters.\n  blocks for different languages. The list is goes beyond this screenshot...  But what\u0026rsquo;s more interesting is that:\n not all code points of a block is used.\n   gray cells are empty/undefined cells   The Unicode suggests that you reserve some empty space (see the \u0026ldquo;non assigned code points\u0026rdquo; in the screenshot).\n So if in future you needed to add new characters, then the newer characters aren’t all located at the end of all existing blocks while being separated from the rest of its similar characters. Imagine if we didn\u0026rsquo;t reserve empty space and had the following ranges:\n// v1 English: 1 - 26 Arabic: 26 - 58 Hebrew: 59 - 81 Then next year in your v2 version, you realized that you must add a new Character for all 3 languages. At that point you can add the new code points to the end of your code points. Your v2 would be like:\n// v2 English: 1 - 26 Arabic: 26 - 58 Hebrew: 59 - 81 New: 81 - 89 👆 is problematic. Because what if in v3 you also needed to add new characters and a new language. Then you\u0026rsquo;d have characters of different languages scattering all over the place.\nSo instead, the Unicode Standard anticipates more characters for each language.\n// Correct way of sectioning languages English: 1 - 128 (128 characters allowed) Arabic: 129 - 384 (256 characters allowed) Hebrew: 385 - 496 (112 characters allowed) Are there benefits to keeping all characters of a language grouped together? Yes. Plenty:\n  Human Readability: When inspecting Unicode tables or debugging, seeing all characters of a language grouped together makes it easier for developers and linguists to understand and work with the data.\n  Font Mapping: Fonts often map Unicode ranges to glyphs. Keeping characters of a language together simplifies font creation and rendering engines, as they can directly target a range instead of handling scattered code points.\n  Extensibility and Compatibility:\n Future Additions: Keeping characters together leaves room for adding more characters in the same range (e.g., new symbols or letters for dialects or historical scripts) without disrupting the organization. Backward Compatibility: Software relying on contiguous ranges remains unaffected by updates to the Unicode standard.    Language detection becomes a lot easier: Currently the way that text processing works is something like:\n  if char \u0026gt;= \u0026#39;\\u{0590}\u0026#39; \u0026amp;\u0026amp; char \u0026lt;= \u0026#39;\\u{05FF}\u0026#39; { // Hebrew block  // Process Hebrew character } Based on all the above reasons, the 2nd choice is cleaner, since you’re keeping all characters of a given block / language near each other.\n But because of the decision of having empty code points to allow future additions with ease, you\u0026rsquo;re not allowed doing for loops or counts because the result of that across the before and after new additions can be different. It’s basically unstable API\nThis is different from a normal Integer range 1...100 where there\u0026rsquo;s always 98 items in between.\n 3 - Some Characters are Clusters Lots of emojis are made up by using two or more code points. Although not exclusive to Flag Emojis, See Regional Indicator Symbols for more.\nlet cs = Character(\u0026#34;\\u{1f1e8}\\u{1f1ed}\u0026#34;)...Character(\u0026#34;\\u{1f1e8}\\u{1f1ee}\u0026#34;) print(cs.lowerBound, cs.upperBound) // 🇨🇭 🇨🇮 One could even argue if it\u0026rsquo;s actually correct to create a range for Characters. Let alone iterate or get the count. Like what is the true lower bound here?\n The decimal value of the first code point of the lower bound 1f1e8 or the decimal value of the second code point of the lower bound or some combination of the two?  When Characters are clusters then creating a range, doing counts and for loops becomes unclear.\n Note: While often Character clusters can be represented in either single code point or combined code point, some characters can only be created from two code points.\n Summary You can create ranges of Characters. However because Character doesn\u0026rsquo;t conform to Strideable, you can\u0026rsquo;t do a for-loop or get a count. Your mindset about them should simply be:\n A range with a lower and upper bound. While iterating it for simple ascii characters is plausible, Swift is just being overly safe since iterating it is confusing (because of locale and character clusters) and unstable (because of versions).\n Where as for integers or other types that conform to Strideable the range has more meaning. As in:\n A range with a lower and upper bound. Iterating is understandable and stable.\n If you were still determined to do a for loop or get count on a range of characters, then while not fully safe, you could use this gist I wrote.\nAcknowledgements Special shout out to Josh Caswell who\u0026rsquo;s always answering and enabling me to author such posts.\n","permalink":"https://mfaani.com/posts/swift/why-cant-you-loop-over-ranges-of-characters-in-swift/","summary":"This is a follow up from my previous post: The power and expressiveness of Swift ranges.\nFor a Character Range:\ncontain works fine let numericalRange = 1...10 numericalRange.contains(8) // true let a: Character = \u0026#34;a\u0026#34; let z: Character = \u0026#34;z\u0026#34; let alphabeticalRange = a...z alphabeticalRange.contains(\u0026#34;k\u0026#34;) // true for-loop and count don\u0026rsquo;t work print(numericalRange.count) // 10 print(alphabeticalRange.count) // ❌ Referencing property \u0026#39;count\u0026#39; on \u0026#39;ClosedRange\u0026#39; requires that \u0026#39;Character\u0026#39; conform to \u0026#39;Strideable\u0026#39; for num in numericalRange { print(num) // 1 2 3 4 5 6 7 8 9 10 } for char in alphabeticalRange { // ❌ Referencing instance method \u0026#39;next()\u0026#39; on \u0026#39;ClosedRange\u0026#39; requires that \u0026#39;Character\u0026#39; conform to \u0026#39;Strideable\u0026#39; print(char) }  What both of those errors mean is that Swift can\u0026rsquo;t figure out what the next character is for a given character.","title":"Why Can't You Loop Over Ranges of Characters in Swift"},{"content":"Learning from my product peers, TIL if you’re making an impactful change, it’s not just about the change itself—it’s about how you communicate its success. A compelling story can amplify your impact, but every great story needs a foundation of data to back it up. Logs, metrics, and dashboards are essential tools that help you visualize, quantify the problem and measure the impact of your solution from the perspectives of:\n Users Engineers Product Managers Company  With these numbers in hand, you can craft a narrative that not only highlights your achievements but also resonates with stakeholders across all levels.\nExample if your change causes less errors (or more users down a funnel), then:\n Previously 2000 users a day were seeing errors, now only 600 see the error. Less chatting with the AI chat box. Are going from 5 triage calls (5 engineers each involved for 2hrs for each triage) a month down to 3 triage calls a month? The success rate of our flow has increased by +1400 a day which means 5% more success rate. The financial impact of 1400 more users is is worth 1400 * $5 per day, also we\u0026rsquo;re now having 1400 less chat box interactions which is reduces load on our servers.  If you didn’t figure out a way or haven’t spent time in capturing the metrics then don’t release it. Otherwise your change isn’t measured / celebrated / understood / shared / marketed / rewarded.\nDeferring the dev work to add logs to a future release will work against you if you can’t store all the data or don’t have metrics before the change. This requires you to have proper process and understanding set up with your Manager and Product Manager so that you don\u0026rsquo;t rush to release.\nIt’s possible that your change can have an unexpected negative impact. You won’t know of it unless you have metrics / alerts / baselines in place.\nNote The 2nd step often requires you to find/reach out the right (product) person and have them help you figure the $ value for it.\nThe more the change impacts the users, the more you have to get Product involved to help you come up with that business / product impact.\nAlso often you do something that has a huge impact on engineers, but the combination your director, vp, culture, business goals don\u0026rsquo;t appreciate what you did — even if it saved the company 2000 engineer / hrs a year. This is why it\u0026rsquo;s often better to identify something that aligns with the existing goals of the company / director / vp. This way you don\u0026rsquo;t have to convince them. You\u0026rsquo;re just fulfilling a pre-defined goal.\n Basically you have be good at the office politics game and choose things that matter to those that promote employees.\n Examples Apple Product Launches do a great job at highlighting the user/product impact or the delta of what their doing vs others/before\n  M3 CPU Performance    macOS vs Windows Adoption Rate  UX Example We reduced the number of screens a user had to see to accomplish a task. When we were showing this to our VP. She asked:\n Can you tell us how long the task took previously? How many screens did the user see previously? How many taps did the user make before? vs the new approach?\n The expected answer would be something like:\n From 15 minutes down to 9 minutes From 16 screens down to 5 screens From 20 taps down to 7 taps  Some other items that would require real metrics to compare are:\n Error Rate Completion Rate User Satisfaction (which is more difficult to calculate)  Basically some metrics could be computed upon code-delivery, others require user metrics.\nSummary Upon sharing my realization with my team members as a common shortcoming of us engineers, one of our company\u0026rsquo;s most distinguished engineers - John Riviello immediately replied with:\n Excellent Advice. Every time I’ve been promoted at our company, it’s because I had data like this to quantify my business impact, and I made sure to collect it every time I shipped something so I had an inventory of impact to reference.\nOne other note along these lines to be aware of, be careful about trying to tie time saved with money saved How to Pitch a Project\n As someone who\u0026rsquo;s recently became a tech lead, it took me a while to understand, regardless of my engineering abilities, I lack significantly in the domain of office skills such as metrics gathering and showing impact. Since realizing that, I\u0026rsquo;ve been accounting time for how to log the diff, not to rush releases, and most of all, the time that I have to spend (or delegate to a PM or Manager) to find a reasonable dollar value to be calculated.\n","permalink":"https://mfaani.com/posts/career/how-quantifying-impact-helps-your-career-growth/","summary":"Learning from my product peers, TIL if you’re making an impactful change, it’s not just about the change itself—it’s about how you communicate its success. A compelling story can amplify your impact, but every great story needs a foundation of data to back it up. Logs, metrics, and dashboards are essential tools that help you visualize, quantify the problem and measure the impact of your solution from the perspectives of:","title":"How Quantifying Impact Helps Your Career Growth"},{"content":"For two (dynamic) libraries to work together they need:\n API compatibility ABI compatibility  If you have correct function, parameter names, and are able to access it (it\u0026rsquo;s not private) then you\u0026rsquo;re good. The compiler validates if classA is using the correct Programming Interface from classB 👈 API (Application Programming Interface)\nExample of API usage struct House { var address = Address(streetAddress: \u0026#34;1100 Happy St.\u0026#34;) } struct Person { var address = Address(\u0026#34;1100 Sad St.\u0026#34;) // ‼️ Missing argument label \u0026#39;streetAddress:\u0026#39; in call } struct Address { let streetAddress: String } ABI compatibility is similar to API, but just at the binary level. The compiler is no longer involved. What\u0026rsquo;s involved is the dynamic linker dyld.\nIt checks to see if the undefined symbols - symbols that are to be provided by another binary can be found in another dynamically linked library. dyld checks if binaryA is using the correct Binary Interface from binaryB 👈 ABI (Application Binary Interface)\nExample of ABI usage Library A\nimport LibB struct Car { let map = Map() map.start(accuracy: .high) } Library B\npublic struct Map { public func start(accuracy: Accuracy) } public enum Accuracy {...} Once the two libraries are compiled, then inspecting libA\u0026rsquo;s symbols using nm (see my other post for more), we\u0026rsquo;d see undefined symbols. Among the list of outputs we\u0026rsquo;d see something similar to this:\nU _$s4LibB3MapVAA0C0VyAA8AccuracyO4startyAF_tFTq U _$s4LibB8AccuracyO4highyA2CmF  U _$s4LibB3MapVAA0C0VyAA8AccuracyO4startyAF_tFTq: Is the mangled name for the Map.start(accuracy:) method in Swift.\nU _$s4LibB8AccuracyO4highyA2CmF: Is the mangled name for the Accuracy enum in Swift, also undefined in libA and defined in libB.\nThe U indicates that it is undefined in libA and must be provided by libB and that things will be resolved by dynamic linking.\n Similarly, if we inspected libB, then we\u0026rsquo;d the same symbols, but as such:\nT _$s4LibB3MapVAA0C0VyAA8AccuracyO4startyAF_tFTq T _$s4LibB8AccuracyO4highyA2CmF T indicates that it is a defined function or method. For more on that see here and here\nWhat breaks ABI?  Removal of existing API: Removing a public function, type or variable. Changes of existing API: Renaming a public function, type or variable OR adding an additional parameter to a function. Reduction of access control: Making a public function, type of variable internal or private Adding to protocol requirements: Adding a new function to the protocol. Increasing the size of a parameter even if the symbol is the same: If you added 100 new private variables to a struct then it can end up increasing the size of the type so greatly that it may break the ABI. It doesn\u0026rsquo;t apply to classes because they\u0026rsquo;re referenced and it\u0026rsquo;s indirect. Using a much newer or older Swift compiler which causes the symbols between the binaries to mismatch. See here Since Swift5, Swift\u0026rsquo;s ABI is stable. This means:  libraries compiled with Swift 5 and SwiftX (X ≥ 5) should always compatible libraries compiled with Swift 5 and SwiftY (Y \u0026lt; 5) are not compatible. Note: Apple first introduced Swift 5 with Xcode 10.2. So this problem isn\u0026rsquo;t more or less non-existent at this point.   etc.  Would it still be a breaking change if you add a new parameter but gave it a default value? Changing func start(accuracy: Accuracy) to LibB.Map.start(accuracy: Accuracy, distance: Int = 10) breaks both ABI and API. Let\u0026rsquo;s explain that:\nLibA precompiled: Assume LibA is pre-compiled. It was only aware of LibB.Map.start(accuracy: Accuracy)\u0026rsquo;s symbol at the time of its complication, even if you give the function a default value, because the symbols are different, LibA and LibB are incompatible.\n LibA needs to get recompiled so its binary is aware of the new symbol. Code change is not needed though. Upon recompilation, LibA learns of the updated symbol. Without recompilation, LibA would be still looking for the old symbol which doesn\u0026rsquo;t have the distance parameter. The table below shows the lack of impact of default values on symbol table.\n     LibB v1 v2.a LibB with new param v2.b LibB with new param and default value     Syntax LibB.Map.start(accuracy: Accuracy) LibB.Map.start(accuracy: Accuracy, distance: Int) LibB.Map.start(accuracy: Accuracy, distance: Int = 10)   Symbol T _$s4LibB3MapVAA0C0VyAA8AccuracyO4startyAF_tFTq T _$s4LibB3MapVAA8AccuracyO8distanceSi4startyyF T _$s4LibB3MapVAA8AccuracyO8distanceSi4startyyF    💡 The symbol of the 2nd and 3rd column are the same.\n💡 Information about the default value doesn\u0026rsquo;t get carried within the symbol tables.\nLibA - source code: Assume LibA is not pre-compiled. You have access to its source code and are always compiling it when you run/build/archive the app. After you pull in the latest version of LibB, you re-compile (as you always do for every run) LibA with LibB.Map.start(accuracy: Accuracy, distance: Int). Although technically a breaking change, since code change isn\u0026rsquo;t necessary and you\u0026rsquo;re always re-compiling then it appears so that it\u0026rsquo;s not a breaking change.\nWait what? So it\u0026rsquo;s a breaking change only if libA is precompiled? It\u0026rsquo;s always a breaking change when the change adds a new parameter to an existing function. Once this happens the next version change should be a major version bump. Because your dependent libraries only know how to handle the old symbol. Yet the problem manifests itself only when your dependent library is pre-compiled. In the Apple world, pre-compiled binaries come in the form of xcframeworks.\nEvery tag/commit on you library can be either delivered as source-code or precompiled. As a result you must version your code with the assumption that it could be either. Even if you shipped pre-compiled, there\u0026rsquo;s nothing holding back another developer giving access to source code for a specific tag.\nThat being said, I\u0026rsquo;ve added new parameters to functions with default values without doing major version bumps. Although this was a mistake, everything worked out because the dependent library/app was using the library\u0026rsquo;s source code directly.\nSo adding a default value doesn\u0026rsquo;t do anything? Default values help with API but not for every instance˚. Not with ABI. As illustrated the above table, default values have no impact on ABI. This is because in most languages, a function is uniquely identified by its name, and its parameters. Both the argument labels, and the types. For more on that see this WWDC Session - Binary Frameworks in Swift\n ˚: Adding a new parameter is also breaks the API. It just doesn\u0026rsquo;t manifest in most kinds of code usage:\n func foo(_: Int) -\u0026gt; Int { 0 } func foo2(_: Int, _: String = \u0026#34;\u0026#34;) -\u0026gt; Int { 0 } [1, 2, 3].map(foo) [1, 2, 3].map(foo2) // Error: Cannot convert value of type \u0026#39;Int\u0026#39; to expected element type \u0026#39;(Int, String)\u0026#39; Shout out to Saagar Jha who hinted the above.\nHow can I not make a breaking change when adding new parameters to my functions? Assume you had the following\npublic struct Map { public func start(accuracy: Accuracy) } Then go to:\npublic struct Map { public func start(accuracy: Accuracy) { start(accuracy: Accuracy, distance: 10) } public func start(accuracy: Accuracy, distance: Int) { ...} } By doing 👆 you haven\u0026rsquo;t changed your ABI, rather you\u0026rsquo;ve just made an addition.\nAre there any breaking changes that are\u0026rsquo;t originated from a change in the API/ABI? Yes. If you change the behavior of something. Examples:\n Performance Change: A function used to take 0.3 seconds but now takes 6 seconds Semantic Change: A function that takes a, b as inputs and returns c, but after a change returns d. This is a breaking change. It may break tests, cause unexpected runtime behavior and cause anger among devs. Behavioral Change: A function that used to fire notifications and now it doesn\u0026rsquo;t. Or previously it didn\u0026rsquo;t fire any notifications to a known channel but now it does. Thread Safety: Removing thread safety in a function can also be a breaking change. Making a function that was previously thread-safe no longer thread-safe. Resource Management: Changing how resources are managed or cleaned up within a function. For example, if a function that previously did not close file handles now closes them, it can impact the overall resource management in an application. Or if a function previously used 1X battery, but now uses 5X. Not a breaking change, but worth mentioning that you can\u0026rsquo;t compile a binary for the macOS platform but then expect it to work for the Linux platform. The binary has to be for the appropriate platform.  It\u0026rsquo;s better to mark all the above changes as a major version change along with proper release notes.\nWhat does ABI help achieve? Dynamic linking is very important to system APIs because it’s what allows the system’s implementation to be updated without also rebuilding all the applications that run on it. It can significantly reduce a system’s memory footprint by making every application share the same implementation of a library.\nSince Swift is (Ahead of Time) AOT compiled, the application and the dylib both have to make a bunch of assumptions on how to communicate with the other side long before they’re linked together. These assumptions are what we call ABI (an Application’s Binary Interface), and since it needs to be consistent over a long period of time, that ABI better be stable!\nSo dynamic linking is a developer\u0026rsquo;s goal, and ABI stability is just a means to that end.\n 👆Above section was extracted from Aria's post: How does a binary written in Swift work with a binary written in Rust? Not every two binaries can work together. The ABI between the two need to understand one another. This is assisted by a ffi. FFIs assist with exposing the interface of one library to the other. Usually the interface is on top of C, since C is a widely used low-level language.\n A foreign function interface (FFI) is a mechanism by which a program written in one programming language can call routines or make use of services written or compiled in another one.\nIt mates the semantics and calling conventions of one programming language (the host language, or the language which defines the FFI), with the semantics and conventions of another (the guest language).\n You basically have to spend extra effort and use the ffi to create the interface. This is something that you don\u0026rsquo;t have to do for two Swift binaries, because it\u0026rsquo;s all automatic.\nSummary API is about correct mapping of Programming Interface. ABI is about correct mapping of symbols. Symbols are based off of function name, parameter names and parameter types. Default values don\u0026rsquo;t show up in symbols.\nABI stability/compatibility is of no concern when you\u0026rsquo;re building from source. It matters a lot more when you don\u0026rsquo;t have access to the source code and are just given the binaries.\nAdding a new parameter with a default value is still a breaking change. There were lots of other ways to break binary compatibility. It\u0026rsquo;s important to be able to identify these and do major version bumps when needed.\nLast but not least, often you\u0026rsquo;ve made a breaking change but your build process masks it and helps you recover from it. Be sure to still do a major bump.\nReferences  Glossary - Swift Team Library Evolution - Swift Team. The link is amazing. Mentions almost every possible breaking/non-breaking ABI change across protocols, classes, structs, properties, extensions, functions, enums, enum cases, typealiases and more. For a full on discussion about about how ABI stability helps achieve dynamic linking and its significance see this How Swift Achieved Dynamic Linking Where Rust Couldn\u0026rsquo;t - Aria Desires. It\u0026rsquo;s fantastic. I\u0026rsquo;d focus only on the \u0026lsquo;Background\u0026rsquo; section. The \u0026lsquo;Details\u0026rsquo; section might be beyond the scope of this article. Swift ABI Stability Manifesto - Swift Team and other low level docs on ABI  Acknowledgements Shout outs to Saagar Jha for helping me figure all of the unknown and reviewing this post.\n","permalink":"https://mfaani.com/posts/devtools/binaries/how-do-binaries-work-together/","summary":"For two (dynamic) libraries to work together they need:\n API compatibility ABI compatibility  If you have correct function, parameter names, and are able to access it (it\u0026rsquo;s not private) then you\u0026rsquo;re good. The compiler validates if classA is using the correct Programming Interface from classB 👈 API (Application Programming Interface)\nExample of API usage struct House { var address = Address(streetAddress: \u0026#34;1100 Happy St.\u0026#34;) } struct Person { var address = Address(\u0026#34;1100 Sad St.","title":"How Do Binaries work together? What breaks ABI?"},{"content":"What problem do some and any solve? var x: Equatable = 10 // Error: Use of protocol \u0026#39;Equatable\u0026#39; as a type must be written \u0026#39;any Equatable\u0026#39; var y: Equatable = \u0026#34;10\u0026#34; // Same error You\u0026rsquo;s think the compiler should let the above compile, it rightfully doesn\u0026rsquo;t.\nThe compiler can\u0026rsquo;t know if the associated type of x, matches with the associated type of y. So it just forbids it. Compiler just doesn\u0026rsquo;t want to be in situation where you\u0026rsquo;d try something like:\nif x == y { print(\u0026#34;Equal\u0026#34;) } Although both are Equatable. One is an Int while the other is a String. So the == can\u0026rsquo;t be applied between them.\nSimilarities We can get things to compile using some or any.\nvar x: some Equatable = 10 var y: any Equatable = 9 Differences  any is careless for your return types. Allows the following:  func aa() -\u0026gt; any Equatable { if a \u0026gt; 3 { return 10 } else { return \u0026#34;ten\u0026#34; } }  some is thoughtful for your return types. Does not allow the following:  func bb() -\u0026gt; some Equatable { if a \u0026gt; 3 { return 10 } else { return \u0026#34;ten\u0026#34; } } Instead some allows identical return types of Int or String:\nfunc cc() -\u0026gt; some Equatable { if a \u0026gt; 3 { return 10 } else { return 9 } } func dd() -\u0026gt; some Equatable { if a \u0026gt; 3 { return \u0026#34;ten\u0026#34; } else { return \u0026#34;nine\u0026#34; } } How does some work across multiple returning types? Remember with older Swift we had to write:\nprotocol Animal {...} func feed\u0026lt;T\u0026gt;(_ animal: T) where T: Animal { ... } It was cumbersome syntax. Well now we can just write that as simple as:\nprotocol Animal {...} func feed(_ animal: some Animal) { }  This declaration is identical to the previous one, but the unnecessary type parameter list and where clause are gone, because we didn\u0026rsquo;t need the expressiveness they provide.\nWriting some Animal is more straightforward, because it reduces syntactic noise, and it includes the semantic information about the animal parameter right in the parameter declaration.\nFor more see this wwdc moment\n  ⚠️ - Attention: some: AProtocol is not a universal replacement for where T: AProtocol. For more on that see here  When should I use any? Apple recommends us to start with some and then only use any when you don\u0026rsquo;t want to restrict the underlying variables to have the same type. Example:\nlet specificAnimals: [some Animal] = [Cow(), Cow()] // ❌ won\u0026#39;t allow adding `Dog()` let variedAnimals: [any Animal] = [Cow(), Dog(), Cat()] When is that you can\u0026rsquo;t use any? var a: some Equatable = 10 var b: any Equatable = 10 b = a // Allowed a = b // ERROR: Cannot assign value of type \u0026#39;any Equatable\u0026#39; to type \u0026#39;some Equatable\u0026#39; Basically you can\u0026rsquo;t fulfill the requirement of some by passing any. This is because some needs specifics while the purpose of any is exactly to hide those specifics. Also see here.\nSummary  Both some and any keywords can be used to satisfy associated type requirements without explicitly specifying the associated type. This allows for more flexibility and abstraction in code. The impact of some is across variables. It enforces identical types to be returned. The impact of any is on a single variable. It has no enforcing to keep returning types identical.    WWDC 2022 - Embrace Swift Generics - Holly Borla  The word choice of any vs. some is a confusing one. In my humble opinion any is named fairly correct, while some should have been renamed to either:\n anyButMatchingOtherReturningTypes anyHomogeneousType anyConsistentType anyWhileGuaranteeingTypeRelationship_and_HoldingAFixedConcreteType somy, somny or insomnia  I specifically left Apple\u0026rsquo;s jargon of \u0026ldquo;opaque\u0026rdquo;, \u0026ldquo;existential\u0026rdquo;, \u0026ldquo;boxed\u0026rdquo;, \u0026ldquo;type erasure\u0026rdquo; out of this post as they often make it hard to understand. That said I recommend figuring those out once you read this post.\nFurther resources  Embrace Swift Generics - WWDC 2022 Opaque and Boxed Protocol Types - Apple Docs What is an existential? - My stackoverflow post Why does SwiftUI use “some View” for its view type? - Paul Hudson  ","permalink":"https://mfaani.com/posts/swift/some-vs-any/","summary":"What problem do some and any solve? var x: Equatable = 10 // Error: Use of protocol \u0026#39;Equatable\u0026#39; as a type must be written \u0026#39;any Equatable\u0026#39; var y: Equatable = \u0026#34;10\u0026#34; // Same error You\u0026rsquo;s think the compiler should let the above compile, it rightfully doesn\u0026rsquo;t.\nThe compiler can\u0026rsquo;t know if the associated type of x, matches with the associated type of y. So it just forbids it. Compiler just doesn\u0026rsquo;t want to be in situation where you\u0026rsquo;d try something like:","title":"Some vs Any"},{"content":"Markdown Your posts can be formatted using markdown syntax. It\u0026rsquo;s critical to know how it works. It\u0026rsquo;s super simple.\nFrontmatter  You can add tags: [swift, json, network call] and it will then add the tags to your post. Add showToc: true and will show a table of contents for your post. Hugo automatically takes the first 70 words of your content as its summary and stores it into the .Summary variable The date of the field affects the order of the post. So if you drafted the markdown of your file 2yrs ago, but published it today, then make sure you adjust the date. Otherwise it will be buried under the newer posts.  Instead, you can manually define where the summary ends with a \u0026ldquo;!\u0026ndash;more\u0026ndash;\u0026rdquo; (Notes: must be wrapped inside angle brackets) divider. Alternatively, you can add a summary to the front matter if you don’t want your summary to be the beginning of your post   Customize Description. The value of this field is used as an abstract in your front page and link previews. Add two spaces to create new line after a line end. Otherwise hugo will just continue the line. This is something hard to grasp. Because of this I usually manually review my rendered post with hugo server -D  Syntax Formatting  Highlighting individual code lines. Example:  class Foo { var name = \u0026#34;Jack\u0026#34; }  or I can add line numbers:\n1 2 3 4  rvm use ruby-2.5.1 || rvm install ruby-2.5.1 gem install bundler -v \u0026#34;2.3.3\u0026#34; bundle install pod lib lint   Syntax Highlighting Not sure if this applies to all themes, but for the PaperMod theme I used, the instructions were as such:\n Find your own custom css from here Add its css link into you extend_head.html, so it\u0026rsquo;s passed down into head of every page.  You can find Hugo docs on configuring your own highlighter. But I wasn\u0026rsquo;t able to make it work\u0026hellip;\nResource management  With images, overtime your static folder will turn into a big graveyard. So it\u0026rsquo;s best that you re-structure your posts. Example:   - Bad way: - Posts - post1 - post2 - static - imageA - imageB - Good way: - Posts - post1 - index.md - images - imageA - post2 - index.md - images - imageB  context-aware is the more technical term that implies that .Title has a different meaning for each post or at the global level vs post level. At the post level, .Title is the title of the post. At the global level, .Title is the title of the website. See here for more. Have expandable section. You can default it to open. See here. Example:  \u0026lt;details\u0026gt; \u0026lt;summary\u0026gt;See answer\u0026lt;/summary\u0026gt; - in-order traversal for all will be: `[1,2,3,4]` - level-order would be different for each. \u0026lt;/details\u0026gt; Making changes to your theme If you ever needed to make changes to your shortcodes, then you have to:\n Fork the theme Change directory to your theme\u0026rsquo;s repo. This is a subdirectory from the main repo that uses your theme. Make changes locally to the theme. Push the commit to your fork. Change directory to your website\u0026rsquo;s directory. Then if you do a git diff you\u0026rsquo;d see a change in the SHA of your submodule. Commit the SHA. Like just do git add adn then git commit. Then once you push to Netlify, Netlify will checkout the correct commit using the SHA of the submodule.   Pro Tip: If you ever wanted to see how your post looks like on an iPhone then on Safari \u0026raquo; Develop \u0026raquo; Open Page with \u0026raquo; iPhone 15 Pro (or whatever device you\u0026rsquo;d like to pick):\n   To quickly inspect the layout on a simulated iPhone from your macOS  Shortcodes It\u0026rsquo;s a way for you to do custom HTML within markdown. Think of writing the html customization in shortcode. Then naming that customization. Then invoking it in markdown. Example see here:\nUsage of shortcode   I purposefully used a screenshot otherwise Hugo would have rendered the shortcode and not shown the syntax.  Shortcode definition \u0026lt;audio controls\u0026gt; \u0026lt;source src=\u0026quot;/{{.Get \u0026quot;audio-name\u0026quot;}}.{{.Get \u0026quot;audio-type\u0026quot;}}\u0026quot; type=\u0026quot;audio/{{.Get \u0026quot;audio-type\u0026quot;}}\u0026quot;\u0026gt; Your browser does not support the audio element. \u0026lt;/audio\u0026gt; Shortcode visualization   Fidelity Account List. The list remains there for some screens.  ","permalink":"https://mfaani.com/posts/content-creation/everything-i-learned-from-blogging-with-hugo/hugo-post-formatting/","summary":"Markdown Your posts can be formatted using markdown syntax. It\u0026rsquo;s critical to know how it works. It\u0026rsquo;s super simple.\nFrontmatter  You can add tags: [swift, json, network call] and it will then add the tags to your post. Add showToc: true and will show a table of contents for your post. Hugo automatically takes the first 70 words of your content as its summary and stores it into the .","title":"Hugo Post Formatting Basics"},{"content":"This is the 2nd post of \u0026ldquo;Everything I learned from blogging with Hugo\u0026rdquo; series\nWhy Hugo?  Hugo is a Static Site Generator. Key is the word \u0026ldquo;static\u0026rdquo;. It means:   A static website is made up of one or more HTML webpages that load the same way every time. Static websites contrast with dynamic websites, which load differently based on any number of changing data inputs, such as the user\u0026rsquo;s location, the time of day, or user actions. While static webpages are simple HTML files that can load quickly, dynamic webpages require the execution of JavaScript code within the browser in order to render\n  Hugo Quick Start. Unlike other tools (gatsbyor Jekyll), there is no npm, Gemfile or ruby hell that you have to deal with. Though this also means that you may not be able to benefit from what npm offers unless it\u0026rsquo;s offered for you in Hugo. It\u0026rsquo;s super fast. Hugo has a semi easy building block system. Each theme has certain abilities that can be enabled as long as you add its appropriate metadata values. These metadata values are known as frontmatter.  High Level  I highly suggest you go through this post. It\u0026rsquo;s about how to create a hugo theme from scratch. Each theme has certain pre-configured layouts. A layout is a structure for a webpage. You can have layouts that inherit from a sort of base class. The base/root structure that all others inherit from is named: baseof.html. Every screen inherits from its layout structure. Every Hugo theme must have a baseof.html file. Then it also most have two main layouts as well.  single.html: A layout for a single post. list.html: A layout for a page that lists multiple posts.   To be clear, you don\u0026rsquo;t have to specify which layout you need to use. Hugo will just know based on the location and naming conventions of your markdown file.  An _index.md file will automatically use the list.html layout. An index.md file will automatically use the single.html layout.    Quiz Now let\u0026rsquo;s just try not to think in the context of Hugo, but just in the context of a website. What do you think each of the following links should represent?\n mfaani.com/posts/first-post (a post) mfaani.com/posts/second-post (a post) mfaani.com (homepage) mfaani.com/posts/devtools (a directory) mfaani.com/about (a page)  First-post, second-post and about page use the single.html layout. They\u0026rsquo;re just different posts.\nBoth homepage and /devtools directory use the list.html layout. They query different ranges of posts.\n Homepage queries all posts. /devtools directory queries only posts under that directory.  ATTENTION: For the homepage, you can add an index.html in the layouts directory. It takes precedence instead of using list.html layout.\nSo for mosts sites you\u0026rsquo;d have single.html, list.html and a index.html. My blog doesn\u0026rsquo;t have an index.html layout.\nConfiguration  Config.yml or Config.toml is where you have the website level configuration. Things like:  Which theme to use. This means changing to a different theme can often be as easy as changing just the theme\u0026rsquo;s name from your configuration file. Website\u0026rsquo;s main navigation menu. In my blog, the navigation menu contains: Blog, Search, Tags, About Website\u0026rsquo;s title Google Analytics id. I used Gideon Wolfe\u0026rsquo;s post to figure out my setup.**** Website\u0026rsquo;s main language (If you have multiple languages) Configuration of global settings. Example: ShowReadingTime: true ShowShareButtons: true ShowBreadCrumbs: true // to show parent pages of a subpage ShowCodeCopyButtons: true # Certain values of the `config.yml` file can be overridden in the frontmatter of an individual post.     Summary  Every theme has something like a base class named baseof.html. Single pages have a layout defined in single.html List pages have a a layout defined in list.html All of the above are within the theme.  How Hugo decides to use which layout is merely based on whether it needs to render a single post or a page that lists multiple posts together.\nSpecial files index.md It\u0026rsquo;s better to explain with an example.\ncontent posts hugo findings imageA.jpg imageB.png index.md third-post.md If I all I had was the above, then it means I have two blog posts named:\n  myblog.com/posts/hugo-finidings\n  myblog.com/posts/third-post\n  index.md just makes things cleaner in a folder. Allows me to group a post and its resources (images, pdfs) in a directory. For more on that see Hugo - Page resources\n  If you don\u0026rsquo;t use index.md then you can\u0026rsquo;t reference to images in its directory. Only way to reference images is if your file is named index.md or _index.md. Only these two special files have the ability to group/bundle up resources with markdown files.\n  NOTE: I could have created a \u0026ldquo;third post\u0026rdquo; directory and then added an index.md within it. And things would have worked the same. If you don\u0026rsquo;t have any resources/images to use then it\u0026rsquo;s really becomes a matter of preference.\n  _index.md Allows us to add metadata (frontmatter) to a directory. It\u0026rsquo;s not used for single posts. Example if I had:\n content posts hugo findings postA.md postB.md postC.md _index.md postD.md then because I added _index.md then the I can access myblog/posts/hugo-findings/ and see a list of all posts that are under the name of that directory. Without adding _index.md opening myblog/posts/hugo-findings/ would result in a 404 error.\nIn Hugo\u0026rsquo;s terminology, /hugo-findings is known as a section. You can add frontmatter to the _index.md_ much like any other post. Sample of _index.md:\n--- Title: '⚡️ SERIES - Optimizing App Size' date: 2022-04-23T04:17:58-04:00 --- In this series I'll take about my struggles on how to be a better an iOS teacher, make better slides and what resources Apple provides. The series contains 10 posts. Make sure you see here. Your blog can have as many _index.md as the number of directories within your posts directory.\nWhat\u0026rsquo;s the difference between _index.md and index.md?    Syntax groups together uses creates     index.md a post and the resources referenced from its frontmatter and accessible to its directory single.html a post   _index.md all posts and the resources referenced from its frontmatter and accessible to its directory list.html a list page    Can I use both an _index.md and an index.md file in the same directory? From my experience you can\u0026rsquo;t. It because unclear to Hugo if it has to process the index.md and make a post for the blog and use the single.html layout or if needs to process the _index.md and make a section/directory and use the list.html layout. For more on their differences see here\nOther notes   If you don\u0026rsquo;t include --- at the top of your post, then none of frontmatter will get processed. And you\u0026rsquo;d have a messed up title + if it\u0026rsquo;s a draft, it will get published.\n  What\u0026rsquo;s the difference of Hugo vs doing things on my own with HTML, CSS?\n  What\u0026rsquo;s the difference between partial template and layout? and here. tldr:\n Partials (footer, head, headder) are small, context-aware components that can be used economically to keep your templating DRY.\n \u0026ldquo;Context-aware\u0026rdquo; is an important term to understand. The partial will have global parameters of your site passed down to it. It would also page specific parameters passed down to it.\n Going through this this tutorial helped me signifcantly. Seeing this tree structure and the internals of baseof.html, single.html, list.html, and what goes inside layout and paritals was also very helpful. Unlike real themes, the tutorial is barebone and simple to understand. Highly recommend.  . ├── archetypes │ └── default.md ├── config.toml ├── content ├── data ├── layouts ├── resources │ └── _gen │ ├── assets │ └── images ├── static └── themes └── exampleTheme ├── LICENSE ├── archetypes │ └── default.md ├── layouts │ ├── 404.html │ ├── _default │ │ ├── baseof.html │ │ ├── list.html │ │ └── single.html │ ├── index.html │ └── partials │ ├── footer.html │ ├── head.html │ └── header.html ├── static │ ├── css │ └── js └── theme.toml from: https://retrolog.io/blog/creating-a-hugo-theme-from-scratch/\nNeed to to make your local development available to another device or make it publicly available? See here\nSummary  Use the config for global configuration of your theme Use frontmatter to configure your posts Hugo has distinct layouts for single pages vs pages that are a list of pages Each layout is made up of multiple templates. Templates are things like header, footer, head, etc. Use index.md to bundle/group resources with a page Use _index.md to bundle/group resources with pages of a directory  ","permalink":"https://mfaani.com/posts/content-creation/everything-i-learned-from-blogging-with-hugo/hugo-high-level/","summary":"This is the 2nd post of \u0026ldquo;Everything I learned from blogging with Hugo\u0026rdquo; series\nWhy Hugo?  Hugo is a Static Site Generator. Key is the word \u0026ldquo;static\u0026rdquo;. It means:   A static website is made up of one or more HTML webpages that load the same way every time. Static websites contrast with dynamic websites, which load differently based on any number of changing data inputs, such as the user\u0026rsquo;s location, the time of day, or user actions.","title":"Hugo High Level"},{"content":"If you\u0026rsquo;re ahead of others, you become people\u0026rsquo;s go to person. It means more involvement. More challenge and opportunities to grow and network.\nBeing first means you can pave the way for others and be helpful. It\u0026rsquo;s one of the key traits of a lead.\nMain Advantages of being first You can set the tone, guidelines that you want others to follow. And not you following others. Often you\u0026rsquo;re late into some bad architecture. Folks have already made their decision on the bad architecture. Things are already in motion. Product has already signed off to the design and it\u0026rsquo;s now a quarterly goal. The cost of correcting the mistake is too high. It\u0026rsquo;s difficult to get people buy into your plan.\nBeing first means you\u0026rsquo;re informed ahead of others, have more time to react to feedback and come up with plans. You\u0026rsquo;re also the first contact point and often that means you\u0026rsquo;re the contact point with another team which helps build relationships.\nBeing early and well-prepared for meetings demonstrates your attentiveness and respect for others' time. Consider the person who arrives ahead of schedule, ensuring that all technical aspects, like screen sharing and microphone functionality, are in perfect order. This individual not only presents themselves more favorably but also enhances the meeting experience for everyone involved. Contrast this with someone who is habitually late, struggles with basic presentation tools, or overlooks essential permissions for content sharing. A rehearsal once revealed that the audience couldn\u0026rsquo;t hear the introductory music due to the noise suppression feature in Teams\u0026hellip;\n 💡 Being first also translates to being fast. Often I\u0026rsquo;ve been handed tasks / projects. The project get deferred to next quarter because someone beat me to getting their own project passed the first phase. Example: Product Team then has 3 projects to choose from. All projects are worthy of pursuit. They decide to prioritize the project that\u0026rsquo;s further ahead. The other two projects gets deferred.\nThis is why you need to build early momentum, have clear communication, be efficient at decision making and having parallel work streams, anticipate competition, pitch early, set visible timelines that others are aware of to create a sense of urgency and progress for yourself and team.\n How to be first Demonstrating care and staying informed requires a blend of knowledge, attentiveness, and the flexibility to manage your schedule effectively. Implementing automation, setting up metrics in advance or just being the go-to person can significantly expedite your ability to be responsive and authoritative, positioning you as the lead. Yet, it\u0026rsquo;s not just about showing concern or being proactive; it\u0026rsquo;s about leading with action, delivering results swiftly, and being at the forefront of addressing challenges or incidents. Equally important is the willingness to share insights and discoveries, even when they may not lead to immediate, tangible outcomes. Investing time to understand and interpret your metrics is invaluable, as it not only enhances your expertise but also cements your status as a trailblazer and leader in your field.\nI was once added into a Slack channel only because I connected with a different team. They owned an upstream service. Later there was an issue to triage for ourselves; Unlike everyone else, spending time to figure out what\u0026rsquo;s wrong with our code, I was able to easily point out an outage in the upstream service affecting our feature.\nBeing first for non-deliverables Being first doesn\u0026rsquo;t need to be about deliverables. It can also be about good culture. Like you can also be first to:\n Give weekly shout outs to team members. Setup a monthly lunch gathering. Greet birthdays of team members or give a hug to a colleague whose recently loss someone. Share what you learned today with others. Talk about great weather. How well a meeting went. Being first at the office, train station or even waking up early allows you to think with zen.  Slowly others will follow the new cultural norms you set. You\u0026rsquo;d see people giving each other shout outs. Other teams trying to mimic your team\u0026rsquo;s togetherness.\nCompany success with being first Here are some reasons based on research and examples:\n Being first typically enables a company to establish strong brand recognition and customer loyalty before competitors enter the arena. For example, Netflix was the first company to offer online movie rental service, which gave it a huge advantage over traditional video stores like Blockbuster. Being first also gives you additional time to perfect your product or service and set the market price for the new item. For example, Apple was the first company to introduce a personal computer with a graphical user interface, which allowed it to dominate the market for years. Being first can also influence how people make choices, as they tend to prefer the options that come first: first in line, first college to offer acceptance, first salad on the menu – first is considered best. This is because people have a cognitive bias called the primacy effect, which means they remember and value the first information they encounter more than the later ones. Being first can also boost your leadership skills and personality traits, especially if you are a first-born child. Researchers from Duetsche Post Foundation found that firstborns have an advantage when it comes to emotional stability, persistence, being outgoing and social, an ability to take initiative and a willingness to take on responsibility, leading them towards top managerial positions.  As you can see, being first has many benefits that can help you succeed in your personal and professional life. So don\u0026rsquo;t be afraid to take risks, innovate, and lead by example. You never know what opportunities might open up for you if you are the first one to do something.\nMistakes  If you\u0026rsquo;re a team lead or manager then being always first isn\u0026rsquo;t going to help your team members grow. I recall having a tech lead, he was first to reply to every slack message in a support channel. For things I had to spend 10-60 minutes to figure out, he already had the answer in 2 minutes. It frustrated me very much. He cooled down after I let him know about it. We agreed that I should instead leave a \u0026ldquo;on-it\u0026rdquo; emoji, so others know I\u0026rsquo;m looking into it, and then I would reach out to him if needed. Being too quick to correct the mistakes of others. Often people realize their mistakes without needing to be told about it. Attempting to go first while going completing solo. Get a few people to buy into your initiative. Get feedback. Make your group/team/company first — even if it doesn\u0026rsquo;t mean leading or creating the team. As long as your part of the pack then that\u0026rsquo;s still a major win. Often just being part of a high-impacting team is the only extra nudge you need to stand out and get the promotion! Try to be first in a few things. Like don\u0026rsquo;t try to be first to learn about what\u0026rsquo;s new in WWDC, what\u0026rsquo;s new in fastlane, what\u0026rsquo;s new in your upcoming features, etc. Focus on one or two aspects and just build around it. Don\u0026rsquo;t overload yourself. Otherwise you can\u0026rsquo;t take on tasks — especially as the first person.  Other Tips  Gamify it. Make it fun. Do it with a group. Give praises and get praises for people who are first. Have a leaderboard. Often even leaving cool emojis for others will kick off a joyful culture that will get back to yourself. Being first requires stamina more than it requires intensity. Usually more tenured/experienced people are first. This means you will fail a lot early on. Embrace failure. By being first you\u0026rsquo;re more exposed to corrections. Focus on strengths, but also have plans to improve your weaknesses.  Hopefully by the end, the result would be you getting \u0026ldquo;First to the egg\u0026rdquo; 🥚🚀🏆!\n","permalink":"https://mfaani.com/posts/career/being-first-is-a-game-changer/","summary":"If you\u0026rsquo;re ahead of others, you become people\u0026rsquo;s go to person. It means more involvement. More challenge and opportunities to grow and network.\nBeing first means you can pave the way for others and be helpful. It\u0026rsquo;s one of the key traits of a lead.\nMain Advantages of being first You can set the tone, guidelines that you want others to follow. And not you following others. Often you\u0026rsquo;re late into some bad architecture.","title":"Being First is a Game Changer"},{"content":"Today I\u0026rsquo;m going to discuss another fun and common challenge. It\u0026rsquo;s the Longest Common Subsequence a.k.a. LCS.\nI\u0026rsquo;ll first focus on discussing a pain point I went through when I was trying to compare the algorithm I deduced on my own vs a few other algorithms I saw online. Our algorithms seemed very similar. Yet different. It made debugging my code based on other code very difficult. This is a very common problem I face when I doing leetcode. For whatever reason my way of coding is more than often to a good extent different from what I see from others. I find it easier to compare the high level of my code with a video from Youtube since most other submissions on Leetcode itself don\u0026rsquo;t contain a good breakdown pseudocode of what\u0026rsquo;s happening.\nLet\u0026rsquo;s first explain the problem. Suppose we have: \u0026ldquo;dabc\u0026rdquo; and \u0026ldquo;aafb\u0026rdquo;. The longest common subsequence is \u0026ldquo;ab\u0026rdquo;. Other examples:\n   Text1 Text2 LCS     \u0026ldquo;abc\u0026rdquo; \u0026ldquo;abc\u0026rdquo; \u0026ldquo;abc\u0026rdquo;   \u0026ldquo;dab\u0026rdquo; \u0026ldquo;db \u0026ldquo;db\u0026rdquo;   \u0026ldquo;eaf\u0026rdquo; \u0026ldquo;abeaf\u0026rdquo; \u0026ldquo;eaf\u0026rdquo;   \u0026ldquo;kpm\u0026rdquo; \u0026ldquo;qspzm\u0026rdquo; \u0026ldquo;pm\u0026rdquo;    The way to do this is to have two pointers grid. One pointer for the current index you\u0026rsquo;re trying to match for text1, another for text2. Each location in the grid below corresponds to the two pointers. Example:\ntext1: kpm\ntext2: qspzm\nApproach One  0 1 2 k p m 0 q 🏁 x x 🔚 1 s x x x 🔚 2 p x x x 🔚 3 z x x x 🔚 4 m x x 🎯 🔚 🔚 🔚 🔚   🏁: start 🎯: end 🔚: out of bounds. return 0  Start from (0,0) and finish at bottom right corner.\nreturn 0 for any value that\u0026rsquo;s beyond the right or top edges of the grid.\n Approach Two  0 1 2 k p m 🔚 🔚 🔚 0 q 🔚 🎯 x x 1 s 🔚 x x x 2 p 🔚 x x x 3 z 🔚 x x x 4 m 🔚 x x 🏁 Start from (2,4) and finish at top left corner.\nreturn 0 for any value that\u0026rsquo;s beyond the left or bottom edges of the grid.\n My confusion originated from the fact that I was coding from one approach, but trying to fix my code based on another approach — without realizing the approaches are different! 🙃🫤🫤😐🤔\n Approach One Code /// Starts from 0,0. Ends at m * n /// Out of bounds would be the BOTTOM and RIGHT edges. func longestCommonSubsequence(_ text1: String, _ text2: String) -\u0026gt; Int { var t1: [Character] = Array(text1) var t2: [Character] = Array(text2) func helper(s: Stage) -\u0026gt; Int { // 💡 doing an index check vs subscripting which can crash is a good middle ground. Index checking is a lot better than having to create a safe-subscript that returns an optional where you ultimately have to default its value to something.  guard s.x \u0026lt; t1.count \u0026amp;\u0026amp; s.y \u0026lt; t2.count else { return 0 } // 💡 Skip certain neighbors in the graph if items at the node match.  if t1[s.x] == t2[s.y] { return helper(s: Stage(s.x + 1, s.y + 1)) + 1 } return max(helper(s: Stage(s.x + 1, s.y)), helper(s: Stage(s.x, s.y + 1))) } return helper(s: Stage(0, 0)) } Helper 💡 Using this helper to build an easier mental model was crucial for me.\nstruct Stage: Hashable { var x: Int var y: Int init(_ x: Int, _ y: Int) { self.x = x self.y = y } } extension Array\u0026lt;Array\u0026lt;Int\u0026gt;\u0026gt; { subscript (stage: Stage) -\u0026gt; Int { get { self[stage.y][stage.x] // a good way to conceptualize is: `x` is as progress, `y` is as level.  } set { self[stage.y][stage.x] = newValue } } } Approach Two Code /// Starts from m * n. Ends at 0 * 0 /// Out of bounds would be the TOP and LEFT edges.  func longestCommonSubsequence(_ text1: String, _ text2: String) -\u0026gt; Int { var t1: [Character] = Array(text1) var t2: [Character] = Array(text2) func helper(_ s: Stage) -\u0026gt; Int { guard s.x \u0026gt;= 0 \u0026amp;\u0026amp; s.y \u0026gt;= 0 else { return 0 } if t1[s.x] == t2[s.y] { return helper(Stage(s.x - 1, s.y - 1)) + 1 } else { return max(helper(Stage(s.x - 1, s.y)), helper(Stage(s.x, s.y - 1))) } } return helper(Stage(t1.count - 1, t2.count - 1)) } 💡 When is a 2D Graph a good model for your challenge? What does the 2D grid model? Any time you have two items with two sets of indexes and you have to compare them as you go, then a 2D graph becomes a very good candidate. The grid models the following:\n It models a graph. Not a tree. Every node in the graph, marks index combinations of the two strings. You can start from any node.  If you start from an edge node then you have to expand into only two direction. If you start from a side-node then you have to expand in three directions. If you start from a node that\u0026rsquo;s not on the edges, then if you have to expand in all four directions. Staring from a corner is just much more natural.    Your traversal can be to your adjacent neighbors or diagonal. In this challenge you will skip adjacent neighbors if certain conditions are met. That is if the values of the indexes in the strings match then move diagonally otherwise bifurcate into the two adjacent indexes of the grid.\nSummary and some other confusions I had along the way To figure out the issue with my code I thought I had look up \u0026lsquo;top down vs \u0026lsquo;bottom up\u0026rsquo; \u0026amp; \u0026lsquo;memoization vs tabulation\u0026rsquo;. But while those are unrelated to figuring out this issue and might confuse you even more, unless fully understood, understanding their differences might make it easier for you to compare different approaches. Try not to spend too much time on the differences. Instead focus on learning how to break down your problem into a simple subproblem.\nAbout Top down approach, Bard said:\n Recursive algorithms are called top-down because they break down a problem into smaller and smaller sub problems until they reach a base case, which is a simple problem that can be solved directly. Once the base case is solved, the results are used to solve the larger sub problems, and so on, until the original problem is solved. This process is called recursion, and it is a powerful way to solve many different types of problems.\nThe term \u0026ldquo;top-down\u0026rdquo; is used to describe this approach because the algorithm starts at the top of the problem hierarchy and works its way down to the base cases. This is in contrast to a bottom-up approach, which starts at the base cases and works its way up to the top of the problem hierarchy.\n Hence both approaches are top down. Only that their direction is different. The first time I heard the term \u0026lsquo;top-down\u0026rsquo;, I thought it had something to do with the fact that in tree traversals you start at the root and the root is always (in algorithm world, not real life) at the top of tree. But it has nothing to do with direction. It\u0026rsquo;s more about approach and if big sub problems depend on small sub problems vs if you\u0026rsquo;re building incrementally.\nOptimizations For both of these solutions you can do memoization.\n memoization for when you start from (m,n). See this gist for its code.  Essentially where you start from will dictate the direction. The direction often happens to also dictate the most performant algorithm you can choose.\nIf you wanted to go for a tabulation approach, then it\u0026rsquo;s better if start from 0,0 and build from there.\n tabulation for when you start from (0,0). See this gist for its code   With tabulation you compute sub problems in advance, with memoization you compute sub problems on demand.\n Note on readability The returning value for the two approaches are:\n return helper(Stage(0, 0)) return helper(Stage(t1.count - 1, t2.count - 1))  Which one makes more sense?\nThe second approach can be slightly more readable in this case. Readers might be confused as to why you\u0026rsquo;re returning the value for Stage(0,0) and not the last. That said for me personally it was easier to write code that starts from (0,0) and expand.\nAre you always able to write your from both directions? Trees No. Graphs Yes.\nTo better understand this, what is the node root node below?\n  Where is the root?   Click to see answer 👇 There isn\u0026rsquo;t any root. This isn\u0026rsquo;t a tree, it\u0026rsquo;s a graph and that twisted my perception for the longest time. Any moment that you have a left and right movement then it\u0026rsquo;s no longer a tree. With Trees you can only go up and down.\n Cycles / multiple paths, break the tree property. A tree can\u0026rsquo;t have multiple ways to get to the same node — a graph can.\n Any of the A,B,C,D nodes can be deemed as a good starting node of the graph. To be clear you could start from any node of the graph, but starting from either A or C seems the most natural / easiest.\n If you pick A as your starting node, then C becomes the last visiting node of the graph. If you pick C as your starting node, then A becomes the last visiting node of the graph. If you pick B as your starting node, then D becomes the last visiting node of the graph. If you pick D as your starting node, then B becomes the last visiting node of the graph.  If you add the edges it becomes more clear that it\u0026rsquo;s a graph.\nAs humans we usually start from the origin node (0,0) but also less frequently from the destination node (m,n).\n Acknowledgements Special thanks to Tim Vermeulen for answering dozens of my questions so I was able to put this post together and Maks Verner for writing this post\n","permalink":"https://mfaani.com/posts/interviewing/dynamic-programming/the-effect-of-direction-on-recursion-and-understanding-code/","summary":"Today I\u0026rsquo;m going to discuss another fun and common challenge. It\u0026rsquo;s the Longest Common Subsequence a.k.a. LCS.\nI\u0026rsquo;ll first focus on discussing a pain point I went through when I was trying to compare the algorithm I deduced on my own vs a few other algorithms I saw online. Our algorithms seemed very similar. Yet different. It made debugging my code based on other code very difficult. This is a very common problem I face when I doing leetcode.","title":"The effect of direction on recursion and understanding code - Longest Common Subsequence"},{"content":"Today we\u0026rsquo;re solving https://leetcode.com/problems/asteroid-collision/\nImagine if we had the following:\n1 5 3 8 6 -\u0026gt; -\u0026gt; \u0026lt;- \u0026lt;- -\u0026gt; Each number represents the size of an asteroid. Each asteroid is either going left or right. Bigger asteroids destroy smaller asteroids. Asteroids with same size both get destroyed. If two asteroids are going in the same direction, they don\u0026rsquo;t hit each other, because all are going in the same speed.\nThe challenge is to return: which asteroids will remain at the end?\nWe can demonstrate the direction and size with an array where positive values mean moving towards right while negative values mean moving towards left: [1,5, -3, -8, 6]\nLet\u0026rsquo;s start from the beginning:\nstart: [1,5, -3, -8, 6] [1] + 5: Same direction. Don't collide. [1,5] remain. [1,5] + -3: Different directions. -3 is destroyed. [1,5] remain. [1,5] + -8: Different directions. 5 is destroyed. [1,-8] remain. [-8] continues towards 1 and collides and destroys 1. [-8] remains. [-8] + -6: Same Direction. Don't collide. [-8, -6] remain. end: [-8, -6] Solution One Pseudocode copy array. loop through: left to right for each asteroid, compare it with its next asteroid: if left asteroid is going right and next asteroid is going left, then: figure out the collision: remove destroyed asteroids pass new indexes for comparison if there\u0026#39;s no sign change, then: continue Code class Solution1 { var i = 0 func asteroidCollision(_ asteroids: [Int]) -\u0026gt; [Int] { var asteroids = asteroids while i \u0026lt; asteroids.count { handleCollision(from: i, arr: \u0026amp;asteroids) } return asteroids } func handleCollision(from i1: Int, arr: inout [Int]) { let i2 = i1 + 1 guard i1 \u0026gt;= 0 \u0026amp;\u0026amp; i1 \u0026lt; arr.count \u0026amp;\u0026amp; i2 \u0026gt;= 0 \u0026amp;\u0026amp; i2 \u0026lt; arr.count else { i += 1 return } if arr[i1] \u0026gt; 0 \u0026amp;\u0026amp; arr[i2] \u0026lt; 0 { if arr[i1].magnitude \u0026gt; arr[i2].magnitude { // remove right asteroid arr.remove(at: i2) // redo comparison for same index, but with updated next index. handleCollision(from: i1, arr:\u0026amp;arr) } else if arr[i1].magnitude == arr[i2].magnitude { // remove both asteroids arr.remove(at: i2) arr.remove(at: i1) // redo comparison for same index, but with updated next index. handleCollision(from: i1 - 1, arr: \u0026amp;arr) i -= 1 } else { // remove left asteroid arr.remove(at: i1) handleCollision(from: i1 - 1, arr:\u0026amp;arr) i -= 1 } } else { // move forward without removing any asteroids i += 1 } } } Time Complexity  for loop: O(n)  remote(at: Int): O(n)   Total: O(n * n)  Space Complexity O(1): If the parameter was passed as inout instead, then it would have been O(1), because we really didn\u0026rsquo;t need another variable. We just did it to make it mutable.\nSolution Two Idea Let\u0026rsquo;s see why using a stack can be more effective than an array in this context:\n A stack is a linear data structure following the Last In, First Out (LIFO) principle, where the most recently added item is the first to be removed, using operations like push, pop, and peek. In contrast, an array allows direct access to any element via indexing, which isn\u0026rsquo;t essential for this problem.\n even if technically built on an array. We don\u0026rsquo;t need random access or frequent inserts/removals—key array traits.\n💡 We need to (repeatedly) compare adjacent objects at the end of the array, a stack structure aligns perfectly, even if technically built on an array. We don\u0026rsquo;t need random access or frequent inserts/removals—key array traits.\n💡 Stacks are usually perceived as \u0026lsquo;vertical\u0026rsquo; data structures while arrays are perceived as \u0026lsquo;horizontal\u0026rsquo; data structures. However you shouldn\u0026rsquo;t limit them to such. Both can be used in horizontal or vertical contexts.\nPseudocode for each asteroid add it to stack handle collisions in stack recursively — until there\u0026#39;s no collision endloop return stack Code class Solution2 { var stack: [Int] = [] func asteroidCollision(_ asteroids: [Int]) -\u0026gt; [Int] { for asteroid in asteroids { stack.append(asteroid) handleStackCollision() } return stack } func handleStackCollision() { guard stack.count \u0026gt; 1 else { return } let right = stack[stack.endIndex - 1] let left = stack[stack.endIndex - 2] if left \u0026gt; 0, right \u0026lt; 0 { if left.magnitude == right.magnitude { // pop both stack.removeLast(2) } else if left.magnitude \u0026gt; right.magnitude { // pop right stack.popLast() } else { // pop left stack.remove(at: stack.count - 2) } handleStackCollision() } } } Time Complexity  for loop: O(n)  append: O(1) traverse back: O(n) removeLast(2): O(2) remove(at: stack.count - 2): O(1)   Total: O(n) NOTE1: The maximum number of collisions is O(n - 1). As a result we won\u0026rsquo;t ever end up traversing the whole array each time. Because of that, the total is O(n). NOTE2: remove(at: Int) time complexity depends on the index. NOTE3: With the leetcode test cases, solution two had only slightly better results. However when I tested things in Xcode with a much larger dataset, solution one errored out due to stack overflow. Solution two worked just fine. My point is Leetcode test samples can mess up your timings and give you the wrong impression about the timings of your solutions.  Space Complexity Stack creation: O(n)\nInteresting notes I was initially trying to use the stack with just recursion. Then I tried to solve it with iteration. It didn\u0026rsquo;t work. Simply put I had to iterate for each new addition. But then do recursion until asteroids stopped colliding.\nIn that sense it was a new challenge, because I\u0026rsquo;ve always seen challenges where I either had to iterate with a simple for-loop or do recursion, never both, let alone have it combined with stacks. It was a really fun challenge.\n Use iteration to get to all items. Then depending on your need, use recursion or whatever\u0026rsquo;s necessary to process the current iteration.\n Conclusion and things I learned along the way  Have a list of different data structures in front of you. It can help trigger your brain. I wasn\u0026rsquo;t able to come up with the idea of using a Stack on my own, but some tips for being able to come up with it are: if you have some \u0026lsquo;undoing\u0026rsquo; to do, like undo adding something, then a stack can be a good choice. FWIW this problem could have also been solved with a linked list as well. I just didn\u0026rsquo;t code that. Writing the pseudocode with indentation will make it easier to reason with, visualize the steps and come up with its time complexity a lot easier. Time Complexity of remove(at: Int), is not always O(n). It\u0026rsquo;s more accurate to say O(array.count - i). Example for an array of 1000 elements:  array.remove(at: 2) would be more or less O(999) because you\u0026rsquo;re shifting 999 items in the array. array.remove(at: 998) would be more or less O(1) because only you\u0026rsquo;re only shifting one item in the array.   Leetcode test cases may not always be a perfect reflection of the time complexity of your solutions.  ","permalink":"https://mfaani.com/posts/interviewing/arrays/asteroids-collision/","summary":"Today we\u0026rsquo;re solving https://leetcode.com/problems/asteroid-collision/\nImagine if we had the following:\n1 5 3 8 6 -\u0026gt; -\u0026gt; \u0026lt;- \u0026lt;- -\u0026gt; Each number represents the size of an asteroid. Each asteroid is either going left or right. Bigger asteroids destroy smaller asteroids. Asteroids with same size both get destroyed. If two asteroids are going in the same direction, they don\u0026rsquo;t hit each other, because all are going in the same speed.","title":"Asteroids Collision"},{"content":"Question: How many envelops can you fit into another?\nEach envelope has a 2D representation. [4,5] -\u0026gt; width = 4, length = 5\nHow many envelops can you fit into one another without rotating any envelopes. Bare in mind you can\u0026rsquo;t fit in two evelopes with same width or height.\nThis is question is very much like a Russian Doll question. Which that question itself uses an Longest increasing subsequence algorithm to solve. If you don\u0026rsquo;t know about LIS then check my post Longest Increasing Subsequence Length\nHigh Level Idea  Sort it by width Then sort envelopes with same width by height. Sorting by height is the tricky part.   The direction you sort is important. It can inverse the impact on your flow. I often got confused with the direction of sorting. You check out my post on Which Way Am I Sorting?). In short the the array’s order will match with the direction of the angle bracket (\u0026lt; or \u0026gt;) you use. Think of right and left, instead of up and down, or of ascending and descending.\n Explanation So say you\u0026rsquo;re given the following envelopes:\n[ [5,4], [6,5], [6,9], [2,3], [6,8] ] Depending on the order in the original array, sorting it by width will can result in any of the following arrays.\n[ [2,3], [5,4], [6,7], [6,9], [6,8] ] or [ [2,3], [5,4], [6,8], [6,9], [6,7] ] or [ [2,3], [5,4], [6,9], [6,7], [6,8] ] or [ [2,3], [5,4], [6,7], [6,8], [6,9] ] or [ [2,3], [5,4], [6,9], [6,8], [6,7] ] or [ [2,3], [5,4], [6,8], [6,7], [6,9] ] So now things are sorted by the width. Great.\nLet\u0026rsquo;s just assume we ended up with this order:\n[2,3], [5,4], [6,7], [6,9], [6,8] \u0026gt; nil✔️ 4\u0026gt;3✔️ 7\u0026gt;4✔️ 9\u0026gt;7✔️ 8\u0026gt;9❌ Based on this approach, four evelopes will fit. But we know that\u0026rsquo;s incorrect. Because the last three items have an identical width of 6.\nHow can we find envelopes that fit if we have multiple with same width? If width is identical then just ignore it. Let\u0026rsquo;s try it:\n[2,3], [5,4], [6,7], [6,9], [6,8] \u0026gt; nil✔️ 4\u0026gt;3✔️ 5\u0026gt;4✔️ 6==6 (width same)❌ This works. However it\u0026rsquo;s not forward proof. Like if our initial width sort ended with [6,9] being the first item and also we had a additional item at the end [7,8]\n[2,3], [5,4], [6,9], [6,7], [6,8] [7,8] \u0026gt; nil✔️ 4\u0026gt;3✔️ 5\u0026gt;4✔️ 6==6 (width same)❌ 8\u0026gt;9❌ You can\u0026rsquo;t add [6,7] or [6,8]. Because 6,9 was selected before and 9 is bigger than 8\nDetail Explanation To say things differently and narrow the problem down. Our problem is more or less like the following:\n[5,4], [6,z],[6,x],[6,y], [7,8] Assumption: z \u0026gt; y \u0026gt; x You have to pick either x,y,z in a way that your pick is bigger than 4 and smaller than 8.\nIf you sorted items with same height in ascending order then you wouldn\u0026rsquo;t know which one to pick. You have to try and error — when width is 6.\nTrick However if you sorted (items by same height) in descending order by their height, and found longest selection of envelopes in \u0026ldquo;increasing height\u0026rdquo; order then it becomes a standard LIS algorithm. Because:\n  Because when chosing from same width envelopes, mathematically you can only select a maximum of one envelope from z, y, x when it\u0026rsquo;s sorted in reverse. If it was sorted in ascending order then all of them can be been picked. FWIW you may end up not picking any, if none of z, y, x satisfy the requirement of being \u0026lsquo;bigger than 4 \u0026amp; smaller than 8\u0026rsquo;.   To find the longest increasing envelopes in height, we simply use LIS - Longest Increasing Subsequence.\nSo if we sorted the ones with same width in descending like below and did and LIS on all the heights then we\u0026rsquo;d end up in this order:\n[5,4],[6,9],[6,8],[6,7],[7,8] Our selected envelopes will be:\n[5,4],[6,7],[7,8] Code class Solution { func maxEnvelopes(_ envelopes: [[Int]]) -\u0026gt; Int { var envs = envelopes envs.sort(by: { // 1 \u0026amp; 2 if $0.first! == $1.first! { return $0.last! \u0026gt; $1.last! } else { return $0.first! \u0026lt; $1.first! } }) var arr: [Int] = [] // 3 let heights = envs.map {$0.last!} // 4 return lengthOfLIS(heights) } } func lengthOfLIS(_ nums: [Int]) -\u0026gt; Int { var dp: [Int] = Array(repeating: 1, count: nums.count) for i in 0...nums.count - 1 { for j in 0..\u0026lt;i { if nums[j] \u0026lt; nums[i] { dp[i] = max(dp[i], dp[j] + 1) } } } return dp.max()! } Explanation  Sort elements in ascending width order. When with is identical, then sort in descending height order Map the sorted result to just their heights. Do an LIS on the heights array.  Time Complexity  sorting: O(n * log n) mapping: O(n) LIS: O(n * n). Total: O(n * n). Note: If you used the binary search for LIS step, then the total time complexity would be: O(n * log n)  Space Complexity  O(n)  ","permalink":"https://mfaani.com/posts/interviewing/dynamic-programming/how-many-envelopes-can-you-fit-into-another/","summary":"Question: How many envelops can you fit into another?\nEach envelope has a 2D representation. [4,5] -\u0026gt; width = 4, length = 5\nHow many envelops can you fit into one another without rotating any envelopes. Bare in mind you can\u0026rsquo;t fit in two evelopes with same width or height.\nThis is question is very much like a Russian Doll question. Which that question itself uses an Longest increasing subsequence algorithm to solve.","title":"How Many Envelopes Can You Fit Into Another?"},{"content":" Attention: This post was updated to include the alternate solution that uses binary search. It reduces the Time Complexity from O(n * n) to O(n * log n).\nBefore we present the question. Let\u0026rsquo;s figure out what a subsequence is:\nWhat\u0026rsquo;s a subsequence? Any selection of items from the original array. The selection must respect the order. Meaning for [1,2,3,4,5] only two of the four below are subsequences:\n[1,2,3,4,5] ✅ [1,4,3,2,5] ❌ order not respected [1,2,5] ✅ [5,1] ❌ order not respected What\u0026rsquo;s the difference between a subsequence and a subarray? Subarray, is just like subsequence, that is order must be respected. Additionally the sub-array has to be made of continuous elements.\n[1,2,3,4,5] ✅ [1,2,3,5] ❌ gap [1,2,3] ✅ [2,4] ❌ gap What\u0026rsquo;s a longest increasing subsequence? This is also known as LIS.\nFor [0, 3, 1, 7, 5, 2, 8] the longest increasing subsequence is: [0,1,7,8].\nFor [2,2,2,2]. LIS is: [2].\nFor [3]. LIS is: [3]\nIn this post, we\u0026rsquo;re only going to calculate the length of the LIS. We won\u0026rsquo;t construct the actual subsequence itself. To learn how to construct the path, see here\nExplanation So this problem can be broken into subproblems i.e.\n The LIS of index 6, depends on the LIS of all indices between 0-5. The LIS of index 5, depends on the LIS of all indices between 0-4. \u0026hellip; The LIS of index 1, depends on the LIS of index 0 The LIS of index 0, is 1. Because every element has a subsequence of 1.  Whenever we can turn our problem into subproblems, then Dynamic programming is a good candidate for finding a solution.\nTo do this, let\u0026rsquo;s create an array named dp to represent the the answer to the subproblem for a given index.\nlet arr = [0, 3, 1, 7, 5, 2, 8] We\u0026rsquo;ll create an array with the same length of our original array. Default all the values to 1. Example: var dp: [Int] = Array(repeating: 1, count: arr.count).\nLet\u0026rsquo;s just assume we want to calculate the LIS all the way to index 4. And we\u0026rsquo;ve already updated the value for all previous indexes. How do you think we need to update dp[4]:\n [0, 3, 1, 7, 5, 2, 8] ↑ dp[4] Which of the following would it be?\n The value for dp[4] will be max(dp[0] + 1, dp[1] + 1, dp[2] + 1, dp[3] + 1) The value for dp[4] will be max(dp[0] + 1, dp[1] + 1, dp[2] + 1)  The correct answer is the second line.\nFirst line is incorrect because 7 is bigger than 5. dp[3] + 1 shouldn\u0026rsquo;t be considered.\nThis means that assuming there is at least one item in the array, the default value should then be 1.\nIn short:\n For every index, we use compare its value with its previous indices  If current index is bigger, then we increase the LIS length between the two indices. At any given index we perform a max between all nodes that were able to increase the subsequence to that point.   We do this till the end of the array. Then do a max against our dp array.  Pro tip Use a paper and go through this example again by yourself. Just compare any two indexes, add to the previous, do a max. I was then able to reason with it a lot lot easier on paper. Here\u0026rsquo;s what I wrote:\n  Longest Increasing Subsequence Length  Code func lengthOfLIS(_ nums: [Int]) -\u0026gt; Int { var dp: [Int] = Array(repeating: 1, count: nums.count) for i in 0...nums.count - 1 { for j in 0..\u0026lt;i { if nums[j] \u0026lt; nums[i] { dp[i] = max(dp[i], dp[j] + 1) } } } return dp.max()! } Time Complexity  for-loop: O(n)  for-loop from beginning till current end index: O(n)    Total = O (n * n)\nSpace Compleixty The dp array: O(n)\nImportant Notes: Assuming your nodes are as such i.e. just a degenerate tree:\na -\u0026gt; b -\u0026gt; c -\u0026gt; d  A lot of times when you\u0026rsquo;re going from one node to the other, all you care about is going from node c, to node d. However in this question, it\u0026rsquo;s about an operation that goes from all previous nodes to this node. i.e. we need to calculate: a -\u0026gt; d, b -\u0026gt; d, c -\u0026gt; d and then do a comparison between them all. This is what makes this question different. Example: In the coin change question, we only needed to calculate things was from previous to next.  Like if all you need to do is add and access certain indexes and not search or remove items, then array and dictionary aren\u0026rsquo;t different so much.\n Additionally in this question, going from left to right or right to left don\u0026rsquo;t make a difference in terms of our answer.  If we start from the beginning, then each index will get added to all answers of its previous indices — if it\u0026rsquo;s bigger than the value being compared to. If we start from the end, then each new index will still get added to all answers of its next indices — if it\u0026rsquo;s smaller than the value being compared to\u0026hellip;    It really doesn\u0026rsquo;t matter which way we select.\nAlternate solution with Binary Search  Any time you hear order, increasing, sorted, decreasing, then perhaps binary search is a helpful way of doing things. This isn\u0026rsquo;t always true, but in this case it is. Think of binary search as an optimization mechanism. Not as a solution finder i.e. try to first solve the challenge without using binary search. Then improve your solution using binary search.  Simply put create a list, append or replace items.\n Append items that are bigger than the current biggest -\u0026gt; Help increase the length of the list. Replace items the first item that is bigger the new item -\u0026gt; Helps reduce the bar for adding smaller items to the list. Note: Such a list like this won\u0026rsquo;t always contain the correct items for the LIS, however it will always have the correct length of the LIS. With the Binary Search approach you optimize the space, but aren\u0026rsquo;t able to regenerate the actual LIS.  It\u0026rsquo;s hard to explain it with words. I\u0026rsquo;ll try to explain it with just two examples, but also see this video as well. Maybe even before this.\nExample1:\n[0, 15, 1, 7, 5, 4, 8, 3] list = [] [] + 0 -\u0026gt; [0] we're expanding the list. [0] + 15 -\u0026gt; [0, 15] we're expanding the list. [0, 15] + 1 -\u0026gt; [0, 1] we've replaced our biggest value with 1. This makes the list acceptable for smaller numbers as its next addition i.e. if 15 was kept then no other number from the list could have been added. [0, 1] + 7 -\u0026gt; [0, 1, 7] we're expanding the list. [0, 1, 7] + 5 -\u0026gt; [0, 1, 5] we've replaced our biggest value with 5. This makes the list acceptable for smaller numbers as its next addition i.e. if 7 was kept then we would have not been able to add 2. [0, 1, 5] + 4 -\u0026gt; [0, 1, 4] we've replaced our biggest value with 4. This makes the list acceptable for smaller numbers as its next addition. [0, 1, 4] + 8 -\u0026gt; [0, 1, 4, 8] we're expanding the list. [0, 1, 4, 8] + 3 -\u0026gt; [0, 1, 3, 8] we've replaced the first/smallest item that's bigger than 3. This makes the list acceptable for smaller numbers as its next addition — while maintaining its order. We find the index to replace by doing a 'search to find the first index that has a value that's smaller than the new item (3) but also that the value at its next index (4) is greater than the new item. Note: While the replacement doesn\u0026rsquo;t change the length, it improves the quality of the list by making it more likely to accept future additions. Future additions which will ultimately shrink down the last item. Which then means more smaller items can get appended. This is important because the search algorithm that we are using to find the insertion point for the new element depends on the list being sorted. If you\u0026rsquo;re still confused about this step, then see Example 2. It better demonstrates why we\u0026rsquo;re doing this.\nExample2:\n[0, 5, 15, 2, 3, 4] list = [] [] + 0 -\u0026gt; [0] [0] + 5 -\u0026gt; [0, 5] [0, 5] + 15 -\u0026gt; [0, 5, 15] [0, 5, 15] + 2 -\u0026gt; [0, 2, 15] \u0026lt;- Made list more acceptable for smaller numbers to be appended. If we can get rid of 15, then we're in a much stronger position to append numbers to the list. [0, 2, 15] + 3 -\u0026gt; [0, 2, 3] \u0026lt;- We did it! Got rid of 15. We have a good chance of appending a new value. [0, 2, 3] + 4 -\u0026gt; [0, 2, 3, 4] \u0026lt;- Appended new value! Code class Solution { func lengthOfLIS(_ nums: [Int]) -\u0026gt; Int { var lis: [Int] = [] guard nums.isEmpty == false else { return 0 } for num in nums { // If it\u0026#39;s bigger than biggest then append it if num \u0026gt; lis.last ?? Int.min { lis.append(num) // if it\u0026#39;s smaller than smallest then replace the smallest.  } else if num \u0026lt; lis.first ?? Int.max { lis[0] = num } else { // find a suitale place to replace it  searchAndReplace(num, in: \u0026amp;lis, startIndex: 0, endIndex: lis.count - 1) } } return lis.count } /// Finds the correct replacing index from within a specific range. Then replaces it.  /// If bigger than middle \u0026amp;\u0026amp; smaller than next element after middle index then replace the element after the middle index.  /// Example: If list is `1,3,5` and we\u0026#39;re trying to add `4`, then since 4 is greater than 3 but less than 5, we\u0026#39;ll replace 5, with 4.  /// tldr the binary search isn\u0026#39;t to find an exact item. It\u0026#39;s more of finding the first/smallest item that\u0026#39;s bigger than our `num` field.  /// - Parameters: /// - num: new number to get added /// - arr: current list of items /// - startIndex: start index to search into /// - endIndex: end index to search into func searchAndReplace(_ num: Int, in arr: inout [Int], startIndex: Int, endIndex: Int) { let middle = (endIndex + startIndex) / 2 if num == arr[middle] { // do nothing } else if num \u0026gt; arr[middle] \u0026amp;\u0026amp; num \u0026lt;= arr[middle + 1] { arr[middle + 1] = num } else if num \u0026gt; arr[middle] { searchAndReplace(num, in: \u0026amp;arr, startIndex: middle + 1, endIndex: endIndex) } else { searchAndReplace(num, in: \u0026amp;arr, startIndex: startIndex, endIndex: middle - 1) } } } I must admit, it\u0026rsquo;s hard to come up with the idea for such a solution. To come up with the idea for this question you have to iteratively improve it. Something like:\n I see \u0026ldquo;increasing\u0026rdquo;. Perhaps I can create something sorted and keep it sorted. Focus on the number of items and not actual subsequence. Once I found a solution through linear search, then maybe I can build on top of it and go with a binary search instead to optimize. What makes it counter-intuitive is to figure out how a bigger subsequence is replaced with a smaller subsequnce. Example from [80, 81, 82, 1, 2, 3, 4], how to replace the current [80 ,81 ,82] with [1, 2, 3, 4] which is really the heart of it. The idea is to combine together all sequences in an increasing order knowing that any number that\u0026rsquo;s bigger than the current biggest will enlarge the sequence for the current longest and any number smaller than the current  Time Complexity for-loop: O(n)\n search and find: O(log n) \u0026ndash; Assuming you\u0026rsquo;re using \u0026lsquo;binary search\u0026rsquo;  Total: O(n * log n)\nMemory Complexity O(n)\nOther Questions that use LIS: See my post on How many envelopes can you fit into another a.k.a the Russian Doll Envelope question.\nAcknowledgements Shout out to Take U forward for their YouTube post. I wasn\u0026rsquo;t able to get a good grasp on the binary search approach without it.\n","permalink":"https://mfaani.com/posts/interviewing/dynamic-programming/longest-increasing-subsequence-length/","summary":"Attention: This post was updated to include the alternate solution that uses binary search. It reduces the Time Complexity from O(n * n) to O(n * log n).\nBefore we present the question. Let\u0026rsquo;s figure out what a subsequence is:\nWhat\u0026rsquo;s a subsequence? Any selection of items from the original array. The selection must respect the order. Meaning for [1,2,3,4,5] only two of the four below are subsequences:\n[1,2,3,4,5] ✅ [1,4,3,2,5] ❌ order not respected [1,2,5] ✅ [5,1] ❌ order not respected What\u0026rsquo;s the difference between a subsequence and a subarray?","title":"Longest Increasing Subsequence Length"},{"content":"This is the 1st post of \u0026ldquo;Everything I learned from blogging with Hugo\u0026rdquo; series\nI started blogging in Dec 2021. It\u0026rsquo;s been a wonderful journey. Has enabled me to gather my thoughts in a far more structured way. The series is based on my setup of Hugo - Netlify - GitHub. I\u0026rsquo;ll shared my knowledge in terms of Hugo knowledge, how to write a blog post and more. If anyone is interested in the Netlify setup, see Hugo Quick Start then Netlify - Hugo setup.\nBlogging is the easiest and most editable form of publishing content In comparison to making videos and other rich media content, writing is far more easier. You don\u0026rsquo;t need a mic, camera, lighting, background, editing skills, story telling, good accent.\nAlso editing a post takes a few minutes. Changing things in a video can take between 10 minutes to 2hrs. And more importantly, you just might have to re-post the video to a different url. For a blog post, you can just include \u0026ldquo;last updated on\u0026rdquo; date to your post and make the edit and re-publish to the same url.\nWhen and why you should start creating a blog?  The best time to create a blog was 5yrs ago. Second best time is today - Chinese Proverb\n Each and every one of us engineers have dealt with unique career paths, apps, teams, architecture, challenges. We all have a lot of insight to share. I used to write a lot on Stack Overflow. I still do, but blogging lets me explore my own passions more deeply and be less reactive to a question on Stackoverflow. It\u0026rsquo;s a different and more fluid medium. Helps your future self to have organized learning.\nAdditionally it widens your network and can help add prestige to your profile.\nContent Creation  Create drafts asap: i.e. when you have an idea or started learning something. I\u0026rsquo;ve forgotten key details days after let alone months down the road. Don\u0026rsquo;t just write bullet points, jot down anything and everything. Often when I come back to my bullet points: \u0026ldquo;css variables\u0026rdquo;, it\u0026rsquo;s been 6 months since. I\u0026rsquo;ve forgotten everything I\u0026rsquo;ve learned. At other times simply because I have my attention more focused on some new challenge, I think things I learned in the past are less important. Your brain tricks you a bit.   Content in your brain is like fresh food. If you don\u0026rsquo;t eat when it\u0026rsquo;s warm and fresh then it will go bad or just take a lot of time to defrost, or some more fresh food will come. Most things after they are learned become trivial and you think less of them. Truth is, anything can be fresh for the right person.\n   Write with the same width of your readers: When you\u0026rsquo;re writing your content in VSC, you may not realize how long and wide your paragraph is. Especially if you\u0026rsquo;re writing on a wide monitor (+24\u0026quot;). So try to set the width of your writing canvas as wide as how it appears in your blog. It avoid long and potentially confusing paragraphs.\n  Use headers from the beginning. Don\u0026rsquo;t let your post turn into a graveyard. Often when I have a large post and don\u0026rsquo;t know where to start from, then I just begin with my headers. I try to create sections along with a hierarchy i.e. I use # and ##. Then I move items around under different headers or merge them or what not. It allows me to envision things so much faster.\n    How I use headers to organize and order my post before I get into its details    Links can die, like Apple can remove a WWDC video. Make sure you annotate the link as much as possible. Example, don\u0026rsquo;t just share the link to the session. Share the link and the name, year and minute of the session. That way folks can perhaps google it and find it or maybe download the video and upload it elsewhere if allowed.\n  Don\u0026rsquo;t over explain. If you\u0026rsquo;re doing that, then usually it means you haven\u0026rsquo;t found the right break down, wording, story, image to explain things. Or at least don\u0026rsquo;t over explain too many things in a single blog post, unless your post is meant to cover a-z or be a post on the terminology of something. Small highly focused posts are often better. Some good examples of that are https://www.hackingwithswift.com and http://css-tricks.com. Most of the time they\u0026rsquo;re laser focused. They just go deep for a single subject. Not 5 at once. tldr it\u0026rsquo;s hard to have breadth and depth in the same post.\n Don\u0026rsquo;t do a tutorial and Q\u0026amp;A in a single post. Keep the tutorial simple. Keep the Q\u0026amp;A post complex. Instead of writing one gigantic post \u0026raquo; write five small posts on different concepts. Then write a sixth post that summarizes and pieces it all together. Often when I split a single post into multiple, I can rationalize about the post a lot easier, because our brains can only cache a certain amount of context at once. The key to being able to split it, is to to have good headers. Often that leads to finding duplicate content or bad ordering of content. In any case I highly recommend you watch this TED Talk by Dominic Walliman. He shares 4 principles for how to give an understandable talk:  Start from things everyone understands Don’t go too far down the rabbit hole. People can only take on a certain amount of information at any one time. If anyone’s interested then they’ll research themselves afterwards… Clarity beats accuracy. The temptation is to give the most scientifically accurate explanation. They tend to be long…As long as you get the person on the right path, then the goal is achieved. Explain why it’s cool. Why it’s relevant. It will make them remember it.      Size matters:: The bigger a post gets, the more refactoring, re-reviewing your code is required. Something that helps me to see the big picture all at once is adding a table contents to my post. Often I might opt of out using the table of contents, but after using it to help me see the big picture.\n  Topic selection:\n Be confident to post on topics that others have posted on before. Why?  Your tone, way of explaining or putting things together is unique. They might have missed some details. Things might have changed. Example how we automate things on GitHub is a forever changing subject. Writing a post is the best way to learn about things.   But also try to go for novel topics as well. Like some of my most read topics are on \u0026lsquo;Cocoapods manifest.lock file\u0026rsquo; or on \u0026lsquo;Why can\u0026rsquo;t Xcode show the caller?\u0026rsquo;    Use rich media: Post videos / gifs as well. They make your website rich. Example, instead of a screenshot of Apple\u0026rsquo;s Playground, actually take a short video and demo it. Videos have audio, show UX far easier, quicker. You don\u0026rsquo;t need to use them in every post, but just that you shouldn\u0026rsquo;t shy away from them. If you end up with rich media, then make sure it\u0026rsquo;s just 5-60 seconds long. Also instead of directly quoting someone, you could often take a screenshot of that within its real context. This makes your post richer. See this post. The image stands out a lot better and livelier.\n  Use focused media: Don\u0026rsquo;t take a screenshot of the entire page or IDE. Otherwise the image will be greatly zoomed out. If you take a focused image instead then, it\u0026rsquo;s easier for the reader to grasp. Take a look at the images of my other post and see how certain images are easier to grasp.\n  Use correct dates: If you stared the draft two months ago, but published today, then make sure the date readers see associated with the post on your blog is the \u0026lsquo;publish date\u0026rsquo;. Otherwise they might think the post was from two months ago and feel its dated.\n  Benefits of getting early feedback  Share a Netlify preview of your post with others when possible. It\u0026rsquo;s a really easy way to get help from a group of experts in a Tweet or in Slack. Often typos, but a lot of time people will come back to you and be like:  \u0026ldquo;Your message in your post isn\u0026rsquo;t clear, you\u0026rsquo;re talking about X, then suddenly jump to Y\u0026rdquo; \u0026ldquo;This line is just wrong. You\u0026rsquo;ve misunderstood what Foo means. That\u0026rsquo;s what Bar is\u0026rdquo; \u0026ldquo;Use this documentation, blog post, other slack thread\u0026rdquo; to improve/deepen on your post.   Often if you publish and then ask, people would be less caring. Previews are enticing much like how an early preview of a movie is. It makes the readers feel special. They truly are. After you made changes to your preview and published your post, make one last check on your blog itself. Make sure everything is correct. Not all feedback has to be from humans. You can also add a Spell Checker Extension on VSC. I usually make 6-10 spelling mistakes in a post 😅.  How can you make sure you don\u0026rsquo;t have a knowledge curse and things are clearly explained? Come back to the post in 2 days, again in 2 weeks, again in 2 months, again in 6 months and review them. See if everything still makes sense. I just came back to two of my old posts. I actually totally forgot I had them written. It\u0026rsquo;s a peculiar feeling. I also found:\n Typos and grammatical mistakes. Things that I skipped without explaining them. Better ways to explain certain things. Areas that made no sense or were cut off. Basically, the context of a fresh mind (who might have forgotten some things) is closer to that of a reader with a blank slate — while the context of an author\u0026rsquo;s mind is like one on steroids of knowledge and context.\nFrom my experience, the posts that I came back to 6 months after were the ones that I found lacking appropriate path and context for how I began and how I navigated the post.   You can only connect your past, current, and future selves if you have a record of your experiences, such as through writing or recording. Each version of yourself is different, but they are all connected. If you start something and use it regularly, your past and future selves are more connected. But if you learn something and don\u0026rsquo;t use it, your past and present selves are less connected.\nFor example, I have been working with UIKit regularly for a long time. Recently, I have been working with SwiftUI sporadically, and as a result, my SwiftUI knowledge is not very good. If I had blogged or documented my findings, it would have been easier for me to pickup SwiftUI from where I left.\n Often I re-read my posts on my iPhone. The look and feel of reading on an iPhone is very different, like wearing a different hat. I\u0026rsquo;m more focused too. I\u0026rsquo;m able to spot mistakes differently.\nInstead of just sharing a link or what originated, share something valuable from your post along with the link. That way you at least get traction from where you posted it (Twitter, Slack). If anyone was interested they\u0026rsquo;ll click to see more. Not always but usually you need:\n Blog post title: How we improved our app \u0026mdash;\u0026ndash;\u0026gt; How we reduced our app size by 30%. Blog\u0026rsquo;s description/subtitle: I\u0026rsquo;ll discuss some tips and tricks \u0026mdash;\u0026ndash;\u0026gt; I\u0026rsquo;ll discuss how creating a dSYM can cause bloat in your app and how stripping will remove it. Your message on Twitter: I\u0026rsquo;ve always struggled with what affects app size \u0026mdash;\u0026ndash;\u0026gt; Use nm -a \u0026lt;your binary\u0026gt; | grep - | wc -l to find out the number of debug symbols your binary has. Learn more in this blog post: [link] (OR: Discover the secrets in this article: [link]) Use an emoji or two when you feel like it. My most commonly used emojis are: 🚀🛠️💻👨🏼‍💻👨🏼🎉   Unless others are eagerly following you, then share as many places as you can. The more social media platforms you\u0026rsquo;ve joined the better. Share it on Twitter, LinkedIn, Slack, and also on https://iosdevdirectory.com. Since adding my blog on the iOS dev directory, I\u0026rsquo;ve have more views and more shares of my posts across the internet.\n Harsh feedback Your articles will be hit and miss. Often people would come at you be like \u0026ldquo;I\u0026rsquo;m not sure where you\u0026rsquo;re going with this post. This post doesn\u0026rsquo;t do any good. Your other posts were better\u0026rdquo;.\nTwo things:\n LISTEN to their feedback. BELIEVE in yourself.  Not everyone\u0026rsquo;s brain works the same way. If something is interesting to you and not others, then so be it. It might be that the person has learned what you\u0026rsquo;re writing about 10 yrs ago and doesn\u0026rsquo;t think it\u0026rsquo;s interesting to write about. Don\u0026rsquo;t let that dissuade you from writing what you think is good.\n Like in the same week I had people telling me my posts were NOT good and my posts were amazing!\n And often your posts are crap, but you have to come to that conclusion yourself.\nOther notes  Once you\u0026rsquo;ve published, then check all your links and images. Checking your links is important because it\u0026rsquo;s not something you can figure out by reading the markdown. You have to render it. Although you could scan your repo for the inclusion of localhost and see if any internal links are accidentally pointing to your local server.  You may simply forget to commit an image or have a Hugo variable/configuration that works ok locally. So double check the published version too. Be wary about accidentally publishing a post. This is why it\u0026rsquo;s good to check your website every once a while and not just the homepage, but go through all your posts to make sure no post has actually sneaked in. Often an old post will get in because you removed the draft: true field but your post date field was from 6 months ago. So it appears in your list of posts, but not in the front page because you have a lot of newer pages. I once copy/pasted links that were off of localhost instead of mfaani.com.   Add your website\u0026rsquo;s URL at the bottom of every image you\u0026rsquo;ve created. Helps your branding and gets you more clicks when the image is shared outside your post Do I need to make a Pull Request for every new post?  If it\u0026rsquo;s just a new post or some edit then just add a good commit message and push. Is it a blog structural change? A Theme change? A CSS change? Something that took a while for you to figure out? Something totally different? Then yes. Add a pull request with a decent PR description and then review it.   If you have too much content for a post, then something I\u0026rsquo;ve began doing recently is to comment out the portion of the post that I think is repetitive or too much detail. That way I can keep it in the same post, but not have it published yet and keep the post lean and focused.   ❤️ Writing gives me significant joy and confidence. Most people you see, including myself, never bother to come and tell you how much they appreciate your writing. But believe me, there are people out there who read your posts and get value from them. They might not say it, but they do. So keep writing and trust yourself!\nAlso after years of writing you level up and write on more novel stuff or more complex stuff. Your writing matures with you.\n Special Thanks To Antoine Van Der Lee for sharing this post in his amazing newsletter.\n","permalink":"https://mfaani.com/posts/content-creation/everything-i-learned-from-blogging-with-hugo/how-to-start-writing-a-post/","summary":"This is the 1st post of \u0026ldquo;Everything I learned from blogging with Hugo\u0026rdquo; series\nI started blogging in Dec 2021. It\u0026rsquo;s been a wonderful journey. Has enabled me to gather my thoughts in a far more structured way. The series is based on my setup of Hugo - Netlify - GitHub. I\u0026rsquo;ll shared my knowledge in terms of Hugo knowledge, how to write a blog post and more. If anyone is interested in the Netlify setup, see Hugo Quick Start then Netlify - Hugo setup.","title":"Tips for post creation"},{"content":"I do leetcoding every once a while, but keep forgetting some tree basics. This post here it to help with that.\nNode vs Side 💡 This was a very subtle yet \u0026ldquo;Aha\u0026rdquo; moment for me. When you\u0026rsquo;re traversing down a tree using dfs, while you can do things like:\nlet leftNode = process(node.left) let rightNode = process(node.right) it might be better to see at as:\nlet leftSide = process(node.left) let rightSide = process(node.right) Mentally for me this was a big shift in how I see it. DFS is iteratively / recursively against a path, much like how you iterate against items within an array. At each iteration of the path, you just process a single node within the path. Decide to then further continue or stop.\nAs an example, with the LCA problem you\u0026rsquo;re always like: \u0026ldquo;does the path \u0026ldquo;beginning from this node\u0026rdquo; contain x or y?\nThe key insight is that each recursive call processes a single node / step AND represents an entire subtree/path - understanding this duality is tricky and crucial.\nAnd with a BFS, this just becomes \u0026ldquo;Node vs Level\u0026rdquo; instead, but otherwise the same thing.\nYour algorithm can often be, continue, stop or aggregate things up to this point node, or return a node within this subtree, or do some form of comparison against another subpath, etc.\n Thinking about this further, I think \u0026lsquo;subtree\u0026rsquo; actually best represents the idea. Side is a bit too generic. Path is somewhat inaccurate term for something that has many branches. Subtree truly matches the visual breakdown.\n Types of Tree Trees can have multiple children. Or on the special case of binary trees have only two. A binary tree is still different from a binary search tree (BST) where things follow a certain order.\nBinary search tree For a Binary tree to be a BST, it has to follow the following rules:\n left node must be smaller than parent. right node must be greater than parent.  Basically left \u0026lt; parent \u0026lt; right\nOrder of insertion matters for BST Example 1 Let\u0026rsquo;s say you have the following numbers [1,2,3,4] in your tree. Depending on the order you add them into the binary search tree, the tree\u0026rsquo;s visualization would be different.\nA: Inserting in the following order: [4,3,2,1] will create:\n 4 / 3 / 2 / 1 B: Inserting in the following order: [4,2,3,1] or [4,2,1,3] will create:\n 4 / 2 / \\ 1 3 C: Inserting in the following order: [2,1,3,4] or [2,3,1,4] will create:\n 2 / \\ 1 3 \\ 4 D: Inserting in the following order: [3,4,1,2] or [3,1,4,2] will create:\n 3 / \\ 1 4 \\ 2 E: Inserting [1,2,3,4] will create\n 1 \\ 2 \\ 3 \\ 4 and so on.\nAll the above are a BST made of 1,2,3,4. Because all conform to the two rules. Yet each is inserted with different order.\nFor the tree to appear as a balanced BST, you should\n get the middle of the array and set it as root. recursively do the same for the left half and the right half.  get the middle of the left half and set it as the left child of the root. get the middle of the right half and set it as the right child of the root. recursion ends when there\u0026rsquo;s no more left/right nodes.    This is done in a pre-order fashion (node, left side, right side). Example:\n [10, 15, 20, 25, 30] 1st range: ⎣___________↑___________⎦ 2nd range: ⎣↑____⎦ 3rd range: ⎣↑⎦ 4th range: ⎣↑____⎦ 5th range: ⎣↑⎦  20˚ 20 20 20 20 / / / \\ / \\ 10˚ 10 10 25˚ 10 25 \\ \\ \\ \\ 15˚ 15 15 30˚   Order of inserts. Each new insert into the tree is annotated with ˚    Range middle index calculation middle/pivot index     0\u0026hellip;4 0 + 4 / 2 2   0\u0026hellip;1 0 + 1 / 2 0   1\u0026hellip;1 1 + 1 / 2 1   3\u0026hellip;4 3 + 4 / 2 3   4\u0026hellip;4 4 + 4 / 2 4    Tree Traversal  Breadth First Search (BFS). Also known as level-order traversal. Depth First Search (DFS).  Binary Search Traversal You can traverse the binary tree in the same fashion. However binary trees can be traversed differently which can come in handy with binary search trees:\n  In Order: left (recurse), self, right(recurse): To elaborate a bit further: If the current node has a left child, recursively visit this child. Then, visit the node itself. If the current node has a right child, recursively visit this child.\n  Pre Order: self, left (recurse), right(recurse)\n  Post Order:: left (recurse), right(recurse), self\n  All the different ways of traversing a BST are more or less a \u0026lsquo;hinted DFS\u0026rsquo;. Meaning in a DFS, if you have multiple children, you just pick whichever you like and just go deeper. However with in-order, pre-order, post-order, you pick a certain node first then continue down the tree again in a fashion similar to DFS.\nThe term \u0026lsquo;order\u0026rsquo; might put you off a bit, it\u0026rsquo;s not top to bottom like a BFS, it\u0026rsquo;s more about smallest number to biggest. Hence an in-order traversal will start from the bottom left where the smallest item is at and finish at the bottom right where the biggest item is at.\nThat being said not every binary tree has an order or is a BST. So alternatively as a way to memorize you can think of nodes of just left and right as numbers grow left to right.\nMapping of a Tree to an Array Mainly two of types of the above traversal types are used for array to BST conversions:\n Level-order traversal: More accurate, but not necessarily useful. Depends on your needs. In-order traversal: More logical and useful. The most commonly used binary search tree traversal method is in-order:  If we have an array of [1,2,3], we can turn it into a tree as such:\nExample 2  2 / \\ 1 3 In-order array: [1,2,3]\nlevel-order traversal: [2,1,3]\nWe can also see the same tree as:\n 2 / \\ 1 3 / \\ / \\ nil nil nil nil In-order array: [nil,1,nil,2,nil,3,nil]\nlevel-order traversal: [2,1,3,nil,nil,nil,nil]\nExample 3:  3 / \\ 1 5 \\ \\ 2 6 In-order array: [1,2,3,5,6]\nLevel-order traversal: [3,1,5,nil,2,nil,6]\nor alternatively:\n 3 / \\ 1 5 / \\ / \\ nil 2 nil 6 / \\ / \\ nil nil nil nil In-order array: [nil,1,nil,2,nil,3,nil,5,nil,6,nil]\nLevel-order traversal: [3,1,5,nil,2,nil,6,nil,nil,nil,nil]\nQuestion Go back to Example 1 above. Ask yourself how is the in-order \u0026amp; level-order traversal among all variations of [1,2,3,4] different from each other?\n Click to see answer 👇  in-order traversal for all will be: [1,2,3,4] level-order would be different for each.   Use Paper when debugging your mapping/traversal codes.  Draw a sample case Then iterate through it. Just like a debugger. Think you\u0026rsquo;re pausing and write down values for your properties. Then traverse and repeat. Often times the order of traversal becomes more clear on paper and you realize your code isn\u0026rsquo;t respecting the intended order. My algorithm was correct, yet I was getting a bad result because I the input array I created in my head was incorrect. As soon as I wrote down on paper, I realized where the conversion of the table to array was wrong. I corrected the array and validated that my algorithm works as expected\u0026hellip; The more you do on paper, the better you get at visualizing things in your head.  Jargon   Traversals:\n BFS DFS BST BST Traversal  in-order pre-order post-order      Root: The top node.\n  Leaf: Any node that doesn\u0026rsquo;t have a left or right node.\n  Depth of a node: The number of edges from the node to the root node.\n  Height of a tree: The maximum number of edges from the root node to a leaf node.\n  Types of BST:\n Balanced tree: A tree in which the heights of the left and right subtrees of any node differ by at most 1. In Example 1, the B,C trees are balanced. Full binary tree: A binary tree in which every node has either 0 or 2 children. A complete binary tree: A binary tree in which every level, except possibly the last level, is completely filled, and all nodes in the last level are as far left as possible. A Degenerate tree: Every non-leaf node has just one child in a binary tree known as a Degenerate Binary tree. Example 1, the A,E trees are degenerate and considered not efficient for sorting.  See here for more.\n   💡 For most leetcode questions you\u0026rsquo;d see people go with DFS. However I think a lot of questions are solved more naturally with a BFS though. BFS is simpler to imagine because it\u0026rsquo;s level by level as opposed to a DFS traversal. With BFS, you just use a queue, store it in a temp, empty the queue, iterate over the temp, add new items to queue and continue until you\u0026rsquo;re done.\n What\u0026rsquo;s the benefit of a balanced Tree?  A balanced tree will allow you to cut the tree into half by having half on its left and the other half on its right. Unlike a degenerate tree where 100% of the remaining nodes is still on the same side. Degenerate trees are bad for doing a binary search tree. They\u0026rsquo;re more similar to a Linked List or an array.  Helper functions  Tree Printer. See here Array to \u0026lsquo;Balanced Height Binary Tree Search\u0026rsquo; Convertors:  level-ordered array to tree. See here. Creates a tree exactly based on its input. in-order array to tree: See here. Only creates a height balanced tree. Won\u0026rsquo;t create any other representation of the tree.   Traversals. See here  Summary  There are different kinds of trees that shouldn\u0026rsquo;t be confused with one another. When it comes to binary trees, you may question why the in-order of different trees ends up the same. The answer is:, Even though insertion order of elements was different, the logic of in-order will always lead to an in-order output. A balanced binary search tree is best for searching because you can cut in half. Differences of level-order vs in-order traversal:  level-order maintains a 1:1 relationship with its tree visualization. It\u0026rsquo;s agnostic to order. And is just all about position. In-order is considerate of order, which is based off of position and logic. level-order can map nil elements in an array back to its original position. In-order isn\u0026rsquo;t capable of properly mapping nil elements, because then a nil node will not have left/right elements. Think of our [1,nil,3] tree. If root is nil then its left and right nodes wouldn\u0026rsquo;t exist. So you have to remove any nil element before mapping back. Level-order is more generic and applicable to non-binary trees as well whereas in-order traversal is only applicable to binary search trees.\nSo choose your traversal/mapping algorithm wisely.‍    Example:\n You can easily map a [1,nil,3] using level-order mapping to:   1 / \\ nil 3  However [1,nil,3] using an in-order mapping can get mapped to a root node of nil, which will create a nil tree.  I was confusing the two traversals and lost a tremendous amount of time, hence this blog post.\nAcknowledgements Shout out to Josh Caswell and Tim Vermeulen for answering my questions so I was able to put this post together.\n","permalink":"https://mfaani.com/posts/interviewing/trees/tree-basics/","summary":"I do leetcoding every once a while, but keep forgetting some tree basics. This post here it to help with that.\nNode vs Side 💡 This was a very subtle yet \u0026ldquo;Aha\u0026rdquo; moment for me. When you\u0026rsquo;re traversing down a tree using dfs, while you can do things like:\nlet leftNode = process(node.left) let rightNode = process(node.right) it might be better to see at as:\nlet leftSide = process(node.left) let rightSide = process(node.","title":"Tree Basics and some Swift helpers for leetcode"},{"content":"In the previous posts we talked about Build Pipeline, Jargon, Static Linker vs Dynamic Linker. In this post we\u0026rsquo;ll benefit from the knowledge gained about the app wrapper\u0026rsquo;s folder structure and the placement of all the different binaries (frameworks and main app\u0026rsquo;s executable) to know where to look for. New in this post is learning how to use the nm command to inspect and count the number of symbols of each binary.\nSymbols What are symbols?  A symbol is a name to refer to a fragment of code or data Fragments of code may reference other symbols (e.g., when a function calls another function)  For more see WWDC 2018 - Behind the Scenes of the Xcode Build Process\nWhy do symbols exist?  Cross-binary communication Debugging information  Type, function, variable names File name, file number Mappings between symbols and their addresses    When and where are symbols generated? Any type, variable, function you define turns into a symbol. They get generated within an object file. A typical object file contains:\n Compiled Code Symbol Table  Global symbols Undefined symbols Debug symbols Swift symbols   DWARF  Can you explain the above symbols a bit more?  Think of global symbols as the public interface. For frameworks this should not get stripped. For apps you can strip them as long as you don\u0026rsquo;t need the public interface for unit-testing. If you strip the global symbols for a framework then you\u0026rsquo;re not exposing the binary\u0026rsquo;s interface to others binaries that need to communicate with your binary. Undefined symbols are symbols that are not defined within the binary. They\u0026rsquo;re imported dependencies of the current binary. They can’t be stripped, because the linker needs them. Debug symbols are used to generate the dSYM. They must get stripped (only after you\u0026rsquo;ve extracted the dSYM). Otherwise it would cause bloat in your binary. Xcode should handle this correctly but often things get messed up. Swift symbols are used to make things compile. Think of them more as scaffolding. Once the binary is compiled, most of them can be stripped. Otherwise it would cause bloat in your binary. Xcode should handle this correctly but often things get messed up.  NOTE: The stripping of these symbols are governed by Xcode Build Settings. But it\u0026rsquo;s beyond the scope of this article. The focus of this article is to examine a binary after it\u0026rsquo;s been made ready for the App Store.\nSo some symbols need to get stripped? Yes. Some. Debug symbols should get stripped only after you have the dSYM created. Most swift symbols should get stripped after compilation as well.\nXcode strips Release builds for you. So you usually don\u0026rsquo;t have to care unless things are misconfigured.\nSo how can I see the symbols of a binary? That\u0026rsquo;s where the nm command becomes useful.\nnm command What does the nm command do? nm is short for \u0026lsquo;Name Mangling\u0026rsquo;. Name refers to the name/symbol of each address. Summarized from Wikipedia - Name Mangling is about:\n In compiler construction, various problems are caused by the need to resolve unique names for programming entities in many modern programming languages.\nName Mangling provides a way of encoding additional information in the name of a function, structure, class or another data type in order to pass more semantic information from the compiler to the linker. Mainly due to the need to distinguish symbols with same identifiers but in different namespaces. It is required in these use cases because each signature might require different, specialized calling convention in the machine code.\n Just pick any binary and just do:\nnm -a \u0026lt;your binary\u0026gt; # List all symbols in the binary nm -g \u0026lt;your binary\u0026gt; # List all globals in a binary (anything that\u0026#39;s made public) nm -u \u0026lt;your binary\u0026gt; # List only undefined symbols in a binary. (any symbol which you import) Note: You can add | xcrun swift-demangle to the end of the nm command. It will help demangle the symbols and make them much more readable.\nLike you can try this on anything. Examples: commands like ls, cp, mkdir, or binaries within macOS apps such as Safari, Terminal, or binaries within the app wrapper of an app you\u0026rsquo;re developing.\nWhat are some ways to get a more granular view into the symbols? Get count of Debug symbols:\nnm -a \u0026lt;your binary\u0026gt; | grep - | wc -l  I\u0026rsquo;m grepping on - because debug symbols are annotated as such. See man nm in Terminal for more. wc -l just gets you the line count.\nGet count of Swift symbols:\nnm -a \u0026lt;your binary\u0026gt; | grep \u0026#39;_$s\u0026#39; | wc -l # don\u0026#39;t forget the single quotes I\u0026rsquo;m grepping on '_$s' (or often with _$S) because Swift symbols are annotated as such. See man strip in Terminal for more information.\nDoes the nm command give you size? No. It just shows you the symbols. Doesn\u0026rsquo;t give you any information about their length. Your focus should be more on the number of symbols.\nTypically Swift symbols are long and that causes size increases.\nWhat you can do instead is that just compare the actual size of the binary for before and after stripping.\nStats \u0026amp; Tips This post isn\u0026rsquo;t to say what\u0026rsquo;s expected to be seen. More on how to inspect symbols. And have a way to validate the impact of changes you made.\nYou might think well I have to go through the entire app archive process and that\u0026rsquo;s a lengthy process. You might have to do that ultimately. But an alternate way is to just:\n Compile a sample file with swiftc and pass different commands e.g. try passing -g to create the dSYM. Record the count of the Debug Symbols and Swift Symbols using nm. After recording, attempt to strip the binary. Then record the count of Debug Symbols and Swift Symbols again Compare the counts with before stripping along with the binary sizes. Rationalize on how things work. Use the steps mentioned in the image from Xcode Build Pipeline. Inspect your Build Settings for each individual target.  For a small swift file I did the steps. The results were as such:\nExamples File 1 import Foundation let arguments = CommandLine.arguments if arguments.count != 3 { print(\u0026#34;Error. Use: Printer firstName lastName\u0026#34;) exit(1) } /// - Note: The value of arguments[0] is the name of the binary let firstName = arguments[1] let lastName = arguments[2] print(\u0026#34;Greetings \\(firstName)\\(lastName)\u0026#34;) Compiling and inspecting WITHOUT debug information # Build swiftc main.swift  # Execute ./main Ethan Hawk # Greetings Ethan Hawk # Inspect nm -a main | wc -l # 43. Total number of symbols.  nm -a main | grep \u0026#39;_$s\u0026#39; | wc -l # 23 Swift symbols.  nm -a main | grep - | wc -l # 0 Debug symbols # Strip strip -ST main # S flag removes Swift symbols. T flag removes debug symbols.  nm -a main | grep \u0026#39;_$s\u0026#39; | wc -l # 0. Successfully nuked all Swift symbols. Compiling and inspecting WITH debug information # Build swiftc -g main.swift # Creates a binary named \u0026#39;main\u0026#39; + main.dSYM # Inspect nm -a main | wc -l # 84. We got more than before (43) because we\u0026#39;re creating some extra debug symbols. nm -a main | grep \u0026#39;_$s\u0026#39; | wc -l # 31 Swift symbols. Which is slightly more than 23 nm -a main | grep - | wc -l # 41 debug symbols. This is substantially higher than 0. # Strip strip -ST main nm -a main | grep - | wc -l # 1 Debug symbols. nm -a main | grep \u0026#39;_$s\u0026#39; | wc -l # 0 Swift symbols.  Apps in the app store are always compiled with dSYMs. And must therefore be stripped.\nActual Stats The lack of correct stripping could lead to a ginormous 30% increase for frameworks (and app) binaries as mentioned this major CocoaPods issue.\nFor a sample framework when misconfigured:\n There were about 125000 debug symbols. After correct stripping it went down to 500. There were about 80000 Swift symbols. After correct stripping it went down to 7000 symbols. The framework went down from 35MB to 24MB after correct stripping.  Summary When using Xcode  Find either the build folder or archive, then find the .app wrapper and within it inspect each binary. If you create a dSYM then Xcode will add more symbols. For Release builds, if things are correctly configured then it will strip the extra symbols using strip command. Otherwise it won\u0026rsquo;t strip and you\u0026rsquo;d have a lot of bloat. You can inspect the symbols of each binary using nm. Use grep to filter for extraneous Debug or Swift symbols. If you have dynamic libraries, then you must inspect each binary individually.  NOTE: The build settings (which affect debug symbols and other speed and size optimizations) are different between Release and Debug builds. As a result make sure you check the App Store build. Not anything else.\nWhen using swiftc in command line  When using swiftc you must pass the required options (-g) for swiftc to create the dSYM. You also have to use strip along with appropriate options (-ST) to strip.  Xcode does a lot of things for you without you realizing it. If you try things with swiftc then you realize more of the details yourself.\nAction Items The purpose of this post is assist with building an intuition for this kind of stuff. This post isn\u0026rsquo;t meant to say you\u0026rsquo;re doing things wrong nor that by following its steps you\u0026rsquo;ll save 30% on App Size. Rather it\u0026rsquo;s just a way to be learn how to inspect things. Often Dependency tools such as Cocoapods, Carthage, make a single change that impacts app size drastically. Those changes are hard to find.\nThat said if you\u0026rsquo;re using Cocoapods and are dynamically linking, then making the following change in your Podfile could save you a fair amount. For my project it saved 30%.\nnext unless config.name == \u0026#39;Release\u0026#39; # cocoapods defaults to not stripping the frameworks it creates, but you want to strip debug symbols off of dynamic libraries.  config.build_settings[\u0026#39;STRIP_INSTALLED_PRODUCT\u0026#39;] = \u0026#39;YES\u0026#39; Examples of mistakes in the toolchain  CoocaPod mistake: Dynamic frameworks should be stripped (STRIP_INSTALLED_PRODUCT). As far I know this issue still exists. I could be wrong though. How to fix: see here Carthage mistake: See: Enhancement : Respect STRIP_STYLE project option \u0026amp; The following comments Xcode change: A small change in an Xcode update (Emerge Tools - How Xcode 14 unintentionally increases app size) might tweak things that are incompatible with your current setup and create issues that are hard for you to figure out.  The tips shared just might help you inspect and narrow things down easier or help with becoming better at having an x-ray view of the build pipeline and the impact of certain settings.\nAcknowledgements Major shout out to Mark Rowe who answered a lot of my questions about nm so I can put together this post. His exceptional RTFM abilities was helpful for figuring out how to make nm and strip commands work. Shout outs to Saagar Jha as well for helping through out these series and reviewing this post.\n","permalink":"https://mfaani.com/posts/devtools/optimizing-app-size/how-can-i-inspect-the-size-impact-of-symbols-in-an-app-binary/","summary":"In the previous posts we talked about Build Pipeline, Jargon, Static Linker vs Dynamic Linker. In this post we\u0026rsquo;ll benefit from the knowledge gained about the app wrapper\u0026rsquo;s folder structure and the placement of all the different binaries (frameworks and main app\u0026rsquo;s executable) to know where to look for. New in this post is learning how to use the nm command to inspect and count the number of symbols of each binary.","title":"How Can I Inspect the Size Impact of Symbols in an App Binary: A Practical Guide for Apple Developers"},{"content":"In the previous post we talked about how the linker\u0026rsquo;s selective loading helps solve the bloat issue. But there are some other limitations to static linking. Because of those limitations software engineers created Dynamic libraries and the Dynamic Linker. In this post we\u0026rsquo;ll go through some of those limitations and discuss the trade-offs between the two ways of linking and their sizing impact.\nInspired by Link fast: Improve build and launch times - 15:47:\n\u0026lsquo;Selective Loading\u0026rsquo; resolves the bloat issue. Are there any other issues with Static Linking? Why do we need dynamic linking? Think about how will adding libraries scale over time, as there is more and more source code. It should be clear that as more and more libraries are made available, the end program may grow in size. That means the static link time to build that program will also increase over time.\nWhat if we switched from using \u0026lsquo;ar\u0026rsquo; to \u0026lsquo;ld\u0026rsquo;? As a result the output of library is now an executable binary. This was the start of dynamic libraries in the \u0026rsquo;90s. As a shorthand, we call dynamic libraries \u0026ldquo;dylibs\u0026rdquo;. On other platforms they are known as DSOs or DLLs.\nSo what exactly is going on here? And how does that help the scalability?\nThe key is that the static linker treats linking with a dynamic library differently. Instead of copying code out of the library into the final program, the linker just records a kind of promise.\nThat is, it records the symbol name used from the dynamic library and what the library\u0026rsquo;s path will be at runtime. How is this an advantage?\n  It means your program file size† is under your control. It just contains your code, and a list of dynamic libraries it needs at runtime. You no longer get copies of library code in your program. Your program\u0026rsquo;s static link time is now proportional to the size of your code, and independent of the number of dylibs you link with.\n  Also, the Virtual Memory system can now shine. When it sees the same dynamic library used in multiple processes, the Virtual Memory system will re-use the same physical pages of RAM for that dylib in all processes that use that dylib.\n  Using dynamic libraries speed up build time.\n  You can share libraries with your other app extensions.\n  Let\u0026rsquo;s assume you have:\n app code that without linking to another library has a size of 20MB appex (app extension) that without linking to another library has a size of 7MB  And both need to link to a static library named Foo. Assume Foo library is 5MB. Also assume both need all symbols in Foo library i.e. all symbols of Foo library will get linked.\nIn this scenario, your total app size is increased 5Mb when Foo library is statically linked into app\u0026rsquo;s main binary. And also increased another 5Bm when Foo library is statically linked into the appex binary.\nIf Foo library was instead linked dynamically with both, then both it would increase the app size by only 5Mb, because the library can be shared.\nI\u0026rsquo;ve shown you how dynamic libraries started and what problem they solve. But what are the \u0026ldquo;costs\u0026rdquo; for those \u0026ldquo;benefits\u0026rdquo;?\n  Launching your app is now slower. This is because launching is no longer just loading one program file. Now all the dylibs also need to be loaded and connected together. In other words, you just deferred some of the linking costs from build time to launch time.\n  A dynamic library based program will have more dirty pages. In the static library case, the linker would co-locate all globals from all static libraries into the same DATA pages in the main executable. But with dylibs, each library has its DATA page.\n  Dynamic linking is that it introduces the need for something new: a dynamic linker! Remember that promise that was recorded in the executable at build time? Now we need something at runtime that will fulfill that promise to load our library. That\u0026rsquo;s what dyld, the dynamic linker, is for.\n  †: Program file size does not mean the \u0026lsquo;app size\u0026rsquo;. It means the main app executable size. Example, consider the following:\nFoo.app - Foo (binary) \u0026lt;- Program file size refers to this. It does not refer to everything that's within the app. - bar (dylib) - baz (dylib) - qux (dylib) One of the biggest size benefits of dynamic linking is when one of the dynamic libraries you link to, is a system framework. In that case:\n You don\u0026rsquo;t have to download/install that dynamic library. Because the library was there when you installed/updated the OS. In other words its size doesn\u0026rsquo;t affect your app size. It only affects the OS size. The OS can update the dynamic library and add new features without you having to do anything. This isn\u0026rsquo;t a size impacting feature. But is also a worthy byproduct.  Static Linking vs Dynamic Linking in more detail. Before we dive deeper into static library vs dynamic library, it\u0026rsquo;s critical to note:\nWhen it comes to comparing \u0026lsquo;Static\u0026rsquo; vs \u0026lsquo;Dynamic\u0026rsquo;, don\u0026rsquo;t try to compare the terms \u0026lsquo;Static Library\u0026rsquo; and \u0026lsquo;Dynamic Library\u0026rsquo;. Instead try comparing \u0026ldquo;Static Linking' vs. \u0026lsquo;Dynamic Linking\u0026rsquo; i.e. focus on the manner of linking as opposed to the nature of the library itself. Once you understand Static vs Dynamic Linking then you can understand Static vs Dynamic Library.\nAt the high level, with dynamic linking you defer linking to the library to runtime time. This comes with some benefits which we\u0026rsquo;ll discuss. A simple illustration is as follows\n  This was from user Paxdiablo on Stackoverflow.com  Can you link a library to another library? Yes. It\u0026rsquo;s very common to link a dynamic library to another. But you almost never link a static library into a dynamic library Unless the static library is exclusively used by the dynamic library.\nYou mostly statically into an app\u0026rsquo;s main executable.\nThe idea is:\n Link things statically if they\u0026rsquo;re always needed or if you want to reduce launch time. Link things dynamically if they\u0026rsquo;re needed on demand or if you want to reduce build time.  That said statically linking to a dynamic library can happen and it will lead to hard to track bugs. See Link fast: Improve build and launch times 15:16:\n When a static library is incorporated into multiple frameworks. Each of those frameworks runs fine in isolation, but then at some point, some app uses both frameworks, and boom, you get weird runtime issues because of the multiple definitions. The most common case you will see is the Objective-C runtime warning about multiple instances of the same class name. Overall, static libraries are powerful, but you need to understand them to avoid the pitfalls.\n And this something difficult to anticipate. Hence it\u0026rsquo;s better to just avoid static linking into a dynamic library.\nStatic vs Dynamic Linking Table  Similarities between Static and Dynamic Linking To be perfectly clear, they have a lot of similarities as well. Both have to get compiled, linked, and increase size. Both have their code absorbed into the app. Yet the manner which they get linked linked, compiled and placed into the app is still different. Both need to get stripped, have a presence in dSYMs but the manner in which they get stripped is somewhat different, and so is the placement of their dSYMs.\nNote: Apple Frameworks are special. Reasons:  OS doesn\u0026rsquo;t get bloated: You link to them, but don\u0026rsquo;t copy them. Also they can be shared between different apps of different companies. Example: The Uber and Lyft apps can use MapKit.framework without needing it to be included on the OS twice. Like imagine if there was a Networking.framework where app apps needed it for networking operations and since it couldn\u0026rsquo;t be shared, all apps had to include a copy. That mean you\u0026rsquo;d have to store that framework as your apps. Your app gets new OS features without needing you needing to go through the distribution process again: Apple Frameworks change with every iOS update. However we don\u0026rsquo;t have to compile our apps again with with the newer iOS version. This is because the newer iOS versions almost never make breaking changes. Apple just dynamically links our app with a newer version of MapKit.framework, a newer version of Networking.framework and voila! Things just work. Apps loads faster: Because you can use a shared System framework that\u0026rsquo;s already in memory. Fun fact, after an OS reboot, a lot of the system frameworks haven\u0026rsquo;t been put on the memory. The OS incrementally adds them. The first app you launch after a reboot, usually loads slower, because you need to load a lot of system frameworks. Subsequent apps load a bit faster because their dependencies have been already put into memory either by the OS or some other app. For more on that see What\u0026rsquo;s the difference between cold launch, warm launch?.  Had Apple made their system libraries static then every app would have needed to:\n Link them into the binary -\u0026gt; Apps would get repeatedly bloated for the same binary Re-link upon any change in a framework. -\u0026gt; A whole lot more app distribution overhead. Apps will launch slightly slower because instead of using a shared Network.framework already in memory, they now have a bigger binary that takes more time to load.  Any last notes? Yes. One that\u0026rsquo;s super important. Because Dynamic Libraries are their own binary, it means that you have to strip them yourself. You have to make sure the stripping flags are correctly set. For a Static Library the stripping flags are mostly governed by the binary that copies all the functions and data. So you worry less, because usually that\u0026rsquo;s taken care of.\nCocoaPods currently makes an egregious error and doesn\u0026rsquo;t strip dynamic libraries. This issue was recorded on GitHub by Jordan Rose. See Dynamic frameworks should be stripped (STRIP_INSTALLED_PRODUCT).\nAs a result you may end up shipping your app to the app store with both debug symbols and Swift symbols. Not stripping them tends to add about 30% to your app size.\nIn my quest, what I\u0026rsquo;ve realized is that build tools often hide certain complexities from us. By doing such, they often make hard to track bugs. Even Xcode and Apple Engineers are not exempt from this. Similar issues were recorded for Cartage. See: Enhancement : Respect STRIP_STYLE project option \u0026amp; The following comments\nReferences To better understand static and dynamic libraries, I highly recommend going through this fantastic tutorial written by Ralph Bergmann for how to create your own dynamic frameworks or static libraries. Going through the tutorial helped me see:\n How I can simply create libraries without CocoaPods. Then I inspected each build folder along with the Products folder. The drastic differences between the Xcode setup between a static library vs a framework.  Acknowledgements Special shout out to Mark Rowe and Saagar Jha who answered dozens of my questions so I can put these posts together.\n👉 Next Post - How Can I Inspect the Size Impact of Symbols in an App Binary: A Practical Guide for Apple Developers\n","permalink":"https://mfaani.com/posts/devtools/optimizing-app-size/how-does-the-linker-help-reduce-app-size-part-two/","summary":"In the previous post we talked about how the linker\u0026rsquo;s selective loading helps solve the bloat issue. But there are some other limitations to static linking. Because of those limitations software engineers created Dynamic libraries and the Dynamic Linker. In this post we\u0026rsquo;ll go through some of those limitations and discuss the trade-offs between the two ways of linking and their sizing impact.\nInspired by Link fast: Improve build and launch times - 15:47:","title":"Optimizing Binaries - How Does the Linker Help Reduce App Size? What are the different types of linking - Part Two"},{"content":"In the previous post we talked about a problem with the linker: Linking a single function from a library could link to the entire library. This creates a lot of bloat. As a result some enhancements were made to linker. The enhancement was to be selective and only load symbols that you need.\nInspired by Link fast: Improve build and launch times - 4:15:\nSelective Loading In a nutshell if you have the following source code written in C:\n extern means that the function is coming from an external file.\nFile Structure As you can see above, some functions depend on other functions:\n In main.c, there\u0026rsquo;s a function called main that calls a function foo. In foo.c, there is foo which calls bar. In bar.c, there is the implementation of bar but also an implementation of another function which happens to be unused. Lastly, in baz.c, there is a function baz which calls a function named undef.  Now we compile each to its own .o file.\n foo, bar, and undef are undefined [They\u0026rsquo;re marked with extern i.e. they\u0026rsquo;re not defined within the file that attempts to use them]. That is, a use of a symbol and not a definition.  Now, let\u0026rsquo;s say you decide to combine bar.o and baz.o into a static library. Next, you link the two .o files and the static library. Let\u0026rsquo;s step through what actually happens.\nHow linker works First, the linker works through the files in command line order. The first it finds is main.o. It loads main.o and finds a definition for \u0026ldquo;main\u0026rdquo;, shown here in the symbol table. But also finds that main has an undefined \u0026ldquo;foo\u0026rdquo;.\n main is defined in main.o because we have its implementation and definition. foo is undefined, because we don\u0026rsquo;t have its definition nor its implementation. We only know that it\u0026rsquo;s external  The linker then parses the next file on the command line which is foo.o. This file adds a definition of \u0026ldquo;foo\u0026rdquo;. That means foo is no longer undefined. But loading foo.o also adds a new undefined symbol for \u0026ldquo;bar\u0026rdquo;.\nNow that all the .o files on the command line have been loaded, the linker checks if there are any remaining undefined symbols. In this case \u0026ldquo;bar\u0026rdquo; remains undefined, so the linker starts looking at libraries on the command line to see if a library will satisfy that missing undefined symbol \u0026ldquo;bar\u0026rdquo;.\nThe linker finds that bar.o in the static library defines the symbol \u0026ldquo;bar\u0026rdquo;. So the linker loads bar.o out of the archive.\nAt that point there are no longer any undefined symbols, so the linker stops processing libraries.\nThe linker moves on to its next phase, and assigns addresses to all the functions and data that will be in the program. Then it copies all the functions and data to the output file. Et voila! You have your output program.\n  The object files within the blue container are linked. The function named \u0026#39;unused\u0026#39; is also linked. It\u0026#39;s because its containing object file was needed because of the bar function  Summary of Static Library You only include object files that you link against. If you don\u0026rsquo;t link against them, then they don\u0026rsquo;t get included in the final binary.\nWith that said, the level of control for selecting source code is not the \u0026lsquo;type level\u0026rsquo; nor \u0026lsquo;function level\u0026rsquo;. It\u0026rsquo;s at the \u0026lsquo;file level\u0026rsquo;. This was demonstrated in the example above where unused function is linked into the final binary, while the entire baz object file is excluded.\nLet\u0026rsquo;s answer some common Linker questions: When does the linker finish linking object files? As soon as they\u0026rsquo;re no longer any undefined symbols. The linker is much like fixing a puzzle. You start with a piece, its edges need other pieces to complete. You keep on adding more pieces. You stop when the last piece is added. Anything remaining is redundant.\nHow are the compiler and linker different? See my previous post on Optimzing App Size - Jargon#Concepts. It explains things at the high level.\nThe compiler needs to know what the other symbols are in order to compile code that uses them.\nThe linker combines these object code files into an executable. The linker needs to know where the other symbols are in order to link code objects that use them.\nFWIW many IDEs invoke them in succession, so you never actually see the linker at work. Some languages/compilers do not have a distinct linker and linking is done by the compiler as part of its work.\nWith Xcode you use:\n swiftc which ends up calling swift-frontend to compile Swift files clang to compile C family of code. ld or clang to link object files.  A bit more about the Linker:\n It\u0026rsquo;s one of the final processes in the build. And what we do is we combine all of these .o files that have been built by the two compilers (clang and swift) into an executable. All it does is move and patch code. It cannot create [object] code, and this is important and I will show that in the example. But we take these two kinds of input files. The first one being object files. Which are what come out of your build process. And the second one being libraries which consist of several types including dylibs, tbd\u0026rsquo;s, and .a files (or static archives).\nfrom WWDC 2018 - Behind The Scenes of Xcode Build Process\n Also See Compiling and Linking - Alex Allain\nWhich tool generates the binary? The compiler doesn\u0026rsquo;t generate an executable.\nThe linker does that.\nIs compilation and Linking done in parallel? Compilation is done in parallel.\nLinking is done serially — after all compilation is done.\nWhat\u0026rsquo;s the input/output of each? The input of the compiler is source code. Its output are object files. The input of the linker are object files. Its output is some binary (dylib or an app binary). Compiler will compile every piece of source code. The Linker will only link object files that are needed. If a certain object file / symbol isn\u0026rsquo;t needed then it won\u0026rsquo;t get linked.\nDoes Compilation take more time or linking? Usually compilation takes a lot more time, because there\u0026rsquo;s a lot of static analysis that your compiler needs to do to validate everything. And while developers don\u0026rsquo;t care if their build time took a couple seconds long, users certainly care significantly if the app launch took a few seconds. This is why finding the right balance between the right amount dynamic and static libraries is critical. Apple recommends you try things out both ways and then profile things to make sure you\u0026rsquo;re making the best decision.\nHow do you access symbols from another file? Different languages have different ways of doing things. The different options are:\n Declare a header file. Include the header file where needed. Use extern to mark a symbol as external to the current file. It will get resolved at link time. Swift doesn\u0026rsquo;t use extern Use a library and import everything within it. Have a language construct along with certain build tools that understand how your code is to be packaged as a module. That is why you don\u0026rsquo;t need to include a header, or mark as extern. The compiler just knows that foo, bar, baz are all part of the same module. Hence have access to the internal symbols. This is how Swift works. You don\u0026rsquo;t need to import/include the header file of another file in your same module.  Any Problems with Static Linking? Yes there are a few. Mainly when it comes to sharing libraries between an app and its app extensions. For more on that see part 2 of this post.\n👉 Next Post - How Does the Linker Help Reduce App Size? What are the different types of linking - Part Two\n","permalink":"https://mfaani.com/posts/devtools/optimizing-app-size/how-does-the-linker-help-reduce-app-size-part-one/","summary":"In the previous post we talked about a problem with the linker: Linking a single function from a library could link to the entire library. This creates a lot of bloat. As a result some enhancements were made to linker. The enhancement was to be selective and only load symbols that you need.\nInspired by Link fast: Improve build and launch times - 4:15:\nSelective Loading In a nutshell if you have the following source code written in C:","title":"Optimizing Binaries - How Does the Linker Help Reduce App Size? What are the different types of linking - Part One"},{"content":"To understand this post, I highly recommend everyone to watch the WWDC 2022 - Link fast: Improve build and launch times and WWDC 2018 - Behind the Scenes of the Xcode Build Process. They\u0026rsquo;re of the best talks I\u0026rsquo;ve ever seen.\nThis post covers some of the jargon and how it all comes together. And even though I love making puzzles, I\u0026rsquo;ll justify why I picked it as my cover.\nFiles   .c: A source code file written in C programming language.\n  .swift: A source code file written in Swift programming language.\n  .o: A machine code interpretation of the source code. An intermediate file. Also known as an object file. Named as such, because it\u0026rsquo;s the object/goal of our source code.\n  .h: A file that doesn\u0026rsquo;t contain implementation. But only the interface.\n  .a: An archive file. Made up from multiple .o files. Also known as a Static Library or Archive.\n  .dylib: A separate binary, from the main executable. Gets linked with the app’s executable at runtime. Gets copied into the app wrapper. As a result, the main executable is smaller and, because the code is loaded only when it is needed, the build time is typically faster. Also known as Dynamic Library. Often they also have an empty file type.\n    : (commonly with empty file types). A binary. For the scope of our discussions our focus is mostly on dylibs or app executables. Binaries in the Apple world (OSX and iOS) usually don\u0026rsquo;t have a file type. They\u0026rsquo;re the end product, the program that gets ran/executed. As the name suggests it\u0026rsquo;s just 0s and 1s. It\u0026rsquo;s the lowest possible language level. You can\u0026rsquo;t get any lower than that. Some examples of an executable are:\n An iOS app\u0026rsquo;s main executable: An app you open on your iPhone, ends up calling the app\u0026rsquo;s executable. Example the Safari app on your iPhone has a Safari executable. Other Examples - macOS binaries: Some examples that you use in the command line are: ls, cp, mkdir, pwd . Every time you do ls, you\u0026rsquo;re calling a binary somewhere in your macos. Executables are also known as binaries. See docs on /bin. Also see the /bin directory below:  If you do `which ls` then you\u0026#39;ll get to see the path to the binary that is used      .dSYM: In short, it\u0026rsquo;s a debug information file. For more see here\n  \u0026lsquo;main app executable\u0026rsquo;: An app may have multiple executables. Examples of this happening:\n An iOS app that has app extensions. App extensions are also executable. But aren\u0026rsquo;t the main executable. A command line interface that has. Users won\u0026rsquo;t have to call your other executables. But your main app will likely call the other executables.    Syntax  extern A C concept. Doesn’t exist in Swift. It’s an instruction/promise to the compiler which tells it that a given symbol is defined in another file. Without it, the compilation of the file which depends on the external symbol (function/variable) will fail. Additionally when the linker sees extern before function declaration, it looks for the definition in another C file.  Other languages have header files which they include. Swift is made more simple. It just compiles a module entirely. So as long as what you need is declared in the same module, then things will compile without having a need to import/include. If it\u0026rsquo;s declared in another module, then you just need to import that module. You don\u0026rsquo;t need to single out a file. This is a convenience made by swift.\nBundles (Structured directories)  .framework: A bundle directory. If you\u0026rsquo;ve seen my previous post on Whats the Difference Between an App (bundle) and a Binary, then a framework is much like a wrapper, but for a dylib. .app: A bundle directory. See my previous post on Whats the Difference Between an App (bundle) and a Binary.  Tools:  cc: Compiler  Converts a .c file to an executable. Converts multiple .c files into a .o file. Can\u0026rsquo;t be used to compile swift files. Must use swiftc instead.   clang: Apple\u0026rsquo;s compiler for the C family language (C, C++, Objective-C, Objective-C++). cc is more of a universal term. ld: The Linker. Converts .o files into an executable. Also known as the Static linker. This is an extremely important part of the whole build process. ar: An archiving tool. Helps to combine files together. Originally used for backups and distributions. Converts multiple .o files into a .a file. The difference between ar and ld is that:  ar just groups together object files. The product of that is a static library. ld creates an app or a dylib. And can do such with some (stripping related) optimizations.   dyld: The runtime linker, for dynamic linking a.k.a. \u0026lsquo;dynamic link editor\u0026rsquo;. swiftc: Is the command-line interface to the Swift compiler, which is responsible for compiling Swift source code into machine code that can be run on a computer. It basically does what cc does for .c files, but just for .swift files. It\u0026rsquo;s ultimately a symlink to swift. For more on that see here strip: Used to strip symbols of all kinds from binaries. You can strip various kinds of symbols  Debug Symbols Swift Symbols Global Symbols Non-Global Symbols All Symbols etc.  Stripping basically does two things for you:  It removes access to a certain symbol. It removes the code for that symbol.   dysmutil: Is applied against a non-stripped binary. It links all DWARF and debug information into a binary and creates the dSYM.  Concepts  Compilation: Converting source code within a single file into machine code. 0s and 1s. The output of compiling a single file is a single object file. Machine Code: Anything that\u0026rsquo;s written in zeros and ones. So the CPU can understand them. You may compile a single file into machine code, but that doesn\u0026rsquo;t mean that the single machine code instructions is enough to execute something. Often you need multiple machine code instructions to be able to complete some basic operations. Linking: Piecing different object files together to create a binary.  To put all these concepts together. See the following images:\nHigh Level   Every file is compiled into Machine Code. Then Linker links the necessary object files together.  1. Compilation   Every file is compiled into Machine Code. An individual Object file is useless. It needs all its other missing pieces to create a functioning application.  2. Linking   The linker starts from a point of entry, usually the main function and then tries to find all its undefined dependencies (any function/variable that\u0026#39;s not defined in main). For each of those dependencies it has to find their dependencies as well. Once all are found, it stops linking. The product of linking all object files together is your app binary or dynamic library\u0026#39;s binary.  Quick Summary - How do all the tools and files work together?  All source files are compiled into an intermediate file known as object code/file (.o file) Any needed object file will get linked together. Often you link source code and libraries together.  The compiler creates an object file. But a file by itself is useless. They\u0026rsquo;re like a piece of a puzzle. The linker links all necessary object files, and creates the final binary. Similar to how a puzzle maker places all the puzzle pieces in place.\nXcode Jargon Dependency order Build Process Represented as a Directed Graph\n What happens when you press build? So the first step is for the build system to take the build description, your Xcode project file. Parse it, take into account all the files in your project, your targets and the dependency relationships. Your build settings, and turn it into a tree-like structure called a directed graph\n The order:\n Knowledge built into the system Target Dependencies Implicit Dependencies Build Phase Dependencies Scheme order Dependencies  Incremental Build \u0026ldquo;The bigger your project, the longer the build process will take. So you don\u0026rsquo;t want to run all of these tasks every single time you build. Instead, the build system might only execute a subset of the tasks on the graph. Depending on the changes you\u0026rsquo;ve made to your project since the previous build. We refer to this as an \u0026lsquo;incremental build\u0026rsquo; and having accurate dependency information is very important in order for incremental builds to work correctly and efficiently.\nNow we talked about how changes affect the build system, and how they relate to incremental builds. So how does the build system actually detect changes? Each task in the build process has an associate signature which is the sort of hash that\u0026rsquo;s computed from various information related to that task. This information includes the stat of the task\u0026rsquo;s inputs like:\n File paths Modification time stamps. The command line indication used to actually perform the command. Other task-specific metadata such as the version of the compiler that\u0026rsquo;s being used.  The build system keeps track of the signatures of tasks in both the current and the previous build. So that it knows whether to rerun a task each time a build is performed.\u0026rdquo;\nUltimately what this means is that every time you click build, Xcode won\u0026rsquo;t necessarily re-compile everything from scratch.\nThings get compiled depending on:\n What\u0026rsquo;s already compiled and exists in the build directory. What\u0026rsquo;s changed in source code or compilation instructions.  If you didn\u0026rsquo;t change the interface of a function then all you have to do is recompile its file and you\u0026rsquo;re good. But if you did change a function\u0026rsquo;s interface, then you need to recompile that file and all its other dependencies.\nTo see what steps were performed for your recent build you can use the Report Navigator. Then just click on Build with the appropriate timestamp. Some suggestions for what keywords to search for are:\n Compile Link Create Directory Copy    Compare the messages in All vs Recent.  Frameworks, Libraries, and Embedded Content  Frameworks: is just a different packaging for dylibs. adds a copy of that framework into your application bundle under the Frameworks directory by default. For more see Big Nerd Ranch Libraries: Static Libraries. The object files get absorbed into the main app\u0026rsquo;s executable. Emebedded Content: App Extensions. There\u0026rsquo;s no linking involved. Yet you have to copy them into your app bundle.  Essentially all three are different form of dependencies of your app\u0026rsquo;s main executable.\nWhat\u0026rsquo;s the difference between \u0026lsquo;Build folder\u0026rsquo; and \u0026lsquo;Product folder\u0026rsquo;? When you want to make a cake, you need flour, milk, sugar, cream, eggs, chocolate chips, oven, mixer, etc. All of those are intermediary objects. Anything intermediate goes into the build folder. The final product, the cake is what goes in the Product folder. The final cake has the flour, milk, sugar and all, but its glued together in a certain structure. Although the chocolate chips are distinguishable from the cake. The rest are not.\nBuilding an app is similar to that. In the build folder you\u0026rsquo;d see the built static and dynamic libraries.\nHowever in the Products folder, you see the app\u0026rsquo;s main executable and its dynamic library. The static libraries are already merged into the app\u0026rsquo;s main executable.\nIf you deleted the Build Folder, then app will still get ran on the device/simulator. However if you deleted the Products folder, then you need to produce those again. The Product is what\u0026rsquo;s needed to run the app. The build folder is what\u0026rsquo;s needed to just build. Real world users when they unpack the app, they end up with something very similar to what we have in the Products folder.\nWhy is this important? It\u0026rsquo;s important to understand because if you ever need to dig into the products folder to find your static library, well you\u0026rsquo;ll never find it. You have to go looking for it in the Build folder as static libraries get merged into the app\u0026rsquo;s executable.\nFYI Apple has some pre-defined structures like app bundle or framework bundle. For more on the details of those structures see docs on Placing Content in a Bundle.\nSummary 1 - Dependency Order Xcode will see that your Cool.app depends on its libraries.\nIn my example the Cool app depends on Core (dynamic) library and Auth (static) Library\n2 - Compile dependencies Model.swift -\u0026gt; `swiftc` -\u0026gt; Model.o Model.o + Controller.o -\u0026gt; `ld` -\u0026gt; Core (dylib a.k.a Dynamic library) Controller.swift -\u0026gt; `swiftc` -\u0026gt; Controller.o  Auth.swift -\u0026gt; `swiftc` -\u0026gt; Auth.o Auth.o + Token.o -\u0026gt; `ar` -\u0026gt; libAuth.a (a Static library) Token.swift -\u0026gt; `swiftc` -\u0026gt; Token.o 3 - Create App Wrapper mkdir Cool.App 4 - Compile App code Foo.swift -\u0026gt; `swiftc` -\u0026gt; Foo.o Foo.o + Bar.o + libAuth.a + Core (dylib) -\u0026gt; `ld` -\u0026gt; cool - a binary that's made up of Foo.o, Bar.o and object files from within libAuth.a Bar.Swift -\u0026gt; `swiftc` -\u0026gt; Bar.o cool executable is placed within Cool.App directory.\n5 - Embed/Copy binaries into the App Wrapper  Copy Family dylib within Cool.App/Frameworks directory.  The app wrapper will be similar to: cool.app (an app wrapper) - cool - a binary that's made up of Foo.o, Bar.o and Vehicle.a - /Frameworks - Family.framework - Family (dylib) 6 - App Launch  User taps on cool.app cool binary is loaded into memory dyld loads the dependencies of cool binary -\u0026gt; Family dylib is loaded into memory. App is ready to use.  Where can things go wrong? Dependency Order  App binary is getting compiled before library is compiled. App binary is compiled with old compiled library code. This can happen when \u0026lsquo;Find Implicit Dependencies\u0026rsquo; is disabled in Scheme\u0026rsquo;s Build action.  Compilation  Typo in your symbols Missing a {, : , ], etc Not declaring a variable Not matching the interface of a function or not conforming to the protocol interface. Not importing a needed library etc.  Linking  Library is in the same Xcode workspace. However there\u0026rsquo;s no linkage defined between the app binary and the library. The implementation of something you linked to is missing. Two implementations exist for a given symbol.  Loading  The dynamic library isn\u0026rsquo;t copied into the /Frameworks directory. The name of the dynamic library doesn\u0026rsquo;t match with what\u0026rsquo;s expected. The dynamic library is moved from its location. The dynamic library is stripped of all its global symbols. Hence the main app can\u0026rsquo;t communicate with the dylib.  Any last notes? A big help in figuring these things out was just simply looking into the man (or help) pages of ld, dyld, clang, swiftc, strip, dsymutil. Make sure you look into for more.\nA big final program Suppose you need a single function from a library named foo.a. You might end up consuming space for every other variable, function, type in library foo.\nWhat can you do resolve this? Hopefully I\u0026rsquo;ll discuss this in my next upcoming post\n👉 Next Post - How Does the Linker Help Reduce App Size? What are the different types of linking - Part One\n","permalink":"https://mfaani.com/posts/devtools/optimizing-app-size/jargon/","summary":"To understand this post, I highly recommend everyone to watch the WWDC 2022 - Link fast: Improve build and launch times and WWDC 2018 - Behind the Scenes of the Xcode Build Process. They\u0026rsquo;re of the best talks I\u0026rsquo;ve ever seen.\nThis post covers some of the jargon and how it all comes together. And even though I love making puzzles, I\u0026rsquo;ll justify why I picked it as my cover.","title":"Optimizing Binaries - Build Pipeline Jargon"},{"content":"This the first post of a series I\u0026rsquo;m doing on how to optimize your app\u0026rsquo;s binaries. The post is more of a high level intro.\nDepending on the action (build, run, test, profile, analyze, archive) of your scheme, the process will have all or some of the following steps:\n Dependency analysis   What happens when you press build? So the first step is for the build system to take the build description, your Xcode project file. Parse it, take into account all the files in your project, your targets and the dependency relationships. Your build settings, and turn it into a tree-like structure called a directed graph. And this represents all the dependencies between the input and output files in your project and the tasks that will be executed to process them. Next the low-level execution engine processes this graph, looks at the dependency specifications and figures out which tasks to execute. The sequence or order in which they must be run and which tasks can be run in parallel. Then proceeds to execute them. From WWDC 2018 - Behind the Scenes of the Xcode Build Process 4:57\n Compile dependencies   Parse all files (swift, .m, .c, .cpp files) in module. Create object (intermediate) files that the CPU can understand. Each file is compiled individually The reason you need this step is because CPU does doesn\u0026rsquo;t understand swift (or python, go, ruby, etc.) The compilation creates an intermediate file that makes it easier for the CPU to understand your swift code\u0026hellip;  Link all object (intermediate) files   Link all the intermediate files and create a binary Each dependency/module/framework/library needs to get built. So if you have 20 dependencies. Then you\u0026rsquo;ll end up linking and creating 20 libraries (If those dependencies have some other dependencies, then those need to get compiled and linked as well). You\u0026rsquo;d also have one app binary linked as well. So you have to link a minimum of 21 times.  Copy resources and all binaries   Each framework that\u0026rsquo;s dynamically† linked will have to get copied into the app bundle. If you have 20 dependencies, then 20 frameworks need to get copied over into the app bundle. Xcode creates the .app bundle wrapper. Then copies the dynamically linked frameworks over into your .app bundle. The copying is necessary because Xcode pulls the frameworks in a two step process:  All frameworks get created in the build directory. Frameworks get copied into the final linked product. It\u0026rsquo;s like laying stuff on the floor, then copying each item into your backpack. Your backpack is the final linked product.    Post Processing  Generating dSYMs. Stripping symbols (from each binary). Code-signing   Archiving   Compresses/archives the Product Places the dSYMs in the archive. The dSYMs were already generated during the compilation step.  Distribution. However distribution isn\u0026rsquo;t part of the build or archive actions. It\u0026rsquo;s usually a step that you\u0026rsquo;d follow afterwards.  Note: Depending on the action:\n Some steps may be included or excluded. Example: Archiving and distribution don\u0026rsquo;t happen for debug builds. Also dSYMs aren\u0026rsquo;t generated unless you change the default settings. Some steps get processed differently. Example: DWARF is generated for debug builds. DWARF with dSYM is generated for archiving steps. Because Archiving steps is configured to use your \u0026lsquo;Release\u0026rsquo; configuration.  Pro tip: If you\u0026rsquo;re ever uncertain about the default value of a flag then just create a dummy new project/framework and test things out. This is because most of the time the default setting is the best choice.\n†: Static libraries, don\u0026rsquo;t have a separate binary in the final app bundle. Their code and symbols just get absorbed into app\u0026rsquo;s main binary. More on that later.\nFor the most part the flow is like this:\n References: Behind the Scenes of the Xcode Build Process\nThe series will include:\n Start with a small example on how to compile a single Swift file. Some detailed jargon. The difference of static vs. dynamic library and their impact on App Size. What\u0026rsquo;s the difference between Compilation and Linking? Where/how the Xcode Build Settings affect the compilation of things. Compiling source code vs. using pre-compiled binaries. Discuss an .app bundle anatomy and how to inspect the bundle and its binaries and get an app store thinning report. How to inspect a binary. How are symbols baked into the app. The stripping flags Apple has and what they each do and their affect on app size and dSYM. Pivot into CocoaPods and its faulty stripping behavior as well.  👉 Next Post - Optimizing Binaries - Build Pipeline Jargon\n","permalink":"https://mfaani.com/posts/devtools/optimizing-app-size/build-pipeline/","summary":"This the first post of a series I\u0026rsquo;m doing on how to optimize your app\u0026rsquo;s binaries. The post is more of a high level intro.\nDepending on the action (build, run, test, profile, analyze, archive) of your scheme, the process will have all or some of the following steps:\n Dependency analysis   What happens when you press build? So the first step is for the build system to take the build description, your Xcode project file.","title":"Optimizing Binaries - High Level Xcode Build Pipeline"},{"content":"A binary is the final product from linking all your source code. It\u0026rsquo;s executable. You can run commands against it.\nAn app, is merely a wrapper / directory, which includes that binary and other things.\nHow do you create a binary? First it\u0026rsquo;s important to understand what a binary is.\n You\u0026rsquo;ve used them every day in the terminal. Examples: ls, cp, mkdir. You pass certain parameters to them. They don\u0026rsquo;t have any file extension. They\u0026rsquo;re in machine code i.e. zeros and ones.  With some simplification, this is how we create a binary from our source code:\n  each .swift -\u0026gt; to a .o; All (necessary) .o -\u0026gt; linked together as a binary  So now you\u0026rsquo;re wondering what an object code is. Object codes, a.k.a object files are:\n Similar to binaries, object codes are also in binary format. Contain undefined symbols; symbols compiled from another source file. Unlike the final binary, an object code is useless by themselves. Because it contains undefined symbols.  Object codes are very much like pieces of a puzzle.\n Once you put all these pieces of the puzzle together in perfect order, then you have a complete picture. Much like a binary.\n If a piece of a puzzle is missing, then the linker will make it so that you have an \u0026lsquo;undefined symbol\u0026rsquo; error. If two identical pieces of a puzzle exist, then the linker throw you the \u0026lsquo;multiple definitions\u0026rsquo; error, since it wouldn\u0026rsquo;t know which one to choose.  OK. So what\u0026rsquo;s an app? An app is just a structured directory, containing the binary and some other files. If you want to create an app wrapper then all you have to do is: mkdir Cool.app.\nWhen Xcode builds your app, it does it in a two step process:\n Build all targets that your app depends on. Create your app wrapper / directory and then copy everything in the right place. To use Xcode jargon, the frameworks will get Embedded into the app bundle / wrapper.  🔍: Make sure you go and inspect the app bundle and see its content. You can even do this for your /Applications directory of your macos as well. You can also ditch Xcode and make your app without it. For more on that see Building osx app bundle\nFor example if an app named Cool had the following dependencies:\n Auth.a (static library) Core.framework (dynamic library)  Then the Build/Products directory will be in this structure:\n The info.plist has the mapping to know what binary it should refer to as its executable, the name of the app, what image it should use for the app icon, the bundle identifier, OS privacy prompts and many more. So when you double click on Safari.app on your macos, it will use the plist and figure out that it needs to open the Safari binary within the app wrapper.\nWrap up A binary is a standalone thing. An app is a packing of the binary with other stuff.\nAlso worth mentioning that while you can interact with apps through the command line, they\u0026rsquo;re usually opened and interacted through some user interface.\nFor binaries it depends. If you have an app wrapper, then that\u0026rsquo;s what you\u0026rsquo;d typically use. Otherwise, you\u0026rsquo;d be limited to the terminal.\nAcknowledgements and References  Huge shout out to some friends (who prefer to not be named) who helped me figure this all out. Compiler vs Linker - Alex Allain WWDC 2018 - Behind the Scenes of the Xcode Build Process  ","permalink":"https://mfaani.com/posts/devtools/whats-the-difference-between-an-app-bundle-and-a-binary/","summary":"A binary is the final product from linking all your source code. It\u0026rsquo;s executable. You can run commands against it.\nAn app, is merely a wrapper / directory, which includes that binary and other things.\nHow do you create a binary? First it\u0026rsquo;s important to understand what a binary is.\n You\u0026rsquo;ve used them every day in the terminal. Examples: ls, cp, mkdir. You pass certain parameters to them. They don\u0026rsquo;t have any file extension.","title":"Whats the Difference Between an App (bundle) and a Binary"},{"content":"Ranges in swift are super simple to create. Yet they come in various forms.\nlet r1 = 1...3 let r2 = 1..\u0026lt;3 let r3 = ...3 let r4 = 3... let r5 = ..\u0026lt;3 They all have range like characteristics, but have slightly different traits. It\u0026rsquo;s because they\u0026rsquo;re actually different types.\nDifferent Range Types let r1 = 1...3 // ClosedRange\u0026lt;Int\u0026gt; let r2 = 1..\u0026lt;3 // Range\u0026lt;Int\u0026gt; let r3 = ...3 // PartialRangeThrough\u0026lt;Int\u0026gt; let r4 = 3... // PartialRangeFrom\u0026lt;Int\u0026gt; let r5 = ..\u0026lt;3 // PartialRangeUpTo\u0026lt;Int\u0026gt; By having different types, the compiler can enforce certain behavior/restrictions within each type. Looking at the names without seeing the syntax that generates them can be confusing. Hopefully the code above helps with that.\n ClosedRange: Contains both lower bound and upper bound. Range: Contains the lower bound, but not the upper bound. PartialRangeThrough: A partial interval up to, and including, an upper bound. PartialRangeFrom: A partial interval extending upward from a lower bound. PartialRangeUpTo: A partial half-open interval up to, but not including, an upper bound.  Each type exposes a slightly different set of properties.\nlet r3 = ...3 // PartialRangeThrough\u0026lt;Int\u0026gt; r3.lowerbound // doesn\u0026#39;t compile r3.upperbound // compiles let r4 = 3... // PartialRangeFrom\u0026lt;Int\u0026gt; r4.lowerbound // compiles r4.upperbound // doesn\u0026#39;t compile let r5 = ..\u0026lt;3 // PartialRangeUpTo\u0026lt;Int\u0026gt; r5.lowerbound // doesn\u0026#39;t compile r5.upperbound // compiles tldr if a range doesn\u0026rsquo;t really have a lowerbound / upperbound, then the compiler doesn\u0026rsquo;t allow accessing it either.\nRange Usage Iterating over a specific range within a sequence: let range = 1...3 let nums = [8,9,10,11,12] for i in nums[range] { print(i) } // Output: 9,10,11 Iterating over a range itself: for i in r1 { print(i) } // Output: 1,2,3 Creating an Array: let array = Array(1...5) print(array) // [0, 1, 2, 3, 4, 5] Syntax Ranges are created using either the ... operator or ..\u0026lt;. Both have a condition that minimum \u0026lt;= maximum\nSome of their capabilities are:\n Intersection with another range Overlaps with another range Bounds (You may not have both upper and lower bounds. It just depends) Contains  None existing operators:\n// let c = 1\u0026lt;.. // ERROR // let y = 1\u0026lt;..11 // ERROR Note the importance of parenthesis: I got bit by this a number of times, so I thought I mention this:\nlet points = [3,5,2] [..\u0026lt;points.count - 1] // incorrect. [range - 1] has no meaning [..\u0026lt;(points.count - 1)] // correct let (x,y) = (2,10) x...y.forEach // incorrect. (x...y).forEach // correct Are the following the same? let closedRange = Int.min...3 let partialRange = ...3 Best way is to try them out\nlet numbers = [10, 20, 30, 40, 50, 60, 70] print(numbers[closedRange]) // 💣💣💣 ERROR: Negative Array index is out of range It\u0026rsquo;s because the range starts from index: -9223372036854775808 (Int.min) all the way to index: 3\nlet numbers = [10, 20, 30, 40, 50, 60, 70] print(numbers[partialRange]) // from the first index in the sequence, all the way to index: 3 // Prints \u0026quot;[40, 50, 60, 70]\u0026quot; print(numbers[3...]) doesn\u0026rsquo;t translate to from index:3 to index: Int.max. It translates to \u0026ldquo;I want everything after this point. In this everything after (and including) 3\u0026rdquo;\nBasically the start and end are implicit here. It\u0026rsquo;s based on the collection they are applied on. Not the range of the index type.\nWhat do docs say in regards to ranges that don\u0026rsquo;t have limits?  It is safe to use operations that put an upper limit on the number of elements they access.\n To be more accurate, the docs say (in full):\n Because a PartialRangeFrom sequence counts upward indefinitely, do not use one with methods that read the entire sequence before returning, such as map(_:), filter(_:), or suffix(_:). It is safe to use operations that put an upper limit on the number of elements they access, such as prefix(_:) or dropFirst(_:), and operations that you can guarantee will terminate, such as passing a closure you know will eventually return true to first(where:).\n Meaning the following have to process the entire range, before the application of map or filter finishes.\n(1...).map { $0 * 2 } // 💣💥 (1...).filter { $0 % 2 == 0 } // 💣💥 However the following is ok, because it\u0026rsquo;s immediately bounded by the array\u0026rsquo;s bounds.\nlet arr = [1,2,3][1...].map { $0 * 2 } print(arr) let arr2 = [1,2,3][1...].filter { $0 % 2 == 0 } print(arr2) Conversely in the case of dropFirst it only has to adjust a finite number of items in the range.\n(1...).dropFirst(5) // good From docs again\n The behavior of incrementing indefinitely is determined by the type of Bound. For example, iterating over an instance of PartialRangeFromtraps when the sequence’s next value would be above Int.max.\n Where does the knowledge of ranges become useful? I found it to be extremely useful during Advent of Code code challenges. I think it\u0026rsquo;s also very useful for Leetcoding and Interviewing. Like instead of trying to manually skip certain bounds within an array, I just modify the range I need to access within an array.\n Ranges help you filter the elements before you get into the for loop - Myself\n for (i,v) in [1,2,3,4,5] { if i =\u0026gt; 3 { return } } vs\nfor v in [1,2,3,4,5][..\u0026lt;4] { print(v) } for v in [1,2,3,4,5][newLowerBound..\u0026lt;newUpperBound] { print(v) } Can I create ranges for stuff other than numbers? Yes!\n Use the closed range operator (\u0026hellip;) to create a closed range of any type that conforms to the Comparable protocol.\n Character Range Like I\u0026rsquo;ve seen folks do\nlet alphabets = \u0026#34;abcdefghijk\u0026#34; However you can simply do:\nlet alphabets = \u0026#34;a\u0026#34;...\u0026#34;k\u0026#34; String Range You could create ranges for Strings. Think of them as two words in a dictionary (like a real book dictionary)\nlet r = \u0026#34;alpha\u0026#34;...\u0026#34;clpha\u0026#34; [\u0026#34;alphaa\u0026#34;,\u0026#34;d\u0026#34;, \u0026#34;cmllllll\u0026#34;, \u0026#34;cllzzzz\u0026#34;].forEach { print(r.contains($0)) // true, false, false, true } For String it\u0026rsquo;s basically a lexicographically i.e. a dictionary sort.\nCustom Type Range struct Person: Comparable { let name: String let salary: Int static func \u0026lt; (lhs: Person, rhs: Person) -\u0026gt; Bool { return lhs.salary \u0026lt; rhs.salary } } let range = Person(name: \u0026#34;Mohammad\u0026#34;, salary: 10000)...Person(name: \u0026#34;Matt\u0026#34;, salary: 20000) // Named After Matt Smollinger who\u0026#39;s mentored me through out my career + answered countless questions from me. Currently an EM at Ford. range.contains(Person(name: \u0026#34;Kotaro\u0026#34;, salary: 15000)) // true. Named after Kotaro Fujita who\u0026#39;s the co-organizer of PhillyCocoa, Side Project Spot Lot Podcast. Principal Engineer at Comcast and more. Surprisingly I wasn\u0026rsquo;t able to do:\nlet r2 = \u0026#34;a\u0026#34;...\u0026#34;m\u0026#34; print(r2.count) // Referencing property \u0026#39;count\u0026#39; on \u0026#39;ClosedRange\u0026#39; requires that \u0026#39;String\u0026#39; conform to \u0026#39;Strideable\u0026#39; Shout out to Josh Caswell for helping me figure it out:\nThe reason for this error is that there\u0026rsquo;s a conditional conformance for ClosedRange when its Bound is Strideable. See extension ClosedRange : Sequence where Bound : Strideable\nThe Swift Engineers have decided not to not have Character conform to Strideable. To learn why, see my other post on Why Can\u0026rsquo;t You Loop Over Ranges of Characters in Swift\nAny last words?  Often usage of ranges can be difficult. Because you might need to convert a Range into ClosedRange or vice versa and it\u0026rsquo;s not very straightforward. You might have to handle bounds. Example if an array is is empty then the 0...array.count - 1 range will translate to 0...-1 which results in a crash/error. Or you might have a left and right range where your range shrinks every time, this could lead to a range of 0, 1, or often negative. So you have to be considerate of all those. As a result you must always have safety checks in your ranges, otherwise your app will crash. To avoid that you should always have the following check if range.startIndex \u0026lt; range.endIndex before processing values at You can create ranges on anything that\u0026rsquo;s Comparable. However only for ranges that are Strideable you can do things like count or for loop. There\u0026rsquo;s another range type that we didn\u0026rsquo;t discuss. See unboundedrange  ","permalink":"https://mfaani.com/posts/swift/the-power-and-expressiveness-of-swift-ranges/","summary":"Ranges in swift are super simple to create. Yet they come in various forms.\nlet r1 = 1...3 let r2 = 1..\u0026lt;3 let r3 = ...3 let r4 = 3... let r5 = ..\u0026lt;3 They all have range like characteristics, but have slightly different traits. It\u0026rsquo;s because they\u0026rsquo;re actually different types.\nDifferent Range Types let r1 = 1...3 // ClosedRange\u0026lt;Int\u0026gt; let r2 = 1..\u0026lt;3 // Range\u0026lt;Int\u0026gt; let r3 = ...3 // PartialRangeThrough\u0026lt;Int\u0026gt; let r4 = 3.","title":"The power and expressiveness of Swift ranges"},{"content":"Been doing Advent of Code in the last couple of days. I completed Day 6. I have been doing it from an Xcode project. However I was thinking it would be nice if I could do it from command line. So I started to do some code clean up. First I started with renaming the folder from AOC to aoc. I did:\ncp -r AOC aoc I saw a new aoc directory created. I was happy. It was easy. Or at least it seemed easy.\nI went back to my AOC directory. I noticed a whole bunch of changes that I didn\u0026rsquo;t make. Confused! I tried figuring it out. I couldn\u0026rsquo;t.\nDecided to remove the changes with:\ngit checkout . git clean -fd I ran git status. Everything was good. Then I went back to my project. I re-created Day6 folder. Xcode through me an error.\n I ran git status again. Didn\u0026rsquo;t see any folder named Day6 added. From the project\u0026rsquo;s directory I did ls AOC. In the output I saw another AOC folder.\nI did ls AOC again. I saw AOC included again. It seemed that I somehow had managed to created a semi-infinite number of AOC folders. But how did I?\nIt\u0026rsquo;s because cp is case-insensitive\nHow can I test this out? Be sure to create a dummy folder if you wanted to test this out. Otherwise you could break an important directory in unrecoverable ways. SEE DISCLAIMER below before you proceed.\nHere\u0026rsquo;s how you can test it. Just do:\nmkdir blog cd blog touch post1 touch post2 cd .. cp -r blog BLOG # notice the different cases The output I got is:\n DISCLAIMER: Once I did this, I wasn\u0026rsquo;t able to easily delete the folder from the macOS bin. I was getting the follow error\n I found this answer that helped me figure out. I ultimately needed to do:\nsudo rm -rfv \u0026lt;path_of_folder_to_delete\u0026gt; Why does this happen? It happens because I didn\u0026rsquo;t eat my vegetables. But also because:\nEffectively cp -r blog BLOG is like doing cp -r blog blog. As if when you\u0026rsquo;re copying, the cp -r command sees new updates to what it just copied. It end up recursively copying the blog folder back into itself. By every new copy, the next paste it does gets bigger and bigger. 🤷\nThis made the source controlled project to have a directory with an identical name to the root directory. Each nested directory was also version controlled.\nAt the end I just ended up re-cloning my project. Only had a day of some unpushed commits, but that\u0026rsquo;s the price I was willing to pay.\nAdditionally even with the finder, I was unable to create two folders named Blog and blog. It\u0026rsquo;s because the Apple File System (APFS) is case-insensitive by default. The APFS formats are:\n APFS: Uses the APFS format. Choose this option if you don’t need an encrypted or case-sensitive format. (The default) APFS (Encrypted): Uses the APFS format and encrypts the volume. APFS (Case-sensitive): Uses the APFS format and is case-sensitive to file and folder names. For example, folders named “Homework” and “HOMEWORK” are two different folders. APFS (Case-sensitive, Encrypted): Uses the APFS format, is case-sensitive to file and folder names, and encrypts the volume. For example, folders named “Homework” and “HOMEWORK” are two different folders.  You can set the format from Disk Utility like this:\n Then if you select a volume, it will show up like this.\n  Shows the encrypted and case-sensitive formats. You can add/view from \u0026#39;Disk Utility\u0026#39;  All of this means if you have a directory that you need opening then doing cd Blog or cd blog makes no difference. The auto-complete from ZSH treats them the same.\nWhat\u0026rsquo;s the solution? Either:\n Use mv instead to move. It will do the right thing. Use cp -r. However give it a different name. And then manually change the name yourself. Change your APFS to be case-sensitive.  ","permalink":"https://mfaani.com/posts/devtools/how-cp-case-insensitivity-can-cause-chaos/","summary":"Been doing Advent of Code in the last couple of days. I completed Day 6. I have been doing it from an Xcode project. However I was thinking it would be nice if I could do it from command line. So I started to do some code clean up. First I started with renaming the folder from AOC to aoc. I did:\ncp -r AOC aoc I saw a new aoc directory created.","title":"How cp case-insensitivity can cause chaos!"},{"content":"Please read How to Think Recursively before reading this post.\nThis post re-applies the steps mentioned in the previous post on a more challenging question.\nQuestion Return all possible ways we can generate a well-formed parenthesis?\nExamples:\n if n = 1 then we can only form () if n = 2 then we can form (()) and ()() if n = 3 then we can form ((())), (())(), ()(()), (()()), (), (), ()  Let\u0026rsquo;s try applying our 4 steps:\nSummary of steps  What information do I need to pass down for each path: The total number of opens, the total number of closed or perhaps how many more have we opened vs closed. Under what conditions do I stop tree traversal?: If I\u0026rsquo;ve opened more parenthesis than our target. If I\u0026rsquo;ve closed more than we\u0026rsquo;ve opened. If I\u0026rsquo;ve closed more than our target. So I didn\u0026rsquo;t hit a base case. What then?: Recursively call the function. Bifurcate into opening and closing both. Don\u0026rsquo;t be smart: all code-paths at this point should call your function again. Let it exit any of its base cases in the next function execution. Figure how to make the first recursive call: Well we start from 0 open/close parenthesis.  enum P: String { case open = \u0026#34;(\u0026#34; case close = \u0026#34;)\u0026#34; } /// - Parameters /// - diff: number of opened parenthesis subtracted by the number of close parenthesis /// - openedCount: We need this to be sure we don\u0026#39;t exceed the total number of allowed parentheses func generateParen(num: Int) -\u0026gt; [String] { var ans: [String] = [] func h(diff: Int, paren: P, openedCount: Int, currentPath: String) { if diff == 0 \u0026amp;\u0026amp; openedCount == num { ans.append(currentPath) } else if diff \u0026lt; 0 { // we\u0026#39;ve closed more than we\u0026#39;ve opened } else if diff \u0026gt; num { // we\u0026#39;ve opened parenthesis more than our num } else { if openedCount \u0026lt; num { h(diff: diff + 1, paren: .open, openedCount: openedCount + 1, currentPath: currentPath + P.open.rawValue) }  h(diff: diff - 1, paren: .close, openedCount: openedCount, currentPath: currentPath + P.close.rawValue) } } h(diff: 1, paren: .open, openedCount: 1, currentPath: \u0026#34;(\u0026#34;) return ans } print(generateParen(num: 3)) // [\u0026#34;((()))\u0026#34;, \u0026#34;(()())\u0026#34;, \u0026#34;(())()\u0026#34;, \u0026#34;()(())\u0026#34;, \u0026#34;()()()\u0026#34;] So the above is good. It\u0026rsquo;s correct. However we didn\u0026rsquo;t follow one of our principles. Can you guess?\nThe highlighted lines is only executed if openedCount \u0026lt; num. But it\u0026rsquo;s cleaner if we just allowed it to be called, but handled it in the base case exits.\nCleaner solution In our else, we\u0026rsquo;re just calling the recursive function. We don\u0026rsquo;t have any conditions. We haven\u0026rsquo;t sneaked in any base-case handling into there. All our base cases are moved to the beginning of the function.\nThis has two advantages:\n Groups all the bases cases together. This makes it a lot easier to process logic. Reduces indentation from our code.  enum P: String { case open = \u0026#34;(\u0026#34; case close = \u0026#34;)\u0026#34; } func generateParen(num: Int) -\u0026gt; [String] { var ans: [String] = [] /// - Parameters /// - remainingOpen: number of parenthesis that aren\u0026#39;t closed. /// - openedCount: We need this to be sure we don\u0026#39;t exceed the total number of allowed parentheses func h(remainingOpen: Int, paren: P, openedCount: Int, currentPath: String) { if remainingOpen == 0 \u0026amp;\u0026amp; openedCount == num { ans.append(currentPath) } else if remainingOpen \u0026lt; 0 { // we\u0026#39;ve closed more than we\u0026#39;ve opened } else if remainingOpen \u0026gt; num { // the \u0026#39;diff (open vs close)\u0026#39; of what we\u0026#39;ve opened is more than our num. } else if openedCount \u0026gt; num { // we\u0026#39;ve opened parenthesis more than our num  } else { h(remainingOpen: remainingOpen + 1, paren: .open, openedCount: openedCount + 1, currentPath: currentPath + P.open.rawValue) h(remainingOpen: remainingOpen - 1, paren: .close, openedCount: openedCount, currentPath: currentPath + P.close.rawValue) } } h(remainingOpen: 1, paren: .open, openedCount: 1, currentPath: \u0026#34;(\u0026#34;) return ans } print(generateParen(num: 3)) In the above, we have three base cases. Followed by recursive calls. It\u0026rsquo;s much cleaner. We clearly isolate base case exits from recursive function calls. To say things differently: It\u0026rsquo;s fine to have if-else to recurse differently. But don\u0026rsquo;t have if conditions without else where it limits the recursion. Because the lack of recursion within an else implies an early exit condition\nBecause we added this cleanliness, we can identify something that can be improved.\nThe highlighted lines have overlapping logic. We can combine them into if openedCount \u0026gt; num. That\u0026rsquo;s because the openedCount \u0026gt; num is enough of a limiting factor and it won\u0026rsquo;t ever allow us to go beyond the max open.\n The other to think about this is the number of conditions needed. We only need to make sure:\n We don\u0026rsquo;t open more than the expected count. We don\u0026rsquo;t close more than we\u0026rsquo;ve opened.  Two conditions should require only two if clauses\u0026hellip;\n Cleanest Solution - removing extra check enum P: String { case open = \u0026#34;(\u0026#34; case close = \u0026#34;)\u0026#34; } func countParan(num: Int) -\u0026gt; [String] { var ans: [String] = [] func h(remainingOpen: Int, paren: P, openedCount: Int, currentPath: String) { if remainingOpen == 0 \u0026amp;\u0026amp; openedCount == num { ans.append(currentPath) } else if remainingOpen \u0026lt; 0 { // we\u0026#39;ve closed more than we\u0026#39;ve opened } else if openedCount \u0026gt; num { // we\u0026#39;ve opened parenthesis more than our num } else { h(remainingOpen: remainingOpen + 1, paren: .open, openedCount: openedCount + 1, currentPath: currentPath + P.open.rawValue) h(remainingOpen: remainingOpen - 1, paren: .close, openedCount: openedCount, currentPath: currentPath + P.close.rawValue) } } h(remainingOpen: 1, paren: .open, openedCount: 1, currentPath: \u0026#34;(\u0026#34;) return ans } print(generateParen(num: 3)) ","permalink":"https://mfaani.com/posts/interviewing/how-to-think-recursively-part2/","summary":"Please read How to Think Recursively before reading this post.\nThis post re-applies the steps mentioned in the previous post on a more challenging question.\nQuestion Return all possible ways we can generate a well-formed parenthesis?\nExamples:\n if n = 1 then we can only form () if n = 2 then we can form (()) and ()() if n = 3 then we can form ((())), (())(), ()(()), (()()), (), (), ()  Let\u0026rsquo;s try applying our 4 steps:","title":"How to Think Recursively - Part 2"},{"content":"These articles are about the gotchas I faced when trying to think recursively. The logic in principle should apply to most recursive problems. In this post, I will use the following question as a point of reference:\n Count how many ways you can climb a staircase. You can jump either one step at a time or two steps at a time.\n Example if there are 3 stair cases then you can either jump:\n1,1,1 2,1 1,2 In total there are three ways to get to the 3rd stair. It\u0026rsquo;s important to mention that that:\n1,1,2 or 2,2 go beyond the desired stair. Because they reach 4. Given that 4 is undesired. Anything after 4 is undesirable as well.\nFoundational steps   Know what it means to travel in a DFS vs BFS. Draw trees for yourself. If you haven\u0026rsquo;t mastered this, then it may be best to not dive deeper yet.\n Try what you\u0026rsquo;ve learned with the most simplest examples. Like the staircase example we\u0026rsquo;re using in this post. Understand the term branching factor. That means from each node, how many new nodes will become reachable.  If all you can do is jump one or jump three, then your branching factor is 2. If you were able to only jump six, then your branching factor is just 1. If you were able to jump one or five, or nine, then your branching factor is 3.      Try learning about the different classic (but simple) problems that are solved using trees. Just knowing the various kinds of problems and how the traverse visually happens, helps you understand other challenges.\n  Use a paper and try to visually solve the question in the simplest form of a tree example (a root and two leafs).\n  Then make your tree bigger by adding one more level to your tree and try to visualize the solution again. If you struggle here, then don\u0026rsquo;t go any further until you figure it out.\n   A good paradigm shift about trees is to think of them as \u0026lsquo;decision trees\u0026rsquo; or in certain cases \u0026lsquo;decision tables\u0026rsquo;\n Ask yourself \u0026lsquo;what information do I need to pass down for each path, so I can have all the variables needed to make a decision\u0026rsquo;?  A tree is basically made up of multiple paths. Each path needs to be able to maintain its own state. The state can\u0026rsquo;t be shared amongst other paths. If things are shared across paths then a state\u0026rsquo;s path gets overridden. You don\u0026rsquo;t want that. Because of this, the state can\u0026rsquo;t be a property of a class nor a local property of your function. It has to be an argument of the function that you pass down as you traverse the tree. In other words it\u0026rsquo;s the stack\u0026rsquo;s property. Here are examples of things you may need to pass down depending on the question:  The previous actions you took, e.g. Jumped 2, then jumped 3. Your helper function should take that as an array of previous jumps [2,3] The computed aggregation of previous jumps, e.g. the aggregation of jumps is 5. You could have jumped 5 through jumping either (5) or (1,1,3) or (4,1) or (1,4) or (2,1,1,1) etc. But all you need to pass is the sum of your jumps as 5. Other sophisticated problems may require you to pass more complex variables down your path.    Under what conditions do I stop tree traversal? What do I return (or don\u0026rsquo;t — in case of a Void function)? Identify condition(s) that your tree stops growing / hits a leaf / ends a path / terminates progression. Return a value. Examples:\nJump StairCase  End if reached the top of the stairs.  if totalJumpSum == targetSum { return 1 } Note: If you needed to return the actual jumps and not just the total \u0026lsquo;numbers\u0026rsquo; then:\nif totalJumpSum == targetSum { newAnswerArray = currentJumpsArray + lastJump // [jump2, jump5] + jump3 answerArray += newAnswer // answerArray += [jump2,jump5,jump3] }  Don\u0026rsquo;t forget you need to also stop when you go beyond the desired target. Example:  if totalJumpSum \u0026gt; targetSum { return 0 } So\n Return 1 if you found an answer, that would increase the total count. Return 0 if needed to terminate, but didn\u0026rsquo;t find a good answer.  As the coder you have to identify when you should stop traversing / recursing because either you:\n You reach the desired target/state. You reached an undesired target/state.  Reasons of termination / undesired states:\n Array, 2D Grid: Out of bounds, beyond our target Range, (sliding) window or 2 pointers: left index becoming greater than your right index Binary tree: there\u0026rsquo;s no other left / right node to traverse. This is similar to an array going out of bounds, except that there aren\u0026rsquo;t any bounds. There\u0026rsquo;s just nothing more to traverse to. Graph: often a visited node is considered a terminated state, because you\u0026rsquo;ve already processed it. It could lead to redundant work or cause cycles. Other kinds of undesired state:  Going passed a certain count Going over our budget Some required logic, becomes incorrect.  In DFS/Backtracking: Upon violating a Sudoku rule, you undo the last step or choice and try a different option. In recursion, this means returning to a previous state and exploring other possible paths after hitting an undesired or invalid condition. For example, if placing a number in Sudoku breaks the rules, you remove that number and try a different one in the same spot. In Sorted contexts or Binary Search: You go beyond the min/max values      So I didn\u0026rsquo;t hit a base case. What then?  Call your recursive function again. Pass whatever path specific information was needed.  You always need to mutate the previous path/state before you recurse again i.e. you have to do whatever\u0026rsquo;s necessary to reflect that you\u0026rsquo;ve moved from one node to another.   Note: You should not do any other checks at this point. Even if you know jumping 2 steps from 2, will go beyond 3 (the desired target), you shouldn\u0026rsquo;t add logic to skip calling the recursive function. You should just call your recursive function. Let it terminate / exit early in the next run of your function\u0026rsquo;s base case checks. This was a confusing point for me personally. I was never sure if I needed to be smart and skip calling my recursive function again. Now I know I shouldn\u0026rsquo;t be smart.\nBasically don\u0026rsquo;t early exits to your recursive calling.  Your overall structure should be like this:\nfunc findSolution(inputs: [Inputs]) { /* Call helper with its current state. */ } func helper(pathState: State) -\u0026gt; Value { /* if reached_base_case: return base_case_value else reached_another_base_case: return other_base_case_value else traverse_down_tree: - Traverse down the tree. - Update the path/stack. - Return the result of all paths˚ together. Each question has a different trick for combining. Example of different ways to combine: - With `+`. Example: sum of all nodes. - With `\u0026amp;\u0026amp;`. - With `||` - With `==` - max of all children. Sum of all children. etc - Processing every previous node with the current node. Example see my post on [longest increasing subsequence](https://mfaani.com/tags/longest-increasing-subsequence/) - If you have a local property and are updating your sum/max, then you don\u0026#39;t need to return a value. You just mutate the property of yours... - Other ways This recursive call will ultimately always lead to hitting a base case that stops recursing. Trust the process. Don\u0026#39;t try to preemptively end it within the normal recursion 😉 ˚: All paths mean all paths that start from the root and end with your terminating (desired + undesired) states. */ } Figure out how to call your recursive function from your main function Probably the easiest step. You just pass your current node and whatever target you have. The recursive helper function will bifurcate and create new branches as needed.\nFor our Count number of ways example:\nYou\u0026rsquo;re starting from stair 0. And your starting/current answer is 0. You also need to pass in your desired stair i.e. 3.\nSummary of steps So to do each of the four steps we discussed earlier:\n What information do I need to pass down for each path: The sum of the jumps so far. This value is what differentiates each path/branch for another. Under what conditions do I stop tree traversal?: If I reach the targeted stair. Or if jumped passed it. So I didn\u0026rsquo;t hit a base case. What then?: Recursively call the function. Combine the results of each node using +. Don\u0026rsquo;t be smart: Don\u0026rsquo;t try not calling your function. Let it exit any of its base cases in the next function execution. Figure how to make the first recursive call: Pass 0 as current node. Pass 3 as you desired target.  The heart of all of it is:  Write down unbounded code that just traverses. Then bound it with base cases.\n Solution A - Simplest choice: func howManyWays(num: Int) -\u0026gt; Int { return helper(origin: 0, target: num) } /// Recursively returns the total number of steps /// - Parameters: /// - origin: arrived step/node /// - target: desired step/node /// - Returns: Total number of ways from a given step/node func helper(origin: Int, target: Int) -\u0026gt; Int { if origin == target { return 1 } else if origin \u0026gt; target { return 0 } else { return helper(origin: origin + 1, target: target) + helper(origin: origin + 2, target: target) } } print(howManyWays(num: 4)) Solution B - Pass down a computed property So instead of passing down both target and current, we can pass remainingSteps\nfunc howManyWays(num: Int) -\u0026gt; Int { return helper(remainingSteps: num - 0) } /// Recursively returns the total number of steps /// - Parameters: /// - remainingSteps: The number of jumps required from current node to target node /// - Returns: Total number of ways from a given step/node func helper(remainingSteps: Int) -\u0026gt; Int { if remainingSteps == 0 { return 1 } else if remainingSteps \u0026lt; 0 { return 0 } else { return helper(remainingSteps: remainingSteps - 1) + helper(remainingSteps: remainingSteps - 2) } } print(howManyWays(num: 3)) Triage your recursion: Do I need a helper function? Managing Additional Parameters:\n State Maintenance: If your recursive solution requires maintaining extra state or parameters (like indices, accumulators, or flags) that aren\u0026rsquo;t part of the initial function call, a helper function can encapsulate these details. Simplifying the Public Interface: By using a helper function, you can keep the primary function\u0026rsquo;s signature simple, hiding the complexity from the user.  Initializing Values: When certain initial values or conditions are required for the recursion to work correctly, a helper function can set these up without exposing them to the user.\nI can\u0026rsquo;t come up with a solution Tree solutions are often a variation of figuring out the right computation/transition for:\n A node and its previous node. A node and some aggregation of all its previous nodes. A node and each individual previous node. A node and other nodes at the same level. Every subtree of the node and subsequent subtrees. A property of the tree or subtree.  Why does my code continue infinitely? It implies that either you:\n Haven\u0026rsquo;t handled all your base cases. Didn\u0026rsquo;t change the state upon traversing.   💡: Often my codes causes stack overflow, but I\u0026rsquo;m coding in Swift Playgrounds and don\u0026rsquo;t have access to a proper debugger. To be able to see logs without the Playgrounds app crashing (due to overflow), I add logs and exit early if I\u0026rsquo;ve called a recursive function more than 20 times. I just add a counter and increment it.\n💡 I also add conformance to CustomStringConvertible protocol so I can log in the exact way I prefer for my custom types.\n Should I return values at the end of my recursive functions? Or should they be void functions? With most programming problems, you can solve it by either way. At the moment I don\u0026rsquo;t have tips for when one becomes the better choice. But I\u0026rsquo;m sure there are moments when one is preferred over the other.\nI can draw the tree and traverse it on paper. I can\u0026rsquo;t do the code though. Any tips? As humans we normally draw our trees layer by layer (BFS). However in code, we usually go deep first, then reach a leaf. Climb back then and try the next unvisited node. In code we typically do DFS not BFS.\nUnderstanding that difference helps. Now try this:\n Draw the entire tree As you want to find an answer, just go down one path i.e. go deep. Ask yourself what causes mutation from previous node to next node in your path. Pass that down in your helper function.  I\u0026rsquo;m passing too many variables. What can I do? If you\u0026rsquo;re passing in a parameter that never changes, then that\u0026rsquo;s often a variable that can be eliminated. Example we don\u0026rsquo;t need to pass down the desired stair. Instead we can just pass the remaining stairs left to jump.\nAny last tips? Add documentation to your code. It often creates a rubber ducky moment for you\u0026hellip;\nAs a summarized alternate way of saying things:\n Think of what terminates recursion. It\u0026rsquo;s either by reaching a desirable target or reaching an undesirable target. \u0026lt;\u0026ndash; Base case Think of how to traverse from an adjacent state to the terminating state. How values and state change. Expand that into a formula which starts from your origin. See here for more.  Optimization (avoiding repeated calculations) I\u0026rsquo;m mainly leaving Memoization out of scope. Just a single yet important note:\nWhen implementing memoization, the choice of key to hash for each node is crucial to avoid redundant calculations and ensure correctness. In tree-based problems, the following attributes are often used to uniquely identify nodes, each serving a distinct purpose:\n The level of the node in the tree. Example you\u0026rsquo;re at the 3rd level away from the root. The value of the node in the tree. Examples:  The value of this node is 18. fibonacci(3). For more complicated problem, the value could be a complex value. For the 416. Partition Equal Subset Sum question I had to create a State type so I can hash based off multiple values.   The path took prior to arriving at that node in the tree. Example nodes A, R, K were took before.  More Reading See part 2 of this article.\n","permalink":"https://mfaani.com/posts/interviewing/how-to-think-recursively-part1/","summary":"These articles are about the gotchas I faced when trying to think recursively. The logic in principle should apply to most recursive problems. In this post, I will use the following question as a point of reference:\n Count how many ways you can climb a staircase. You can jump either one step at a time or two steps at a time.\n Example if there are 3 stair cases then you can either jump:","title":"How to Think Recursively - Part 1"},{"content":"I made some changes to the repo of our private pod. Pushed up my branch. Tests all ran. Then my colleague asked for some updates to my PR (pull request). I made changes and pushed it to GH. I wanted to merge the changes. But then CI was failing. So I ran CI again. It failed again. They say third time is the charm. So I ran it two more times. It still failed. I thought I had changed something in the project file, because I was getting some Swift compatibility error. I erased the derived data and clean build. I was able to build and run all tests. I wasn\u0026rsquo;t sure why things were still failing.\nI tried creating a new dummy PR with just an empty new file. It still failed. I then thought it was perhaps something in the last commit. It wasn\u0026rsquo;t.\nRoot Cause Problem was from an incompatible dependency. But how did it show up all of a sudden out of no where?!\nThe PodSpec was doing something like:\ns.dependency \u0026#39;FoodFire\u0026#39;, \u0026#39;~\u0026gt; 4.0\u0026#39; Note: ThePodfile didn\u0026rsquo;t have any mention of FoodFire. It just relied on the PodSpec for its dependencies.\nA single CI run does two things:\n Build, compile the project. Go through its unit-tests and UI-tests. Do pod lib lint --private --allow-warnings --verbose --fail-fast --skip-test  So lets say the last time you did pod update FoodFire the latest release of FoodFire was 4.1.3. This will write the following into your Podfile.lock:\nPODS: - APIClient (1.4.0) - FoodFire (4.1.3): # This is the restriction you ultimately end up with.  - Trucks (~\u0026gt; 7.7.1) - MyCoolPod (24.0.0): # THIS IS OUR POD - SmoothManager (= 2.0.1) - APIClient (~\u0026gt; 1) - SwiftyGif (= 9.0.1) - FoodFire (~\u0026gt; 4.0) # This the restriction set on the version.  - SmoothManager (2.0.1) - SwiftyGif (4.2.1) - Trucks (7.7.1) In your CI, you run the Example app and all its unit-tests + UI-tests. Great. Everything works.\nThen you do pod lib lint and all of sudden things go to hell 🤬.\nThe following will explain how that can happen.\n Podfile.lock is using FoodFire 4.1.3 An engineer goes in and publishes new 4.1.4 FoodFire PodSpec. Your CI then goes through its linting process:  run pod repo update so your agent has all the latest resources it needs run pod lib lint    What you have to understand is that:\n At step 3.1, your agent has now downloaded the 4.1.4 PodSpec. Your linting is rightfully oblivious to Podfile.lock. It will use 4.1.4 (not 4.1.3)  At this moment your linting vs. normal project building/running have bifurcated into using two different sets of dependencies.\nIn my situation, the 4.1.4 was causing a compilation error. Linting was failing.\n To be clear, the developer who who did a patch update made a huge mistake. If they properly did a major version bump to 5.0.0 then this issue would have never happened.\n Debugging tips to resolve such issues faster?  In general if things only fail on CI, then it\u0026rsquo;s usually a good idea to go step by step with exactly what your CI is doing. Run pod lib lint locally and see if things work. If doesn\u0026rsquo;t then look into the logs for anomaly. See if all the dependency is using the version you\u0026rsquo;re expecting. Pay closer attention to the version thats logged in CI. See if it\u0026rsquo;s the version you expect. Run the app locally with the version CI gives and see if things work as expected.  Summary of Problem pod lib lint is oblivious to Podfile \u0026amp; Podfile.lock. It only cares about the versions mentioned in the PodSpec.\nFor this reason, if you\u0026rsquo;re using the optimistic operator then the outcome of\npod repo update pod lib lint will vary depending on the time it was ran.\nIn general stuff you do in CI, dependency management, build phase scripts are things that go unnoticed when developing or reviewing code. Having an insight to look into these can be helpful.\nProposal Discussion: I explained the problem to some CocoaPods experts and mentioned my proposal. The following is that conversation [with some edits]:\nIn your CI before doing anything run: pod update \u0026lt;FooPod\u0026gt; — where: the PodSpec is specifying the version using the optimistic operator (~\u0026gt;). This allows your project\u0026rsquo;s UI, Unit-tests run with the latest possible dependencies.\nOrta one of CocoaPods original creators replied back with:\n yep, for the first few years we recommended adding the podfile.lock to .gitignore if you were shipping a library which is effectively the same thing.\nBut too many people misinterpreted it\n\u0026hellip;\nin the typescript compiler we don\u0026rsquo;t have a lockfile for example\n Interesting. And just to be sure. Also ignore /Pods. Right?\n yes, I normally always do this. but I understand the desire.\n My altered Proposal:  Keep the Podfile.lock + /Pods folder. Ask devs to do pod update more aggressively. You could just add this as part of your build phase, pre-commit hook etc.  Add a pod update step in our CI — before you build the Example app and run your tests.   For pod update to work as expected:  Avoid specifying any dependency in your Podfile — if it’s already specified in your PodSpec. Otherwise your example is restricting what you\u0026rsquo;ll test.   Note: In the case that you pass --skip-tests to your pod lib lint command, the linting is still needed. It checks two things:  If the PodSpec is valid. If app compiles using the latest dependencies specified in the PodSpec. Since this ignores the Podfile, it will act as aggressively as possibly. Which is good. It helps you catch any breaking change that wasn\u0026rsquo;t versioned correctly.    Final Summary The manner in which you maintain dependencies for a library as an owner is to be different from how you maintain dependencies for a project/app/library as a consumer. As library owners we have very little control over what the host app does for the dependencies we\u0026rsquo;ve listed in our PodSpec. We can\u0026rsquo;t tell if it\u0026rsquo;s using the minimum specified or the maximum specified. For this reason, it\u0026rsquo;s always great to not stay behind your dependencies i.e. you don\u0026rsquo;t want your example app to be locked to 4.1 of your dependency while your actual host app is using the 4.9 version of your dependency.\nTechnically speaking 🤞if all your dependencies use Semantic versioning correctly🤞 then you shouldn\u0026rsquo;t run into problems because of lagging behind a couple versions.\nHowever if you\u0026rsquo;re more aggressive with building your app with the latest dependencies, you can provide feedback faster to the maintainer of your dependency. Example of feedback are:\n App doesn\u0026rsquo;t compile. You didn\u0026rsquo;t do semantic versioning correctly. App compiles, but behavior is different:  Pod still returns an image, but it\u0026rsquo;s a totally different image and you weren\u0026rsquo;t expecting this The function syntax is the same, however it\u0026rsquo;s three times slower. The function syntax is the same, however the results are different. Jordan Rose, an Apple Engineer mentions a subtle note on semantic versioning on Binary Frameworks in Swift - 21:33 from the session.      I\u0026rsquo;ve added a new private property to the Spaceship class. And I\u0026rsquo;m using it in the Spaceship\u0026rsquo;s initializer.\nNow, neither of these things are going to appear in the module interface. They\u0026rsquo;re not part of your framework\u0026rsquo;s public API.\nSo this sort of change only requires updating the minor, or the Patch Version component.\nKeep in mind though that I did change the behavior of the initializer, and so if this was documented behavior before, then this would be a semantics breaking change, and clients would have to consider whether to update, and therefore, I should change the major version number instead.\n Acknowledgements I like to thank all the CocoaPod creators and contributors. Also give special thanks to Orta for sharing his insight and the historical context on maintaining a library.\n","permalink":"https://mfaani.com/posts/devtools/why-does-pod-lib-lint-fail-suddenly-fail-to-build/","summary":"I made some changes to the repo of our private pod. Pushed up my branch. Tests all ran. Then my colleague asked for some updates to my PR (pull request). I made changes and pushed it to GH. I wanted to merge the changes. But then CI was failing. So I ran CI again. It failed again. They say third time is the charm. So I ran it two more times.","title":"Why does pod lib lint suddenly fail to build?"},{"content":"My team was dealing with a large flow, where user can transition from multiple states or sometimes skip certain states. We didn\u0026rsquo;t have a centralized controller, every screen just had logic on where it should go next.\nThis made it difficult for us to see all our logic at once. We asked around and was told state machines are a good fit for our situation.\nState machines are void of any UX. You just pass an event to it. Based on the event and the current state, you update to a new state along with an optional command to act upon.\nA watered down example of a State machine is:\nCurrent state + Event -\u0026gt; Command -\u0026gt; New state ----------------------------------------------------------------------------------- .houseIsClean + .momEnteredHome -\u0026gt; nil -\u0026gt; (no state change) .houseWasAMess + .momEnteredHome -\u0026gt; .cleanHouse -\u0026gt; .cleaningHouse .cleaningHouse + .houseGotCleaned -\u0026gt; nil -\u0026gt; .houseIsClean .houseIsClean + .guestsWillCome -\u0026gt; .makeFood -\u0026gt; .makingFood .makingFood + .foodPrepared -\u0026gt; nil -\u0026gt; .waitForAllGuests .waitForAllGuests + .allGuestsArrived -\u0026gt; .serveDinner -\u0026gt; .eating Now let\u0026rsquo;s see how that can be deemed similar to graphs\u0026hellip;\nInterview Question You are climbing stairs. It takes n steps to reach the top.\nEach time you can either climb 1 or 2 steps.\nIn how many distinct ways can you climb to the top?\nExample if there are 3 steps to the top, then you can do:\n- 0 --jump1--\u0026gt; 1 --jump1--\u0026gt; 2 --jump1--\u0026gt; 3 - 0 --jump1--\u0026gt; 1 --jump2--\u0026gt; 3 - 0 --jump2--\u0026gt; 2 --jump1--\u0026gt; 3 Let\u0026rsquo;s draw a graph for counting how many ways we can get to 3: How Graphing is similar to state machines: To a certain point: when we apply the same concept:\n There\u0026rsquo;s a subtle note vs the house example. It\u0026rsquo;s real, meaning you have a messy house, a clean command, a transient state where the house is cleaning. In real world, there\u0026rsquo;s a user, a network stack which ultimately add delay and require you to work for user interactions or network responses.\nHowever with the climbing stairs there\u0026rsquo;s no jumping transient state. As there\u0026rsquo;s no waiting for a user or network response.\nA jump1 from step0, immediately leads to step1. It doesn\u0026rsquo;t lead to a jumping state. The state for the jumping becomes easier. Something like this:\n Current state -\u0026gt; Command -\u0026gt; New state ----------------------------------------------------------------------------------- step0 -\u0026gt; jump1 \u0026amp; jump2 -\u0026gt; step1 \u0026amp; step2 step1 -\u0026gt; jump1 \u0026amp; jump2 -\u0026gt; step2 \u0026amp; step3 step2 -\u0026gt; jump1 \u0026amp; jump2 -\u0026gt; step3 \u0026amp; step4 step2 -\u0026gt; jump1 \u0026amp; jump2 -\u0026gt; step3 \u0026amp; step4 step3 -\u0026gt; nil (terminating state) step3 -\u0026gt; nil (terminating state) step4 -\u0026gt; nil (terminating state) It\u0026rsquo;s important to note that:\n The only possible commands are \u0026lsquo;jump1\u0026rsquo; and \u0026lsquo;jump2\u0026rsquo;. (If we were allowed to jump 3 steps, then \u0026lsquo;jump3\u0026rsquo; would have been another command as well) However the current state can be a different number each time. From each state we bifurcate to apply two new commands. Because we bifurcate, we have to add the result (number of paths that lead to step n from node m) of the two branches. A code-path terminates when it reaches n or above.  If we reach n exactly then we return 1. If go over n, then we return 0.   In state machines, once you reach the terminating event, then your state machine is finished. However since we\u0026rsquo;ve bifurcated multiple times, the exploring will only terminate when all branches terminate.  💡Summary:  An Edge in a graph is like an command in a state machine. The current node in a graph is like the states in a state machine. The next node in a graph is like the after result of handling a command in a state machine. The end of a path is like reaching the end of a state machine. A leaf branch of a base case is like terminating a state machine. Meaning state machine finishes i.e. the branch can\u0026rsquo;t recurse any more. In the tree example, base case is either if:  Target has been reached. (You reached the desired stair) You\u0026rsquo;ve passed beyond your target. (You jumped pass the desired stair)   Real State Machines, wait for user interaction or network responses, or completion of executed tasks. Algorithm questions don\u0026rsquo;t.  Code func howManyWays(num: Int) -\u0026gt; Int { return helper(origin: 0, target: n) } func helper(origin: Int, target: Int) -\u0026gt; Int { if origin == target { return 1 } else if origin \u0026gt; target { return 0 } else { return helper(origin: origin + 1,target: target) + helper(origin: origin + 2, target: target) } } print(howManyWays(num: 4)) Code using memoization/caching: var cache: [Int: Int] = [:] func howManyWays(num: Int) -\u0026gt; Int { return helper(origin: 0, target: num) } func helper(origin: Int, target: Int) -\u0026gt; Int { if let val = cache[origin] { return val } var ans: Int defer { cache[origin] = ans } if origin == target { ans = 1 return ans } else if origin \u0026gt; target { ans = 0 return ans } else { ans = helper(origin: origin + 1, target: target) + helper(origin: origin + 2, target: target) return ans } } 💡 Summary - Appendix  If we\u0026rsquo;ve already handled a certain event + state combination, then we just store the output command in memory. By doing this, we don\u0026rsquo;t need to re-calculate the command every time.  Difference between State Machine and Trees/Graphs Does it matter how you got to a start node or state? Or all that matters is that you\u0026rsquo;re at a specific Node (event)? A state machine usually doesn\u0026rsquo;t care how you arrived at a certain event. All it cares about is 1. event 2. state. Based on those two, it can come up with a command. However trees and graphs, often require to know exactly the previous nodes you traversed through to arrive at a certain point. Example if you want to built a nav stack, then you might wanna hold a reference to a previous view you.\nIf you had a single path/branch through out your tree i.e. if it look liked a single line, then it would match with a state machine. However if you have multiple branches, which 99% of the time you do, then you must always:\n Keep an array to know how you arrived at a current node. Example you could arrive at node F from a path of [A, B] or [A, D, C] Update the path that was passed down to i.e. change it to [A, B, F], or [A, D, C, F] Pass the new path down to the next node.  Note: In the \u0026lsquo;How many ways you can climb\u0026rsquo; example, we didn\u0026rsquo;t have a need to know what our current path is, but for other questions example Generate Parentheses you need to pass certain \u0026lsquo;path-specific\u0026rsquo; variables down the path.\nTree vs a single branch A tree is made up of multiple branches. States (or values of interest i.e. the answer/returned value) can be associated with:\n  The entire tree\n The state is associated with the entire tree. You don\u0026rsquo;t need to pass it down your branch. You can just retrieve it from a property a class. Example: the can sum problem. It doesn\u0026rsquo;t matter if your sum of 7 was made through 5,2 or 2,2,2,1 or 4,3 or 7. All that matter is that you got there.    A single branch/path\n The state is associated with the current path. You need to pass and update that value down your branch. Example: the generate parenthesis problem. You have to pass down how many parenthesis you\u0026rsquo;ve opened or closed so far.    Another Dynamic Programming Example:  Assuming you can travel in only the right and down direction of a grid: count the number of possible ways to reach from one corner of a 3 x 3 grid to another.  Our commands are only  move right move down   Our events are:  arrived at grid (1,0) arrived at grid (0,1) arrived at grid (1,1) \u0026hellip; arrived at grid (3,3)   Our terminating state is:  arrived at (4,0)\u0026hellip;(4,3) or (0,4)\u0026hellip;(3,4). Because anything passed the 3rd item in the grid — either vertically or horizontally is a terminating state or out of bounds\u0026hellip;   State transitions that we don\u0026rsquo;t need to re-calculate over and over are:  storing the number of ways to reach to (3,3) from (3,2) storing the number of ways to reach to (3,3) from (2,3) storing the number of ways to reach to (3,3) from (2,2) \u0026hellip; storing the number of ways to reach to (3,3) from (0,0)      Note: Your graphs shouldn\u0026rsquo;t be cyclic otherwise you won\u0026rsquo;t be terminating/ending your graph or state machine. For this reason, State Machines are usually named \u0026lsquo;Finite State machines\u0026rsquo;.\n","permalink":"https://mfaani.com/posts/interviewing/how-understanding-state-machines-helps-with-building-trees-and-graphs/","summary":"My team was dealing with a large flow, where user can transition from multiple states or sometimes skip certain states. We didn\u0026rsquo;t have a centralized controller, every screen just had logic on where it should go next.\nThis made it difficult for us to see all our logic at once. We asked around and was told state machines are a good fit for our situation.\nState machines are void of any UX.","title":"How Understanding State Machines Helps With Building Trees and Graphs"},{"content":"Note: This post has got a bit longer than what I originally intended. You might want to skip to the very end to see the verdict and table first and then read again.\nI\u0026rsquo;ve started an iOS training course in my local community. At the beginning I thought it would be easy for me to just start training people with whatever I know. Later I decided to look at some available resources. I found great amount of material prepared by Apple. Only problem was that there were soo many that I got confused. In this article I plan on cataloging and comparing the different resources provided by Apple.\nWhat you must understand is:\n Some of Apple\u0026rsquo;s Documents (PDFs) are a tiny bit out sync with their Playground tutorials. Some of their books found through the Books app are available in Xcode 11, Xcode 12, and Xcode 13. Making it confusing because you may not notice that the book is for an older Xcode version. So just make sure you\u0026rsquo;re downloading the correct Xcode version. Some Playgrounds require a (free) subscription. See Apple Playground Release Notes here  Where to begin? Overall Apple has teaching material in their Swift Playgrounds app and PDF books. In my opinion what you choose, depends on how you answer the following questions.\n Are you looking for ages 10+ or 14+? Do you want to learn using Playgrounds or learn using PDF + Xcode? Do you want to learn SwiftUI or UIKit? Do you want to learn basic programming (for-loops, conditions, etc) or want to jump in straight into building apps?  Overall Apple has two distinct series. \u0026lsquo;Every Can Code\u0026rsquo; \u0026amp; \u0026lsquo;Develop in Swift\u0026rsquo;\n   Book Name Associated Playgrounds Tutorial type Swift vs. UIKit Real world vs Abstract concepts     Everyone Can Code Puzzles (Age: 10+) Learn to Code 1, Learn to Code 2, Spirals, \u0026lsquo;Rock, Paper, Scissors\u0026rsquo; and more Very engaging games with awesome graphics and cute characters for learning basic programmings The SwiftUI portion is not gamified, but still interactive It\u0026rsquo;s just concepts or simple views   Everyone Can Code Adventures (Age: 12+) Learn to Code 2, Blu\u0026rsquo;s Adventure, \u0026lsquo;Lights, Camera, Code\u0026rsquo;, Assemble your camera, Flashy Photos and more Very engaging games with awesome graphics and cute characters for learning basic programmings The SwiftUI portion is not gamified, but still interactive More advance concepts and simple views   Develop in Swift (Age: 14+) All Xcode, No Playgrounds Not interactive. Must just read PDF and follow in Xcode UIKit based. CompletionHandler based You build apps and learn about the app design process + discussions about real programmer\u0026rsquo;s problems and considerations    In this post I focus on going through the \u0026lsquo;Everyone can code\u0026rsquo; series. I have another post in the works for \u0026lsquo;Develop in Swift\u0026rsquo;.\nOverview Everyone Can Code - Curriculum Guide PDF \nPlaygrounds overview  Everyone Can Code Puzzles ebook: Everyone can code Puzzles  Score: 10 The book is amazingly rich with its content, instructions and graphics.  Chapters:  Sample content:  Playground - Learn to Code 1   Score: 9\n Only reason I gave a 9 is because it\u0026rsquo;s often too difficult — even for myself. The hints also aren\u0026rsquo;t that great. In the while loop, and algorithm chapters I had to look into the solutions myself. Imagine how difficult they must be. Otherwise the graphics are rich. The music and sound are top-notch. The pace and direction of the lessons are generally fantastic. And each lesson/chapter is very focused on learning one thing only — while using what you learned from previous    Chapters\n Commands: moveForward() Functions: add(5, to: 10) For Loops: for i in 1...number {} Conditional code: if lightIsGreen Logical Operators: if isBlocked \u0026amp;\u0026amp; isOnGem While Loops: while ageIsUnder18() Algorithms: functions and loops combined\u0026hellip;    The Playground itself should suffice, but if you wanted extra depth and breadth then you can use the \u0026lsquo;Everyone Can Code Puzzles\u0026rsquo; ebook.\nPlayground - Rock Paper Scissor My Score: 6\nMost of the game is already coded. You end up tweaking very few parts of it. As a result you don\u0026rsquo;t learn as much.\nPlayground - MeeBot Dances† My score: TBD\n†: You need to subscribe UBTech Jimu Robots:\n MeeBot Dances: In the “From Other Publishers” section of the More Playgrounds screen, tap UBTech Jimu Robots, then tap Subscribe.\nTap Get to download the MeeBot 2.0 Dances playground.\n I wasn\u0026rsquo;t able to find the correct URL for subscription. You can just download their playground from here.\nIt\u0026rsquo;s not a requirement but you can hook up your iPad to a real robot and then start coding. The robot can be purchased from here.\nPlaygrounds - Shapes, Answers, Spirals TBD\nPlayground - Learn to Code 2 My Score: 10\nThe Playground is focused on:\n Variables: var age = 0 Types: Features are called properties. Behaviors are called methods. Dot Notation Initialization. Creating the new. let expert = Expert() Parameters. Creating options: func paintRoom(color: Color) World Building. Instead of playing the game, you learn how to create the scene by adding gems and bricks. Arrays. Create a list. Your list has methods to remove, append, insert or iterate over its elements.  Everyone can Code - Adventures Every can code Adventures\n  Score: 10 It\u0026rsquo;s exceptionally good as the previous book, but then goes into much more depth.   Chapters\n  Objects in Views scene.place(ghost, at: Point(x:250, y:250))\n  Events and Handlers: toolName.onTouchMoved = handlerName(touch: )\n  Arrays: images.remove(at: Int)\n  More Events and Handlers: let xDirection = touch.position.x – player.position.x\n  Functions as arguments:\nscene.camera.when(snail, isWithin: 10.cm, do: { snail.flee(from: scene.camera.node, safeDistance: 100.cm) })   Return Types and Outputs: return graphic\n  Classes and Components:\n  public func start() { cameraView.start() }   Playground - Get Started with Apps My Score: 8 (Because the playgrounds often crash. Otherwise they\u0026rsquo;re great) It crashes in the way that you can\u0026rsquo;t open the playground anymore 😢. Even a hard reset didn\u0026rsquo;t fix things.\n 💡 To circumvent the crashing, copy the playground as you make progress. Then delete the previous copy you had. You\u0026rsquo;ll learn how to declare views, place images and text, deploy useful modifiers, and understand the power of composability. This isn\u0026rsquo;t your typical playground. It\u0026rsquo;s more of a mini-Xcode within Playgrounds. You can\u0026rsquo;t skip chapters. You have to go through it sequentially. There are no undo buttons. If you delete a file, there\u0026rsquo;s no undo button. I had to download a copy of the playground again, save the file I deleted into Files, then add the file into my original playground again.  Get Started with Apps, Keep Going with Apps TBD\nSolutions https://education-static.apple.com/teaching-code/learn-to-code-solutions.pdf\nTeacher Guides Teacher courses  Apple has courses that you can take as a teacher. You can become certified if you go through their courses. I tried on becoming a certified teacher for coding, but you first have to become certified in iPad or Mac training, then you can become certified for coding 🤷‍♂️. To become certified for iPad, you have to go through the following tutorials: The iPad itself Pages for iPad Keynote for iPad Numbers for iPad iMovie for iPad GarageBand for iPad From https://appleteacher.apple.com/#/home/resources search for \u0026lsquo;Playgrounds\u0026rsquo;, \u0026lsquo;Swift\u0026rsquo;, \u0026lsquo;Code\u0026rsquo;.  I must admit I learned a number of things in these tutorials. They\u0026rsquo;re relatively short. Takes about 3hrs to go through it all. The GarageBand tutorial was very difficult.\nYou can also get certified for Mac, and a number of other courses. See the rest of the courses:\n Teacher guides  Apple also has a \u0026lsquo;Teacher Guide\u0026rsquo; for its Every Can Code Puzzles \u0026amp; Everyone Can Code Adventures tutorials. They\u0026rsquo;re available to download from iBooks app. They have really good recommendations for what to say during the class, lots of nice images and (graphical) solutions.  Teacher forums  Also since late Aug 2022, Apple has launched forums: https://education.apple.com/en/community Can also search for #AppleEDUchat #AppleTeacher on Twitter.  How to start a coding club Tips from Swift Code Club on how to start a coding club:\n Have posters and T-Shirts created for the club. Make your first week a big event. Add friendly competitions. Recruit judges and mentors. Bring in a guest speaker. Show videos, etc. Record showcases and presentations. Allow people to pitch app ideas and showcase prototypes and then give feedback.  General Teaching Tips:  Bring kids to the front or ask them to share their screen while they\u0026rsquo;re coding. I had students who didn\u0026rsquo;t want to go the first time, but after trying it they were asking to back for the 2nd time. Take the pace as slow as possible. Understanding concepts is one thing. Understanding the syntax is another thing. I had a knowledge curse (Expert blindness) and thought I could finish the \u0026lsquo;Learn to code 1\u0026rsquo; tutorial in 2 session. It\u0026rsquo;s taking about 4 sessions, but we still don\u0026rsquo;t cover all lessons of every chapter. I forgot how I was slow in learning syntax myself too. You can skip chapters and jump to whatever you want. (The SwiftUI playgrounds don\u0026rsquo;t allows random access though.) Start simple. Ex if you have an assignment asking them to write a function then first begin by giving them the function signature and ask them to just implement it. Figuring out the function signature is a struggle of its own. Having a Slack workspace can help with fast easy sharing of code snippets. Kids are super fast to joining your Slack. Or ask questions that makes them think:  What happens if I remove the double quotes? Can you assign an Int to this (String) variable? Would the function take a different variable? Example, you can pass a different constant, case. Example change scaleToFit to scaleToFill. Where did this image come from? This questions asks them to think bigger than the only file they\u0026rsquo;re currently seeing i.e. see the project\u0026hellip;    Other Tutorials https://developer.apple.com/tutorials/swiftui\nVerdict - what course should I take?  If you have absolute beginners or under 14yrs of age then you can only use the \u0026lsquo;Everyone can code\u0026rsquo; Playground series.  If you want extra depth, read the ebooks while you do the Playgrounds. Otherwise just go with the Playgrounds. Regardless of using the ebooks during class, the ebooks are great resource for a student to have.   If you have 14+ years of age students and want to start with more real world code and are also ok with learning UIKit then use \u0026lsquo;Develop in Swift\u0026rsquo; series. Because UIKit will be useful and around in a lot of big companies until 2030\u0026hellip; If you\u0026rsquo;re not ok with teaching UIKit and want to only teach SwiftUI then you can\u0026rsquo;t use \u0026lsquo;Develop in Swift\u0026rsquo;. Must use the various Playgrounds Apple has.  ","permalink":"https://mfaani.com/posts/training/what-resources-does-apple-provide-for-teaching-ios-to-students/","summary":"Note: This post has got a bit longer than what I originally intended. You might want to skip to the very end to see the verdict and table first and then read again.\nI\u0026rsquo;ve started an iOS training course in my local community. At the beginning I thought it would be easy for me to just start training people with whatever I know. Later I decided to look at some available resources.","title":"What Resources Does Apple Provide for teaching iOS to students?"},{"content":"I always got confused as to what\u0026rsquo;s the end result my sort. I wasn\u0026rsquo;t sure if it would end up being ascending or descending. The ultimate trick is to not think of up vs down. Instead think of increasing/decreasing from left to right.\nWe perceive arrays as horizontal beings. Hence left and right make more sense vs up and down\nlet nums = [1,4,2,3] let sorted_nums = arr.sorted(by: { $0 \u0026lt; $1 // left is smaller [1,2,3,4] i.e. ascending }) If you see $0 \u0026lt; $1 then left side is smaller than the next item. i.e. it\u0026rsquo;s ascending.\nlet nums = [1,4,2,3] let sorted_nums = arr.sorted(by: { $0 \u0026gt; $1 // left is bigger [4,3,2,1] i.e. descending }) If you see $0 \u0026gt; $1 then right side is smaller than the next item. i.e. it\u0026rsquo;s descending. Similarly writing array.sorted(by: \u0026lt;) is identical.\nAlternate way:  array.sorted(by: \u0026lt;) will make it increasing. array.sorted(by: \u0026gt;) will make it decreasing.  And if I don\u0026rsquo;t pass a block? It will make the array increasing. Increasing is more natural as that\u0026rsquo;s how we\u0026rsquo;d list numbers from 1\u0026hellip;n and we almost never list numbers n\u0026hellip;1.\nlet nums = [1,4,2,3] print(nums.sorted()) // [1,2,3,4] $0 vs $1 Never place the $1 on the left side when sorting. $0 is the first number, $1 is the second number in the comparison. If you switch them then the order will get reversed.\n","permalink":"https://mfaani.com/posts/interviewing/arrays/which-way-am-i-sorting/","summary":"I always got confused as to what\u0026rsquo;s the end result my sort. I wasn\u0026rsquo;t sure if it would end up being ascending or descending. The ultimate trick is to not think of up vs down. Instead think of increasing/decreasing from left to right.\nWe perceive arrays as horizontal beings. Hence left and right make more sense vs up and down\nlet nums = [1,4,2,3] let sorted_nums = arr.sorted(by: { $0 \u0026lt; $1 // left is smaller [1,2,3,4] i.","title":"Which Way Am I Sorting?"},{"content":"Question: You have a binary tree. But only two of its elements have been swapped. This makes it a faulty binary tree. The challenge is to swap those two elements. And fix the tree.\nSolution I knew I had to traverse it. But then what?\nWith a little help from reading online, I realized I should traverse it, and store the values into an array. Then you just loop the array and find the bad indexes.\nHere\u0026rsquo;s the tricky part:\nSuppose your array is something like:\n[1, 2, 4, 3, 6, 7, 8] You loop and find out 4 is in a bad spot. You swap it with 3. And your tree is fixed.\nHowever if your array ends up like this:\n[1, 2, 8, 4, 6, 7, 3] You loop and find out 8, is in a bad spot. You swap it with 4 and up with:\n[1, 2, 4, 8, 6, 7, 3] but that\u0026rsquo;s still incorrect.\nSo once you have the array built, what you need to do is:\n Find the first item that not in its place. Let\u0026rsquo;s name this f (for first). Continue comparing every two consecutive pair to find the last item that\u0026rsquo;s not in its place. Let\u0026rsquo;s name this s (for second). As soon as you find the 2nd item, then you can stop searching. Swap those two items.  To show you step two visually:\n[1, 2, 8, 4, 6, 7, 3] ↑ ↑ ❗️ ❗️ f s [1, 2, 8, 4, 6, 7, 3] ↑__↑ ↑__↑ first-pair second-pair f ← -\u0026gt; s Perform swap between `f` and `s`: [1, 2, 3, 4, 6, 7, 8] The heart of all the code would be:\nvar fBadVal: Int? var sBadVal: Int? var didFindFirstNodeThatNeedsSwapping = false for i in 0..\u0026lt;tree.count { if let next = tree[safe:i + 1], next.val \u0026lt; tree[i].val{ if didFindFirstNodeThatNeedsSwapping == false { fBadVal = tree[i].val didFindFirstNodeThatNeedsSwapping = true } sBadVal = tree[i + 1].val } }   The swapping nodes happen between the first node of the first bad pair (8) \u0026\u0026 the second node of the second bad pair (3). Setup of bad Binary tree: class TreeNode { var val: Int var left: TreeNode? var right: TreeNode? init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { self.val = val self.left = left self.right = right } } var one = TreeNode(1, nil, nil) var eight = TreeNode(8, nil, nil) // bad position var six = TreeNode(6, nil, nil) var three = TreeNode(3, nil, nil) // bad position var two = TreeNode(2,one, eight) var seven = TreeNode(7, six, three) // root node var four = TreeNode(4, two, seven) 1, 2, 8, 4, 7, 6 , 3 4 / \\ 2 7 / \\ / \\ 1 8 6 3 Actual code: class TreeFixer { var tree: [TreeNode] = [] func recoverTree(_ root: TreeNode?) { // traverse the tree  inOrder(root) { node in node.map {tree.append($0)} } print(tree.map {$0.val}) // find the elements where they\u0026#39;re not matching... var fBadVal: Int? var sBadVal: Int? var didFindFirstNodeThatNeedsSwapping = false for i in 0..\u0026lt;tree.count { if let next = tree[safe:i + 1], next.val \u0026lt; tree[i].val{ if didFindFirstNodeThatNeedsSwapping == false { fBadVal = tree[i].val didFindFirstNodeThatNeedsSwapping = true } sBadVal = tree[i + 1].val } } print(fBadVal, sBadVal) // traverse again \u0026amp; swap elements inOrder(root) { node in node.map { node in if node.val == fBadVal { node.val = sBadVal! } else if node.val == sBadVal { node.val = fBadVal! } } } // print new tree, so we can validate our work... print(\u0026#34;SORTED TREE: InOrderTraversal\u0026#34;) inOrder(root) { node in print(node?.val) } } // Traverse tree and return a block handler for each node.  func inOrder(_ root: TreeNode?, nodeHandler: (TreeNode?) -\u0026gt; ()) { if let left = root?.left { inOrder(left) { node in nodeHandler(node) } } nodeHandler(root) if let right = root?.right { inOrder(right) { node in nodeHandler(node) } } } } extension Array { public subscript(safe index: Int) -\u0026gt; Element? { guard index \u0026gt;= 0, index \u0026lt; endIndex else { return nil } return self[index] } } let fixer = TreeFixer() fixer.recoverTree(four) Time Complexity  Traversing is O(n). Appending to the array is O(1). Traversing the array is O(n). swapping in the tree is O(n).  Given that they\u0026rsquo;re not nested to total Time complexity is O(n)\nSpace Complexity O(n) because you need an array.\nSummary:  Often translating a tree into an array can be helpful. Using an inOrder method that takes a handler block can be helpful. Using a safe subscript for the array\u0026hellip;something that Swift doesn\u0026rsquo;t have can be helpful. main trick was to continuously look for 2nd index.  ","permalink":"https://mfaani.com/posts/interviewing/recover-binary-tree/","summary":"Question: You have a binary tree. But only two of its elements have been swapped. This makes it a faulty binary tree. The challenge is to swap those two elements. And fix the tree.\nSolution I knew I had to traverse it. But then what?\nWith a little help from reading online, I realized I should traverse it, and store the values into an array. Then you just loop the array and find the bad indexes.","title":"Recover Binary Tree"},{"content":"A good number of interview questions require you to constantly split an array/string in half.\nThis is relatively easy to achieve when the array count is odd.\nHowever when the count is even, it\u0026rsquo;s not as easy.\nlet a = [1,3,8,10,22] // middle index is 2 let b = [1,3,8,10] // middle index is 1.5 which is non-existent. So what now? Most important thing to note is: There\u0026rsquo;s no such thing as \u0026ldquo;middle index\u0026rdquo; when the count is even, I mean there\u0026rsquo;s two middles in that case. I guess the \u0026ldquo;middle\u0026rdquo; are those two indices.‌‌ ‌But then you have to pick one. For this reason it\u0026rsquo;s often better to think of it as a pivot because it\u0026rsquo;s really the 2nd index. The 2nd index isn\u0026rsquo;t the middle index of a 4 element array.\nMoving away from \u0026lsquo;find the middle index\u0026rsquo; to a \u0026lsquo;find the pivot index which we\u0026rsquo;ll use as a middle index\u0026rsquo; helped me understand the concept better.\nAfter that, you typically just need logic that handles all three cases of before, after and the index itself or similarly lower, greater, equal to the index itself.\nExample - Flip an array [0,0,2,2] -\u0026gt; [2,2,0,0] The general way to calculate the pivot index is either:\nlet pivotIndex = (lower + upper) / 2 let pivotIndex = lower + (upper - lower) / 2 # This approach helps avoid integer overflow. In swift we get the integer number which is automatically rounded down. [0,0,2,2] pivot index = (0 + 3) / 2 = 1\n[0,0,2,2] | pivot  indexes before pivot: swap indexes equal to pivot: swap (when the range is odd, this would be a no op) indexes after pivot: ignore, as they were swapped with indexes before pivot.  Example - Binary Search Question - Find 99 in the following array [2, 7, 20, 24, 40, 99] 1st Iteration pivot range = 0 -\u0026gt; 5\npivot index = (0 + 5) / 2 = 2.5 -\u0026gt; pivot is 2\n[2, 7, 20, 24, 40, 99] | | | left pivot right 2nd Iteration pivot range = 3 -\u0026gt; 5\npivot index = 4. It\u0026rsquo;s now actually the middle of its range.\n[2, 7, 20, 24, 40, 99] | | | left pivot right 3rd Iteration pivot range = 5 -\u0026gt; 5\npivot index = 5. left and right index are also index 5\n[2, 7, 20, 24, 40, 99] | left pivot right Different way of seeings things We\u0026rsquo;re not always cutting the array/section in half.\n For odd ranges: you calculate the middle index correctly. Yet you’re not precisely cutting the section in half. Example:  [2, 7, 20, 24, 40] pivot point is index: 2.\n  You will drop three items: (20, 24,40) if you’re looking for 2 or 7.\n  You will drop three items: (2, 7, 20) if you’re looking for 24 or 40.\n  You will drop four items: (2, 7, 24, 40) if you’re looking for 20.\n  For even ranges: You\u0026rsquo;re not really calculating a middle. You may or may not end up precisely cutting the section in half. Example:\n  [2, 7, 20, 24, 40, 99] pivot point is index: 2.\n You will drop four items: (20, 24, 40, 99) if you’re looking for 2, 7. You will drop three items: (2, 7, 20) if you’re looking for 24, 40, 99. You will drop five items: (2, 7, 24, 40, 99) if you’re looking for 20.  👆 isn\u0026rsquo;t to make you think how many you\u0026rsquo;re dropping. It\u0026rsquo;s just once you realize you\u0026rsquo;re not always cutting in half, things actually start to make more sense.\nAttention Both 1st and 2nd approach will result in an index 0 for an empty array. Accessing index 0 will result in an \u0026ldquo;out of bounds\u0026rdquo; error. So you must exit early for empty arrays.\nConclusion Don\u0026rsquo;t think of middle index as the absolute middle index. Think of it more as a pivot point. Things just work as long as you have correct logic to handle all varying cases (less than, equal to, greater than)\nConcluding strategy is:  Find the range Find a pivot. Don\u0026rsquo;t think of it has a middle index. Have appropriate logic to process all the following cases:  Before pivot The pivot itself After pivot   Find new range. Then Find new Pivot. Repeat steps again  Acknowledgments Special thanks to Mira, Suyash and Arthur for answering my questions so I can put this post together.\n","permalink":"https://mfaani.com/posts/interviewing/how-to-calculate-the-middle-index/","summary":"A good number of interview questions require you to constantly split an array/string in half.\nThis is relatively easy to achieve when the array count is odd.\nHowever when the count is even, it\u0026rsquo;s not as easy.\nlet a = [1,3,8,10,22] // middle index is 2 let b = [1,3,8,10] // middle index is 1.5 which is non-existent. So what now? Most important thing to note is: There\u0026rsquo;s no such thing as \u0026ldquo;middle index\u0026rdquo; when the count is even, I mean there\u0026rsquo;s two middles in that case.","title":"How to Calculate the Middle Index?"},{"content":"This post is the result of some post-discussion on my Swift Strings for iOS interviewing post.\nGiven that characters look like one another or that are invisible code points I thought there might be some room for abuse. Luckily Unicode has great documentation security. At the high level there are two types of security issues:\nVisual Security Issues  Suppose that the user gets an email notification about an apparent problem in their Citibank account. Security-savvy users realize that it might be a spoof; the HTML email might be presenting the URL http://citibank.com/\u0026hellip; visually, but might be hiding the real URL. They realize that even what shows up in the status bar might be a lie, because clever Javascript or ActiveX can work around that. (And users are likely to have these turned on, unless they know to turn them off.) They click on the link, and carefully examine the browser’s address box to make sure that it is actually going to http://citibank.com/\u0026hellip;. They see that it is, and use their password. However, what they saw was wrong—it is actually going to a spoof site with a fake “citibank.com”, using the Cyrillic letter that looks precisely like a ‘c’. They use the site without suspecting, and the password ends up compromised.\n Non-visual Security Issues  For example, suppose that strings containing the letters “delete” are sensitive internally, and that therefore a gatekeeper checks for them. If some process casefolds “DELETE” after the gatekeeper has checked, then the sensitive string can sneak through. While many programmers are aware of this, they may not be aware that the same thing can happen with other transformations, such as an NFKC transformation of “Ⓓⓔⓛⓔⓣⓔ” into “delete”.\n The technical term for similar looking characters is confusables.  Similar looking characters  One of main places where having knowledge about confusables is domain names.\nWhat happens if instead of typing www.google.com, someone is given a fake URL of www.gòogle.com. Or what happens if instead of https://www.bankofamerica.com someone is given a fake URL of https://www.bânkofamerica.com.\nLet\u0026rsquo;s try them:\ngòogle.com --\u0026gt; (is represented as) xn-gogle-uta.com   The punycode encoding is different  bankofamerica.com --\u0026gt; xn-bnkofamerica-pbb.com   The punycode encoding is different  That\u0026rsquo;s weird. Why is the URL changing? Anytime a confusable is part of the domain name, then the browser will alter the domain name to something else. The end result is a confusing and perhaps ugly domain. Browsers purposely make the domain name ugly, so users are alerted that the URL they used is phony. Upon seeing that the user is more likely to double check things and perhaps not enter credentials for that URL.\nUsing more technical terms:\n Browsers will typically turn confusables into punycode\n  Punycode is a representation of Unicode with the limited ASCII character subset used for Internet hostnames. Using Punycode, host names containing Unicode characters are transcoded to a subset of ASCII consisting of letters, digits, and hyphens, which is called the letter–digit–hyphen (LDH) subset. For example, München (German name for Munich) is encoded as Mnchen-3ya.\n tldr Punycode is a domain name with non-ascii code turned into ascii code.\nIs every character with diacritics a confusable? No! If you just tried münchen.com, you won\u0026rsquo;t see it changed in the address bar of your browser. It won\u0026rsquo;t get prefixed with xn because ü is NOT considered a confusable.\nWhen do domain names get punycode encoding? Any time they contain non-ASCII characters.\nWill we always see the punycode encoding? Punycode encoding may happen without you seeing it. Safari makes a distinction between the \u0026ldquo;real domain name\u0026rdquo;, used for DNS requests and such, and the \u0026ldquo;user-visible\u0026rdquo; domain name, which is used in the address field, status bar, etc.\nThe real domain name is always converted to punycode, the \u0026ldquo;user-visible\u0026rdquo; one is only converted when needed for security i.e. it\u0026rsquo;s converted if confusable characters were used.\nCan you share an example of punycode encoding that doesn\u0026rsquo;t have security concerns? This is the case for most non-english domain names. They may use non-latin characters (Hebrew, Arabic, Chinese, etc) or use latin-based characters (French, German, Italian, etc).\nSurprisingly I\u0026rsquo;m having a hard time finding actual domains names that use non-ascii pattern. As one example: räksmörgås.josefsson.org\nBut for more see Internationalized domain name .\nhttps://github.com/WebKit/WebKit/blob/main/Source/WTF/wtf/URLHelpers.cpp#L833 – this is where it computes the user-visible form of the URL. The checks for whether the host name contains lookalike characters are within allCharactersInAllowedIDNScriptList. It looks like it prohibits some Unicode scripts entirely, and also prohibits individual lookalike characters.\nSo can we have non-English URLs? Yes. But they get mapped. Because all DNS lookups for domain names, is restricted to use ASCII. Examples:\n  domain name real domain name (used for DNS) user visible domain (what users see)     apple.com (all characters are ascii) apple.com apple.com   tòp.com (non-ascii are used. But they\u0026rsquo;re confusables) xn\u0026ndash;tp-2ja.com xn\u0026ndash;tp-2ja.com   räksmörgås.josefsson.org (non-ascii are used. But they\u0026rsquo;re not confusable) xn\u0026ndash;rksmrgs-5wao1o.josefsson.org räksmörgås.josefsson.org    To see the resolved URL, you can use the browser\u0026rsquo;s console. Networking.\n  Notice the difference in real domain vs user facing domain.  What\u0026rsquo;s the difference between URL and domain name? URL: https://www.arsenal.com/history\nProtocol: https://\nDomain name: arsenal.com\npath: /history\nThe application of punycode is solely for the domain name. Not other elements of the URL\nAcknowledgements Major shout out to Mark Rowe who answered all my questions so I can put together this post.\nOther References  Must see: Internationalized Domain Names (IDN) FAQ Internationalized domain name - wiki  ","permalink":"https://mfaani.com/posts/unicode-security/","summary":"This post is the result of some post-discussion on my Swift Strings for iOS interviewing post.\nGiven that characters look like one another or that are invisible code points I thought there might be some room for abuse. Luckily Unicode has great documentation security. At the high level there are two types of security issues:\nVisual Security Issues  Suppose that the user gets an email notification about an apparent problem in their Citibank account.","title":"Unicode Security"},{"content":"Question A simple compression algorithm would be to replace repeating characters with their count.\nExample aaa --\u0026gt; a3 aaabb --\u0026gt; a3b2 aaabbaa --\u0026gt; a3b2a2 Want to stand out? Ask if there\u0026rsquo;s a difference between a \u0026amp; A and if you need to add uppercase \u0026amp; lowercase together or need to separate them.\nIn my implementation I assumed they\u0026rsquo;re different.\nCode // start counting, then upon seeing a diff, write the count. func compress(_ str: String) -\u0026gt; String { let chars = Array(str) guard !chars.isEmpty else { return str } var currentChar = chars.first! var currentCount = 1 var ans: String = \u0026#34;\u0026#34; guard chars.count \u0026gt; 1 else { return \u0026#34;\\(currentChar)\\(currentCount)\u0026#34;} for i in 1..\u0026lt;chars.count { if chars[i] == currentChar { // increment currentCount += 1 } else { // append subsection ans.append(\u0026#34;\\(currentChar)\\(currentCount)\u0026#34;) // reset currentChar = chars[i] currentCount = 1 } } ans.append(\u0026#34;\\(currentChar)\\(currentCount)\u0026#34;) return ans } Tests print(compress(\u0026quot;aaabbbbccaA\u0026quot;)) // a3b4c2a1A1 print(compress(\u0026quot;a\u0026quot;)) // a1 print(compress(\u0026quot;\u0026quot;)) // ","permalink":"https://mfaani.com/posts/interviewing/challenges/compress-string/","summary":"Question A simple compression algorithm would be to replace repeating characters with their count.\nExample aaa --\u0026gt; a3 aaabb --\u0026gt; a3b2 aaabbaa --\u0026gt; a3b2a2 Want to stand out? Ask if there\u0026rsquo;s a difference between a \u0026amp; A and if you need to add uppercase \u0026amp; lowercase together or need to separate them.\nIn my implementation I assumed they\u0026rsquo;re different.\nCode // start counting, then upon seeing a diff, write the count.","title":"Compress String"},{"content":"Question You can edit a string in three different ways: insert, remove or replace a character. Write a function to see if two strings are one or zero edits away.\nExample like, like --\u0026gt; true (zero edits) like, likes --\u0026gt; true (one edit: remove/insert) like, life --\u0026gt; true (one edit, replace) like, lik --\u0026gt; true (one edit: remove/insert) like, pine --\u0026gt; false (two edits) like, lion --\u0026gt; false (two edits) Strategy   For most questions that require you to return true or false, you can reduce the scope of the question by removing noise. Often that means exiting early or having conditions that make it impossible to have a true result\u0026hellip; Example: If the string lengths differ by 2 or more characters, then you can exit / return an answer.\n  Try not to solve this problem in one go. Break it down into two checks:\n   Does replacing a character make my strings equal? Does inserting a character make my strings equal?   Keep things simple. See how I avoid adding complex logic by intentionally passing the longer string as first parameter of isOneInsertAway.\n  Stop caring about things you don\u0026rsquo;t need. You can find the answer without knowing which character, at which index needs to be replaced. A LOT of questions often can be resolved without caring for a lot of things. Example if you\u0026rsquo;re solving a question on knowing if there\u0026rsquo;s a path between A, B. Then you don\u0026rsquo;t need to care how you got from A to B. You just need to know if there\u0026rsquo;s a path. Not caring simplifies your thought process. For this reason it\u0026rsquo;s often good to write down things that don\u0026rsquo;t matter.\n  Code func isOneOrZeroEditsAway(_ str1: String, _ str2: String) -\u0026gt; Bool { let str1 = Array(str1) let str2 = Array(str2) if str1.count == str2.count { return isOneReplaceAway(str1, str2) } else if str1.count - 1 == str2.count { return isOneInsertAway(str1,str2) } else if str2.count - 1 == str1.count { return isOneInsertAway(str2, str1) } else { return false } } /// loop over characters. if difference, bump diff count /// if at the end the count was more than 1, return false.  func isOneReplaceAway(_ str1: [Character], _ str2: [Character]) -\u0026gt; Bool { var diffCount = 0 for i in 0..\u0026lt;str1.count { if str1[i] != str2[i] { diffCount += 1 } } return diffCount \u0026lt;= 1 } /// Order matters. `str1` is always one character longer /// if character is equal, then proceed /// if not equal, then just move index on `str1` and continue comparing... func isOneInsertAway(_ str1: [Character], _ str2: [Character]) -\u0026gt; Bool { var diffCount = 0 for i in 0..\u0026lt;str2.count { if str1[i + diffCount] != str2[i] { diffCount += 1 } } return diffCount \u0026lt;= 1 } It took me a while to understand that the following are interchangeable: \u0026ldquo;OneInsertAway\u0026rdquo; and \u0026ldquo;OneRemoveAway\u0026rdquo;.\n \u0026ldquo;OneInsertAway\u0026rdquo; is about inserting one character to the shorter string \u0026ldquo;OneRemoveAway\u0026rdquo; is about removing one character from the longer string.  Either way works. It\u0026rsquo;s just these difference of approaches or possibilities that puts me off some times.\nTests print(isOneOrZeroEditsAway(\u0026#34;like\u0026#34;, \u0026#34;like\u0026#34;)) // true print(isOneOrZeroEditsAway(\u0026#34;like\u0026#34;, \u0026#34;likes\u0026#34;)) // true print(isOneOrZeroEditsAway(\u0026#34;like\u0026#34;, \u0026#34;life\u0026#34;)) // true print(isOneOrZeroEditsAway(\u0026#34;like\u0026#34;, \u0026#34;lik\u0026#34;)) // true print(isOneOrZeroEditsAway(\u0026#34;like\u0026#34;, \u0026#34;pine\u0026#34;)) // false print(isOneOrZeroEditsAway(\u0026#34;like\u0026#34;, \u0026#34;lion\u0026#34;)) // false Complexity Obviously O(n) where n is the length of the shorter string.\nRelated This question falls under the category of Edit Distance\n","permalink":"https://mfaani.com/posts/interviewing/challenges/check-if-two-strings-are-one-edit-away/","summary":"Question You can edit a string in three different ways: insert, remove or replace a character. Write a function to see if two strings are one or zero edits away.\nExample like, like --\u0026gt; true (zero edits) like, likes --\u0026gt; true (one edit: remove/insert) like, life --\u0026gt; true (one edit, replace) like, lik --\u0026gt; true (one edit: remove/insert) like, pine --\u0026gt; false (two edits) like, lion --\u0026gt; false (two edits) Strategy   For most questions that require you to return true or false, you can reduce the scope of the question by removing noise.","title":"Check if Two Strings Are One Edit Away"},{"content":"This is bit of fast paced intro into Swift Strings.\nASCII, Unicode and the challenges they introduce. ASCII Ages ago, only characters that existed were just a to z, A to Z, and bunch of other English characters. This was problematic. You were limited to only 7 bits i.e. only 2 ^ 7 - 1 = 127 characters. Also non-English characters where not part of ASCII. Literally the name says \u0026lsquo;American Standard Code for Information Interchange\u0026rsquo;. So characters like å or ä were not part of it. Let alone ب or ج or characters of other languages.\nCharacter mapping in ascii let uppdercaseA: Character = \u0026#34;A\u0026#34; print(String(describing: uppdercaseA.asciiValue)) // 65 let lowercaseA: Character = \u0026#34;a\u0026#34; print(String(describing: lowercaseA.asciiValue)) // 97 let variantA: Character = \u0026#34;á\u0026#34; print(String(describing: lowercaseA.asciiValue)) // nil Unicode Then came Unicode. It was roomy. Currently Unicode has 144,697 characters.\n Unlike ASCII where it only supported English, Unicode supports characters in most languages. It has the same concept of mapping in ASCI. Anything that is in asci will have the same value it did in Unicode. This helps backwards compatibility. Has combining logic. Examples:  \u0026quot;a\u0026quot; + \u0026quot; ́\u0026quot; together will create \u0026quot;á\u0026quot;. You\u0026rsquo;d have to write it as such: \u0026quot;a\\u{301}\u0026quot;. \\u{\u0026lt;code point\u0026gt;} 🇺 + 🇸 together will create 🇺🇸 . For more on that see https://en.wikipedia.org/wiki/Regional_indicator_symbol   Similar to how Hex is prefixed with 0x, Unicode is prefixed with U+  U+1F600 (😀 emoji) U+0041 (\u0026ldquo;A\u0026rdquo;)   Unicode has blocks for each language. For more on that see my other post on Why can\u0026rsquo;t you loop over ranges of characters - benefits of keeping all characters of a language grouped together  Unicode is a giant table of code-points (Swift calls scalars) matching to some full characters, some combining accents, and some invisibles.\n(Swift) Character It\u0026rsquo;s a heavily overloaded term. Anything perceivable by us as humans as a single character. a, â, õ, !, ~, , 1 etc. A character is more or less an array (one or more) code points without a (grapheme) break. Examples:\nlet x = \u0026#34;a\u0026#34; let y = \u0026#34;á\u0026#34; let z = \u0026#34;a\\u{301}\u0026#34; func visualize(_ string: String) { print(\u0026#34;string:\u0026#34;,string) print(\u0026#34;used code points:\u0026#34;) for s in string.unicodeScalars { print(s,\u0026#34;:\u0026#34;,s.value) } print(\u0026#34;----\u0026#34;) } visualize(x) visualize(y) visualize(z) Output:\nstring: a used code points: a : 97 // See https://unicode.scarfboy.com/?s=U%2b0061 ---- string: á used code points: á : 225 // See https://unicode.scarfboy.com/?s=U%2b00E1 ---- string: á used code points: a : 97 // See https://unicode.scarfboy.com/?s=U%2b0061 ́ : 769 // See https://unicode.scarfboy.com/?s=U%2b0301 ---- What\u0026rsquo;s interesting is that a\\u{301} produces á. Unicode has logic to combine code points. Much like how Math knows how to combine things. i.e.\nlet y = \u0026#34;á\u0026#34; let z = \u0026#34;a\\u{301}\u0026#34; print(y == z) // true Think of how 5 + 3 is equal to 7 + 1 yet if you compared the two literally then they\u0026rsquo;re obviously not the same.\n Character comparisons occur on the final rendered Character. Not the the literal form of it.\n 👆 is one of the main reasons why Swift Strings are complicated. Like if you\u0026rsquo;re reading\na then its palindrome is just a.\nHowever if you\u0026rsquo;re reading á then it\u0026rsquo;s reverse could be either:\n á (the reverse of y)  ́a (the reverse of z)  i.e. one could argue that the palindrome of á is  ́a i.e. two code points can end up being palindrome with one code point.\nDepending on how the string/character is constructed the character count could be different — i.e. a is 1 character, but á can be either just á or a ́.\nAlso\nOther notes from https://unicode.org/glossary   A grapheme is a logical entity, not visual: it can be composed of one or multiple code points.\n  A \u0026ldquo;glyph\u0026rdquo;, also in the glossary (https://unicode.org/glossary/#glyph) which is typically used to mean \u0026ldquo;the drawn representation of graphemes\u0026rdquo;.\n  Italic and bold don\u0026rsquo;t create a different code point. If they did, then Unicode would have exploded. Basically:\n grapheme + (italic) font -\u0026gt; (italic) glyph grapheme + (bold) font -\u0026gt; (bold) glyph    What should you do in interviews? If all you want is looping then do:\nJust do:\nlet str = \u0026#34;hello world\u0026#34; for s in str { print(type(of:s)) } If you need indexing then without giving away too much time for explanation just convert the string to an array of characters. 99.99% of questions you get asked aren\u0026rsquo;t considerate of previous Unicode complexities.\nlet str = \u0026quot;hello world\u0026quot; let characters = Array(str) print([characters[2]]) // \u0026quot;l\u0026quot; However if you\u0026rsquo;re confident and you\u0026rsquo;re not losing time or you think it works to your advantage then discuss the complexities. It could help you stand out vs. the rest of the pack.\nConverting a string to an array has a time complexity of O(n). Since most algorithmic problems can\u0026rsquo;t be solved better than O(n) then it\u0026rsquo;s totally fine to do so.\nWhat if I don\u0026rsquo;t want/can\u0026rsquo;t use an array of Characters? Try mastering the String - Manipulating indices section.\nA helper method I use is this:\nextension String { func at(_ i: Int) -\u0026gt; Character { return self[self.index(self.startIndex, offsetBy: i)] } func safeAt(_ i: Int) -\u0026gt; Character? { guard i \u0026lt; count else { return nil } return at(i) } } Also it\u0026rsquo;s good to understand that startIndex is the position of the first character in an nonempty string.\nThe endIndex is: A string’s \u0026ldquo;past the end\u0026rdquo; position \u0026ndash; that is, the position one greater than the last valid subscript argument.\nWith an empty string, accessing the startIndex and endIndex will both cause crashes. In an empty string, startIndex is equal to endIndex.\nWhy is endIndex \u0026ldquo;past the end\u0026rdquo; position?! If endIndex were the last valid subscript, then a question would arise as: in an empty collection what should the value of endIndex be? You can\u0026rsquo;t change the logic for calculating the endIndex — only for empty arrays. You need consistency.\n  first and last return optional values because the thing might be empty, so there is no \u0026ldquo;first\u0026rdquo; nor \u0026ldquo;last\u0026rdquo; element or index\n  On the other hand, startIndex is where the collection begins, and whether it’s empty or non-empty, an array always begins at 0. Because of this, accessing an array using startIndex or endIndex can cause out of bounds crashes. Example:\n  extension String { func at(_ i: Int) -\u0026gt; Character { return self[self.index(self.startIndex, offsetBy: i)] // ❌ CRASH: String index is out of bounds ❌ } } let ss: String = \u0026#34;\u0026#34; ss.at(0) This is also partly for ease of comparisons/calculations:\n An array\u0026rsquo;s count is endIndex - startIndex isEmpty is startIndex == endIndex  If endIndex was the last index, you\u0026rsquo;d need to do extra math in there for those calculations.\nThe docs have a nice example of putting the above together to:\nlet name = \u0026#34;Marie Curie\u0026#34; let firstSpace = name.firstIndex(of: \u0026#34; \u0026#34;) ?? name.endIndex let firstName = name[..\u0026lt;firstSpace] print(firstName) // Prints \u0026#34;Marie\u0026#34; Try not to memorize the above. If you understand the \u0026lsquo;why\u0026rsquo; then you\u0026rsquo;ll memorize it naturally.\nAlso see this discussion in the dev forums and the original post by Dijkstra himself\nif empty check To check whether a string is empty, use its isEmpty property instead of comparing the length of one of the views to 0. Unlike with isEmpty, calculating a view’s count property requires iterating through the elements of the string.\nSummary  a has a code point of 97. It\u0026rsquo;s a grapheme by itself.  ́ has a code point of 769. It\u0026rsquo;s a grapheme by itself. Together they form a new grapheme cluster: á. á, á, á are all the same grapheme, but because of the font they\u0026rsquo;re different glyphs. For interviewing most people that are comfortable with Swift, find it easier to convert the string to an array of Characters. Because the focus of the interview isn\u0026rsquo;t on your String skills it\u0026rsquo;s about your interview, algo and DS abilities. Or at least that\u0026rsquo;s what it should be.  References  Also recommend seeing my other post on Why can\u0026rsquo;t you loop over a Range of Swift Characters Stackoverflow - What\u0026rsquo;s the difference between a character, a code point, a glyph and a grapheme? What\u0026rsquo;s the difference between ASCII and Unicode? Quora - What\u0026rsquo;s the difference between a character, a glyph, and a grapheme? Unicode Glossary  ","permalink":"https://mfaani.com/posts/interviewing/string/","summary":"This is bit of fast paced intro into Swift Strings.\nASCII, Unicode and the challenges they introduce. ASCII Ages ago, only characters that existed were just a to z, A to Z, and bunch of other English characters. This was problematic. You were limited to only 7 bits i.e. only 2 ^ 7 - 1 = 127 characters. Also non-English characters where not part of ASCII. Literally the name says \u0026lsquo;American Standard Code for Information Interchange\u0026rsquo;.","title":"Swift Strings for iOS interviewing"},{"content":"The first time I listened to an iOS podcast was yrs ago. I was struggling to grasp basic iOS concepts and podcasts weren\u0026rsquo;t really covering basic technical material. It just didn\u0026rsquo;t seem like the right way of learning about things. Two weeks ago I got invited to Side Project Spotlight Podcast. I was thrilled but also clueless. Had to do some googling, listen to a few episodes and purchase some equipment. Here are the things I learned throughout the process.\nTalking points  Ask for an overview of what\u0026rsquo;s going to be discussed from your host. This is what my host told me:  Be prepare to introduce yourself. Practice it out. Talk about where you live. What you currently do. How you got into Programming. Where you\u0026rsquo;re the most active. But also talk about your non-programming life. Just because the order of a previous episode of the podcast was A then B then C, it doesn\u0026rsquo;t mean your talk will have the same order of sections. Your host can do whatever they want, however they feel. Everything is relaxed. Podcasts to meetings are much like meetups to conferences. Stories and conversations are what\u0026rsquo;s important. Have a few stories prepared. It can be about:  Failures Journeys Leadership Friendships Inspirations First job or hiring experiences Challenge or Success Funny incidents Product Team    For the most part the conversation just flows naturally. Your rehearsing just helps. Don\u0026rsquo;t be too technical. Nor speak in length.\nHow to Talk   Speak slow and clear.\n  Have a professional microphone. AirPods aren\u0026rsquo;t professional microphones 🫠🫠🫠.\n I ended buying a Blue Yeti Nano for $69.99. There were some more premium products, but from the feedback I got, it seemed like a good choice. Reduce any possible noise (heater, fan, kids, tv) in your room. Test it. Watch a Youtube video on the microphone on its correct position/angle. I needed to reduce the microphone gain (the output volume of your microphone) as it was my headphones' output as well. Getting early to the recording is helpful to get feedback on your audio from the host, because they they likely have more experience than you. Also you may not notice that you\u0026rsquo;re using your computer\u0026rsquo;s built-in microphone. A standard way of testing is to rub the microphone with your nail and ask if others can hear it. Often setting the input at the OS level doesn\u0026rsquo;t adjust the input device. Fix is easy. You just need to set input for Zoom. Pro tip: If you\u0026rsquo;re using a mic, then make sure you accompany it with a headphone, otherwise your microphone will echo the the sound it hears. You won\u0026rsquo;t notice this until you get to the meeting where people are talking back and forth I also learned about \u0026lsquo;Pop Filters\u0026rsquo;. For more on that see this video. I ended up not buying one and things just worked fine.    Here\u0026rsquo;s an example of how AirPods compare with a Yeti Nano:\n  AirPods2 Your browser does not support the audio element.  Blue Yeti Nano Your browser does not support the audio element.   It\u0026rsquo;s ok to have a few hiccups in your speaking. But it\u0026rsquo;s good to know if you over-use something too much. Through out my talk I realized I overused the term \u0026ldquo;I feel like\u0026rdquo; 🤦‍♂️. Speak with energy. Ask a friend to ask a few questions for a few minutes. Record that it. Make it a conversation, so you can gauge your energy level easier. Listen to it and see if your energy is good enough. Energy doesn\u0026rsquo;t mean being loud but after hearing the recording, I often felt like I was too shy or my voice was low.  Other tips  You\u0026rsquo;re the guest. But that doesn\u0026rsquo;t mean it\u0026rsquo;s going to be all you talking. Empty your cup, your hosts have things to share as well. Everyone there is to share and learn. Truth to be told, towards the end I was asked about \u0026ldquo;what\u0026rsquo;s next?\u0026rdquo; and paused. I realized I didn\u0026rsquo;t have a good answer. But the insight that Kotaro and Stephen offered was really helpful. Don\u0026rsquo;t interrupt anyone. Nor lose your patience if they go on a streak or pivot just as you were to hit your climax. You can pivot too 😀. Your talk will likely be on Zoom. It makes the conversation more alive and easier for you to read visual cues for when someone is about to talk. And because Zoom is used, then wear nice clothes and have a decent camera background and remain attentive. Don\u0026rsquo;t try to correct your hosts. You may be incorrect yourself. If you really needed then first check if the host is giving room for correction. Like instead of saying \u0026ldquo;That\u0026rsquo;s incorrect / You\u0026rsquo;re wrong\u0026rdquo;. Just go with \u0026ldquo;I thought it was like \u0026hellip;. instead. No?\u0026rdquo; and if they gave you signals that they\u0026rsquo;re willing to hear more, then indulge them with more stuff. If there\u0026rsquo;s anything you don\u0026rsquo;t want talking about then, let your host know of it days before, not just before the conversation. Be candid but also comfortable about it. If something was discussed in the conversation that you didn\u0026rsquo;t like or was just too off-topic, then just continue with the conversation. Often the hosts pivot a bit early in the talk, just to have enough content to select from. It\u0026rsquo;s like a director doing different cuts so they can later choose the best recording. Still if there was something you really didn\u0026rsquo;t want released then just ask your host to remove those sections. I asked for something to be removed. They were 100% onboard. It\u0026rsquo;s ok to go a bit over time, because there will be some editing and shrinkage. So plan ahead for this as well. Others can\u0026rsquo;t see your screen or others may be hearing about you for the first time or have a hard time understanding your accent. I was listening to this Podcast and the person had some (English) accent and I couldn\u0026rsquo;t tell if they were saying \u0026lsquo;Peacock\u0026rsquo; or \u0026lsquo;Peak calk\u0026rsquo;. Turns out they were actually saying PCalc. 🤦‍♂️ e.g. you might need to spell out your Twitter handle or blog name letter by letter. The alternative to that is to mention: \u0026ldquo;Look into the show notes\u0026rdquo;. Hosts are really good for filling this information. But you must also be prepared to help with filling the information.   Philly CocoaHeads + Side Project Spotlight shout out🔊 I would like to officially give thanks to all the Philly CocoaHeads community for supporting me through the years by answering my questions (I\u0026rsquo;m known for asking a lot of questions). Facilitating meetups and inviting me to this wonderful podcast. Special shout out to Stephen Tolton, Kotaro Fujita and Aaron Bilenky whom all gave me this opportunity. They changed my perception of Podcasts. I\u0026rsquo;ve started listening a lot more since then.\nSide Project Spotlight is associated with Philly CocoaHeads. Their podcast is for app builders, documenting the process of producing real apps for the Apple App Store.\nYou can subscribe to their podcast through their different mediums. My episode (aired on May 9th, 2022) can also be accessed from here\n","permalink":"https://mfaani.com/posts/content-creation/how-to-prepare-yourself-as-a-podcast-guest/","summary":"The first time I listened to an iOS podcast was yrs ago. I was struggling to grasp basic iOS concepts and podcasts weren\u0026rsquo;t really covering basic technical material. It just didn\u0026rsquo;t seem like the right way of learning about things. Two weeks ago I got invited to Side Project Spotlight Podcast. I was thrilled but also clueless. Had to do some googling, listen to a few episodes and purchase some equipment.","title":"How to Prepare Yourself as a Podcast Guest?"},{"content":"So I had this need to add a brew package named gh into our build script. I spoke with with the team that handled our agents and asked them to add a new package on the agents.\nI was told that the package has to be included as part of the build script and that build scripts need to be self-contained.\nAt first I didn\u0026rsquo;t fully understand what \u0026lsquo;self-contained\u0026rsquo; means in this context but as I digged more into our Jenkinsfile I learned what it meant. First let\u0026rsquo;s figure out:\nHow is running pod lib lint on my local machine different from running it on an agent? On my local machine if I want to run pod lib lint, I just do it. I don\u0026rsquo;t ever check if CocoaPods is installed or not. I just know that I\u0026rsquo;ve installed and use it.\nHowever you can\u0026rsquo;t assume the same on a agent. An agent isn\u0026rsquo;t owned by you. And you have no control over what command line tools it has. You need CocoaPods installed.\n To have CocoaPods you need (recommended) bundler. To have bundler you need ruby. To have ruby you need (recommended) rvm.  Our CI team installs rvm on all agents, as that\u0026rsquo;s not a trivial process. The rest (installing ruby, bundler, CocoaPods) is to be owned by the build script.\nHmmm. OK. So should I install ruby, bundler, cocoapods on every build? No. You just install it if it wasn\u0026rsquo;t installed before. Example:\n1 2 3 4  rvm use ruby-2.5.1 || rvm install ruby-2.5.1 gem install bundler -v \u0026#34;2.3.3\u0026#34; bundle install pod lib lint   Line 1 only installs ruby if it\u0026rsquo;s not installed.\nLines 2 \u0026amp; 3 only install packages once. The bundle and gem commands have have logic internally to exit early if the dependencies are installed already.\nLine 4 can then safely execute.\nWhat should I do if I need a brew dependency installed?  On all agents: Have brew installed. This needs to be done only once. In your build script do: gh --version || brew install gh.  By having gh --version || brew install gh in your build script, you make your scripts self-contained i.e. your build script doesn\u0026rsquo;t depend on the gh formula being installed.\nAnd what\u0026rsquo;s the cover image about? A self-contained RV is an RV that has a bathroom and tank system for holding water. It doesn\u0026rsquo;t need any outside sources to operate.\nBuild scripts need to be just like that. 😀\nThey should be able to work without knowing what dependencies exist on the agent.\nAnything else? 💡 Yes. A good RV won\u0026rsquo;t leave any trash behind itself. Similarly build scripts must clean up after themselves. Usually that\u0026rsquo;s done with just deleting after your checked in the branch. However things can quickly get tricky if you make changes outside of your repo\u0026rsquo;s directory.\nExample: if you create a new keychain with the name of \u0026ldquo;FooProject\u0026rdquo; and don\u0026rsquo;t delete it once your build script finishes, then if the next build script attempts to create a keychain with the same name then it will fail.\nCreating same Keychain twice $ security create-keychain -p kevin123 FooProject.keychain-db # (ran by build job 1... no error) $ security create-keychain -p kevin123 FooProject.keychain-db # (ran by build job 2) security: SecKeychainCreate FooProject.keychain-db: A keychain with the same name already exists. Looking into the logs makes it easy for you triage. But if it\u0026rsquo;s something that leads to a visual error or a visual password prompt then it\u0026rsquo;s HARD to triage. You may not have access to the build machine and would need to pair with the team that owns the machines and SSH into it and triage things together. Example such a prompt would pause the build while leaving no trace on your machine. Only way to know it happened is to SSH into the agent.\n You have two ways to resolve this:\n At the end of your build script, clean up even things you did outside your checked out directory. At the beginning of your build script, make sure things are clean i.e. delete any keychain that could cause conflicts for your build script.  The first solution works if the build agents only use a single build script. If multiple teams (and more in the future) are using the same agents, then it becomes hard to track all their build scripts / GitHub Actions / Fastlane, etc and see if they do proper clean up.\nThe second solution works better. Because it does a base clean up within itself — before starting. Just remember to log and report back to the owners of the faulty build scripts.\nConclusion It\u0026rsquo;s ok to expect rvm, brew, Xcode to be installed. But for any other dependency or package, you should check for its presence. If not available then install it during your build script.\nIf you touched anything outside your working directory, then it\u0026rsquo;s best to clean up afterwards. You also can\u0026rsquo;t assume other build scripts have done proper clean up, so you may need to do a base clean up before doing anything in your script.\n","permalink":"https://mfaani.com/posts/what-is-a-self-contained-build-script/","summary":"So I had this need to add a brew package named gh into our build script. I spoke with with the team that handled our agents and asked them to add a new package on the agents.\nI was told that the package has to be included as part of the build script and that build scripts need to be self-contained.\nAt first I didn\u0026rsquo;t fully understand what \u0026lsquo;self-contained\u0026rsquo; means in this context but as I digged more into our Jenkinsfile I learned what it meant.","title":"What is a Self Contained Build Script?"},{"content":"I googled a bit for \u0026ldquo;CurrentValueSubject Example\u0026rdquo;. Surprisingly I wasn\u0026rsquo;t able to find a simple answer. So I created a few examples:\nBasic import UIKit import Combine class ViewController: UIViewController { var name = CurrentValueSubject\u0026lt;String, Never\u0026gt;(\u0026#34;Jason\u0026#34;) override func viewDidLoad() { super.viewDidLoad() setup() } func setup() { let _ = name.sink { value in print(value) // Jason, Jason Bourne } name.send(\u0026#34;Jason Bourne\u0026#34;) } } Basically every time you call send on a publisher, the subscriber gets a callback.\nDebugging Notice the print(\u0026quot;debug - \u0026quot;). It just helps us see the steps.\nimport UIKit import Combine class ViewController: UIViewController { var name = CurrentValueSubject\u0026lt;String, Never\u0026gt;(\u0026#34;Jason\u0026#34;) override func viewDidLoad() { super.viewDidLoad() setup() } func setup() { let sub = name.print(\u0026#34;debug - \u0026#34;).sink { value in print(value) // Jason, Jason Bourne } name.send(\u0026#34;Jason Bourne\u0026#34;) } } Output:\ndebug - : receive subscription: (CurrentValueSubject) # It has a subscription debug - : request unlimited # The subscriber wants to know of every value the publisher emits. It doesn't want to stop after a certain number. debug - : receive value: (Jason) # A value of 'Jason' was received' debug - : receive value: (Jason Bourne) # A value of 'Jason Bourne' was received' debug - : receive cancel # The subscription was terminated. Deinitialization gotcha - no retain import UIKit import Combine class ViewController: UIViewController { var name = CurrentValueSubject\u0026lt;String, Never\u0026gt;(\u0026#34;Jason\u0026#34;) override func viewDidLoad() { super.viewDidLoad() setup() } func setup() { let _ = name.sink { value in print(value) // Jason. WILL NOT necessarily be called for \u0026#39;Jason Bourne\u0026#39; } name.send(\u0026#34;Jason Bourne\u0026#34;) } } Here the subscription is valid only until the end of the setup function. However when you use _ you\u0026rsquo;re telling the compiler that I care less and it may be deinitialized immediately.\nGenerally the only way to guarantee your subscriptions stay alive is to reference them from a scope that outlives the time you want to receive subscriptions.\nThe docs on sink also say:\n This method creates the subscriber and immediately requests an unlimited number of values, prior to returning the subscriber. The return value should be held, otherwise the stream will be canceled.\n If you try using the Debugging technique, you\u0026rsquo;ll realize that the subscription is canceled before name.send(\u0026quot;Jason Bourne\u0026quot;) is reached.\nDeinitialization gotcha - scope exit import UIKit import Combine class ViewController: UIViewController { var name = CurrentValueSubject\u0026lt;String, Never\u0026gt;(\u0026#34;Jason\u0026#34;) override func viewDidLoad() { super.viewDidLoad() setup() DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in self?.name.send(\u0026#34;Delayed name\u0026#34;) } } func setup() { let sub = name.sink { value in print(value) // Jason, Jason Bourne. Will NOT be called for \u0026#39;Delayed name\u0026#39;  } name.send(\u0026#34;Jason Bourne\u0026#34;) } } \u0026lsquo;Delayed name\u0026rsquo; will not get printed, because the subscription that happened after setup() is cancelled once sub goes out of scope.\nSolution class ViewController: UIViewController { var name = CurrentValueSubject\u0026lt;String, Never\u0026gt;(\u0026#34;Jason\u0026#34;) override func viewDidLoad() { super.viewDidLoad() setup() DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in self?.name.send(\u0026#34;Delayed name\u0026#34;) } } var sub: AnyCancellable? func setup() { sub = name.sink { value in print(value) // Jason, Jason Bourne, Delayed name } name.send(\u0026#34;Jason Bourne\u0026#34;) } } sub = name.sink {...} is a subscription. It really needs to return a type. It just happens to do that so:\n You get a reference to it. Therefore have some level of control over how/when you can cancel the subscription. Because you gave it a name. i.e. you didn\u0026rsquo;t just do name.sink {...} or _ = name.sink{...} then the subscription will remain seeking events until the subscription goes out of scope. For the above example that\u0026rsquo;s when sub property goes out of scope i.e. when the viewController deinitilizes.  But doesn\u0026rsquo;t the subscription remain after the object is deallocated? Good question. Docs on AnyCancellable say:\n An AnyCancellable instance automatically calls cancel() when deinitialized.\n tldr within its deinit it will call cancel.\nAccessing value \u0026amp; Updating value var name = CurrentValueSubject\u0026lt;String, Never\u0026gt;(\u0026#34;Jason\u0026#34;) print(name) // 😐 Combine.CurrentValueSubject\u0026lt;Swift.String, Swift.Never\u0026gt; print(name.value) // Jason name = \u0026#34;David\u0026#34; // ❌ ERROR: Cannot assign value of type \u0026#39;String\u0026#39; to type \u0026#39;CurrentValueSubject\u0026lt;String, Never\u0026gt;\u0026#39; name.send(\u0026#34;David\u0026#34;) ✅ It\u0026rsquo;s also named as current valuesubject because:\n As demonstrated above, you can always access its current value using .value. Upon subscription, the subscriber will get a callback for the current value of the publisher.  You can think of CurrentSubjectValue as APublisherThatItCurrentValueIsAccessibleAndWillEmitItsValue. For more on that see here\nAlternate solution import UIKit import Combine class ViewController: UIViewController { var name = CurrentValueSubject\u0026lt;String, Never\u0026gt;(\u0026#34;Jason\u0026#34;) var subscriptions: Set\u0026lt;AnyCancellable\u0026gt; = [] override func viewDidLoad() { super.viewDidLoad() setup() } func setup() { name.sink { value in print(value) // Jason, Jason Bourne }.store(in: \u0026amp;subscriptions) name.send(\u0026#34;Jason Bourne\u0026#34;) } } Remember we said that we should retain the return value of a subscription?\nThere\u0026rsquo;s a cleaner way other than doing let sub = name.sink {...}\nYou could just store the subscription using store(in:)\nThis way you don\u0026rsquo;t create multiple (unwanted) objects just so you could retain the subscription. You just store one variable and then dump all the subscriptions into it.\nCan you explain what var subscriptions: Set\u0026lt;AnyCancellable\u0026gt; = [] is again? It might helpful if we named the variable differently. Alternate names are:\n// just to retain all our subscriptions var subscriptionRetainer: Set\u0026lt;AnyCancellable\u0026gt; = [] // a group of cancelable items. Remember every subscription is cancellable. That's why often the names are used interchangeably var cancellables: Set\u0026lt;AnyCancellable\u0026gt; = [] // a place to just dump my subscriptions and not care too much. var subscriptionDumpster: Set\u0026lt;AnyCancellable\u0026gt; = [] // a bag (of subscriptions) that is soon to be disposed. This is how RxSwift used to name things. var disposableBag: Set\u0026lt;AnyCancellable\u0026gt; = [] // self-explanatory var storageForAllOurSubscriptionsSoWeDon'tCreatVariablesForThem: Set\u0026lt;AnyCancellable\u0026gt; = [] All the names above are for the same purpose. It\u0026rsquo;s just that everyone names it differently.\nBeyond that, you could use it for grouping subscriptions. Example:\nvar primaryUserSubscriptions: Set\u0026lt;AnyCancellable\u0026gt; = [] var secondaryUserSubscriptions: Set\u0026lt;AnyCancellable\u0026gt; = [] Why is the function to subscribe named sink? So a publisher is a stream of data. Think more of a water flow analogy. It collects (and combines) data from streams.\nThe map, filter functions, consume the stream but then also produce a stream. A sink functions consumes the stream without producing a newer stream. Because it\u0026rsquo;s terminal in its nature and with a water flow analogy, the term sink somewhat makes sense.\nWhat\u0026rsquo;s with the cover image of the post? It\u0026rsquo;s an image of disposable bags. Just like how some name the set of their subscriptions. 😀\nOK. I get what Set\u0026lt;AnyCancellable\u0026gt; does. But why not just use the normal subscription I had before? It\u0026rsquo;s just a convenience. That is:\n All subscriptions get to live as long as the set is living. You don’t have to create variables just so you can increase the life-cycle of the subscription.  All that said, often a single value can still be useful if you need to shorten the lifetime. Example if you want to only subscribe after viewWillAppear and cancel after viewWillDisAppear\nDoes Set\u0026lt;AnyCancellable\u0026gt; help avoid memory leaking? The concept of disposeBag / having a Setvariable, is not about leaking memory i.e. it’s not about ending subscriptions when an object goes out of memory. That will happen automatically.\nThe main purpose is to retain the subscription until the object is in memory (or until you call cancel yourself) Because if you don’t store your subscriptions (or use a disposable bag), your subscriptions will go out of memory right away as shown earlier. So no leaking will happen.\nAnything else about Set\u0026lt;AnyCancellable\u0026gt;? You almost always want it to be a private variable. It really has no purpose outside its current class.\nSummary  Use send to update values. Use print() like name.print(\u0026quot;a prefix\u0026quot;).sink{...} to see what\u0026rsquo;s happening under the hood. Your subscription needs to be retained, otherwise your subscriptions would get cancelled either immediately or upon exiting the current scope. A subscription returns an AnyCancelleable. The reason for that is to give you control over the scope/duration of the subscription. It\u0026rsquo;s not about leaking memory. AnyCancellable automatically calls cancel when deinitialized. Set\u0026lt;AnyCancellable\u0026gt; offers a nicer API that helps reduce clutter in your code. Set\u0026lt;AnyCancellable\u0026gt; is not the solution for every kind of subscription you have. Engineers name their Set\u0026lt;AnyCancellable\u0026gt; different things. Yet the purpose of it identical across all engineers. Almost all the time you want your Set\u0026lt;AnyCancellable\u0026gt; to be a private variable.  ","permalink":"https://mfaani.com/posts/currentvaluesubject-example/","summary":"I googled a bit for \u0026ldquo;CurrentValueSubject Example\u0026rdquo;. Surprisingly I wasn\u0026rsquo;t able to find a simple answer. So I created a few examples:\nBasic import UIKit import Combine class ViewController: UIViewController { var name = CurrentValueSubject\u0026lt;String, Never\u0026gt;(\u0026#34;Jason\u0026#34;) override func viewDidLoad() { super.viewDidLoad() setup() } func setup() { let _ = name.sink { value in print(value) // Jason, Jason Bourne } name.send(\u0026#34;Jason Bourne\u0026#34;) } } Basically every time you call send on a publisher, the subscriber gets a callback.","title":"CurrentValueSubject Example"},{"content":"I\u0026rsquo;ve done this so many times, but every time I do it with hesitance. It\u0026rsquo;s usually because I\u0026rsquo;m not certain I\u0026rsquo;m finding the right commit or if I\u0026rsquo;m applying it the right way. These steps explain the process and how to validate that you\u0026rsquo;re doing thins the right way.\n Find out the library version that your release is using. Check out that version of your library. Create a new branch off that version. Find the SHA of the commits you want to cherry pick. Cherry pick them in and validate you\u0026rsquo;ve cherr-picked correctly. Make a new release/tag. Go back to your main app. Check out the release branch again. Create a new release version Update your Podfile (or whatever it is that you\u0026rsquo;re using) to pull the updated version.  All the steps together\n# 1.1 cd path/to/mainProject # 1.2 git checkout release/22.0 # You see that your `Podfile.lock` is using the `1.32.0` version of you library # 2.1 cd path/to/library # 2.2 git checkout 1.32.0 # 3 git checkout -b release/1.32.1 # (IMPORTANT. Avoid creating a branch named `1.32.1` because you git won't know if you want to checkout the branch or tag `1.32.1`) # 4.1 git checkout main # 4.2 git log --oneline -20 # copy the SHA of the commit. I only added `--oneline` because I wanted it to be in one line. `-20` means last 20 commits. # 5.1 (Optional) git show \u0026lt;SHA of the commit I wanna cherry pick\u0026gt; # If I wanted to be sure that I'm cherry picking things correct, then I just do and I can see what exactly I'm cherry picking. I highly recommend doing this. # 5.2 git cherry-pick aec4e0f9; git cherry-pick d963d6bc # (Cherry-picking two commits) # 5.3 (Optional) git diff 1.32.0 # validate all the changes version your previous library release. # 6 git tag 1.32.1 # 7 cd path/to/mainProject; git checkout release/22.0; git checkout -b release/22.0.1 # 8 Update your `Podfile` or whatever dependency management system you're using to use `1.32.1` of your library and pull in the changes with `pod install`. ","permalink":"https://mfaani.com/posts/how-to-cherry-pick-a-library-fix/","summary":"I\u0026rsquo;ve done this so many times, but every time I do it with hesitance. It\u0026rsquo;s usually because I\u0026rsquo;m not certain I\u0026rsquo;m finding the right commit or if I\u0026rsquo;m applying it the right way. These steps explain the process and how to validate that you\u0026rsquo;re doing thins the right way.\n Find out the library version that your release is using. Check out that version of your library. Create a new branch off that version.","title":"How to cherry-pick a library fix?"},{"content":"TIL I finally learned when to use unowned as opposed to using weak.\nDifferences  Under the hood: unowned is essentially a force-unwrap of a weak capture, with all that it entails. Because of this using it slightly more dangerous. Crash danger: Accessing an unowned reference while it\u0026rsquo;s nil will cause a crash. Compilation error: Every weak reference, must be an optional property. Otherwise you\u0026rsquo;ll get a compilation error:  weak var delegate: DataEntryDelegate = DataHandler() // ERROR: \u0026#39;weak\u0026#39; variable should have optional type \u0026#39;DataEntryDelegate?\u0026#39;  Call site: Unlike weak references, it\u0026rsquo;s not mandatory for unowned references to be an optional. This makes the call site cleaner i.e. you don’t have to do the optional unwrapping dance.  Similarities  Both don\u0026rsquo;t keep a strong hold on the instance it refers to.  Don\u0026rsquo;t Use unowned for async/network operations. Because the object may become nil and then accessing it would crash!\nDo Only use unowned when you’re certain that something can never be nil and you just want cleaner looking code. e.g. you need to break the reference cycle between a childVC \u0026amp; ParentVC. Obviously a childVC can’t be on the stack if its parent is nil. So that’s a good example.\nNote: You could use weak, which then you\u0026rsquo;d have to do optional unwrapping. However it\u0026rsquo;s just not as clean as using unowned\u0026hellip;\nDocs The docs say:\n In contrast, use an unowned reference when the other instance has the same lifetime or a longer lifetime\n  Unlike a weak reference, an unowned reference is expected to always have a value. As a result, marking a value as unowned doesn’t make it optional, and ARC never sets an unowned reference’s value to nil.\n Example (from Apple): class Customer { let name: String var card: CreditCard? init(name: String) { self.name = name } deinit { print(\u0026#34;\\(name)is being deinitialized\u0026#34;) } } class CreditCard { let number: UInt64 unowned let customer: Customer init(number: UInt64, customer: Customer) { self.number = number` self.customer = customer } deinit { print(\u0026#34;Card #\\(number)is being deinitialized\u0026#34;) } }  Because a credit card will always have a customer, you define its customer property as an unowned reference, to avoid a strong reference cycle.\n How does the crash look like? class ViewController: UIViewController { var original: UIViewController? unowned var unowned: UIViewController = UIViewController() // doing this just because we need to initialize all non-optional properties... override func viewDidLoad() { super.viewDidLoad() original = UIViewController() unowned = original! original = nil print(unowned) // Thread 1: signal SIGABRT --- Fatal error: Attempted to read an unowned reference but the object was already deallocated print(original!) // Fatal error: Unexpectedly found nil while unwrapping an Optional value } } A crash for an unowned reference that\u0026rsquo;s been deallocated is:\n signal SIGABRT \u0026mdash; Fatal error: Attempted to read an unowned reference but the object was already deallocated\n Why is it named unowned? Naming it \u0026ldquo;unowned\u0026rdquo; clearly communicates that the reference doesn\u0026rsquo;t contribute to ownership or memory management.\nIt\u0026rsquo;s meant to emphasize its passive role in memory management and the importance of carefully ensuring its validity within the expected lifespan relationship. It\u0026rsquo;s a clear and intentional choice that conveys both the benefit and the potential risk associated with this type of reference.\nThe naming strategy aligns with several other syntax that Apple has for \u0026lsquo;undefined\u0026rsquo;, \u0026lsquo;unreferenced\u0026rsquo;, \u0026lsquo;unsafe\u0026rsquo;, \u0026lsquo;UnsafePointer\u0026rsquo;\nReferences:  https://stackoverflow.com/questions/54299046/what-means-unowned-self-and-what-are-the-benefits/54313373#54313373 https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html  ","permalink":"https://mfaani.com/posts/whats-the-difference-between-unowned-and-weak-references/","summary":"TIL I finally learned when to use unowned as opposed to using weak.\nDifferences  Under the hood: unowned is essentially a force-unwrap of a weak capture, with all that it entails. Because of this using it slightly more dangerous. Crash danger: Accessing an unowned reference while it\u0026rsquo;s nil will cause a crash. Compilation error: Every weak reference, must be an optional property. Otherwise you\u0026rsquo;ll get a compilation error:  weak var delegate: DataEntryDelegate = DataHandler() // ERROR: \u0026#39;weak\u0026#39; variable should have optional type \u0026#39;DataEntryDelegate?","title":"Whats the Difference Between Unowned and Weak References?"},{"content":"I know this blog is mainly an engineering blog, but this is something I struggled to learn and thought blogging my findings would be helpful for my friends who want to learn about their investments or evaluate and compare offers.\nThe following are a result of my findings after an hour long phone call with Fidelity representative. I opened the chat section in my browser and asked to speak with someone. They gave me a phone number. And they\u0026rsquo;re available Monday - Sunday til midnight EST. I was waiting on the line for about 7-8 minutes. I had an hour long great conversation + along with some follow up questions with some friends. After 3 yrs of being an employee I finally got answers. Please note I made some changes to the actual numbers. The screenshots are just for demonstration purposes.\nSo at my company, I get:\n Restricted Stock options (RSU or RS) Stock option Plan (SOP) 401k  But the \u0026lsquo;account list\u0026rsquo; on the left is slightly vague. It looks like this:  Fidelity Account List. The list remains there for some screens.   I was never able to tell how much I actually currently have if I was to leave the company today and how much of it is unvested and will be lost if I leave.\n Quick breakdown - Individual: All your vested or bought stocks - vested (by RSUs) - bought (by ESPP) - Retirement account - Stocks that are granted but not vested yet. - SOP - ESPP - RSU The Individual sections/accounts are **already** vested and taxed. The Retirement account is tax-deferred. It's all yours i.e. no vesting, but you'll end up paying taxes when you cash them. If you cash them before 65 then you'll also pay a penalty! The stocks section is the portion of your stocks/options that isn't vested. As it's only been granted. RSU Vesting schedule Note: Stocks are granted at their designated time. Then they need to get Distributed and vested. Once they get granted, your distribution time table depending on your company will be like:\n 15% first year distributed. 15% second year distributed. 15% third year distributed. 15% forth year distributed. 40% fifth year distributed.  or\n 25% first year distributed. 25% second year distributed. 25% third year distributed. 25% forth year distributed.  Either on the same day of distribution or 3 days later, the distributed stocks will get finally vested. For the most part distributed and vested are 100% the same day.\nTo see the distribution plan/table, follow the arrows sections:\n  Go to this section to see what your distribution table  Sample distribution:\nTo see in this view, make sure you\u0026rsquo;ve selected In Table Format.\n  RSU in table format - Helps you see how far are you in the vesting of each year\u0026#39;s stock that was granted to you before. Anything Distributed is vested.  If you want to see the your the timeline of the vesting of a specific grant, then from the above screen:\n Select the \u0026lsquo;Account \u0026amp; Info\u0026rsquo; dropdown Then select the \u0026lsquo;Transaction history\u0026rsquo; dropdown.  You\u0026rsquo;ll get landed to such a page:\nAdjust the \u0026lsquo;Transaction Date\u0026rsquo; and \u0026lsquo;Grant Date\u0026rsquo; if needed and hit Apply.\n  With regards to a specific grant, lists all the vestings that has happened so far.  Why is my stock value far less than my actual RSUs? Because you pay income taxes when they get vested. And whenever you sell the vested stocks then you get taxed for capital gains (profit or loss) between the sell price and vesting price.\nExample:\n You\u0026rsquo;re granted a single stock at $200 at 2020. It gets vested at $400 at 2021. You sell it for $500 at 2025.  For tax purposes, the grant price / date isn\u0026rsquo;t important. The vesting date is.\n In 2021, you\u0026rsquo;ll have $400 increase as taxable income. In 2025, you\u0026rsquo;ll have $100 increase as taxable capital gains. ($500 - $400 = $100)   If you sold your stocks less than a year after it had vested then whatever profit you make is taxed based in income tax.\nIncome tax rates are higher than capital gains tax rates.\n Do I have to file taxes for each year for my RSUs?  Vesting Stocks: Whenever it vests, very likely it\u0026rsquo;s already included in your W2 and you get taxed as income tax. So as long as you process your W2 then it will all get processed. Cashing Stocks: If you\u0026rsquo;re selling then you have to pay capital gains tax. That\u0026rsquo;s no longer reflected in your W2. You should be getting 1099-B from your brokerage instead. Need to use that when you\u0026rsquo;re filing taxes.  For taxes about options. See here\nShould I sell my stocks now? So if you sell your stocks today, then whatever profit you have will be considered in this year\u0026rsquo;s taxes. Typically when you\u0026rsquo;re working you already have like ~$100,000 as your salary, and if you sell your stocks you\u0026rsquo;re just adding more to your income. More income might get into a higher tax bracket.\nBut if you don\u0026rsquo;t cash on it until you\u0026rsquo;re retired, then you\u0026rsquo;re selling it in a year where your income is ~$0 (because you don\u0026rsquo;t have any income salary). So your tax bracket is lower.\nHow do I change my ESPP contribution rate? For my company we\u0026rsquo;re only allowed to do that in the first two weeks of every quarter. During a quarter we can only set it back to %0 i.e. for my account I wasn\u0026rsquo;t able to reduce it from %10 to %5.\nHow to access it: Portfolio \u0026raquo; Stock Plans \u0026raquo; ESPP Contribution Balance \u0026raquo; Change Contribution Rate\nI\u0026rsquo;m not sure why but it has two (current and future) offerings. I believe those are for my current quarter and upcoming i.e. I wasn\u0026rsquo;t given an option to change it for current and all upcoming offerings.\nWhat\u0026rsquo;s a Stock Option? From here:\n Companies can grant them to employees, contractors, consultants and investors. These options, which are contracts, give an employee the right to buy, or exercise, a set number of shares of the company stock at a preset price, also known as the grant price.\n Options aren\u0026rsquo;t actual stocks when they vest, they\u0026rsquo;re not worth anything. They\u0026rsquo;re an option to buy stock at a specific (hopefully lower) price than the stock currently trades at. If the current price is lesser than the option price then you won\u0026rsquo;t exercise it at all.\nPre-IPO options are not super valuable because you can\u0026rsquo;t sell the stock (not 100% true but mostly true) but the idea is that post-IPO, now you can buy all this stock at $6 that\u0026rsquo;s worth $20 or whatever. So you can buy for 6 and sell for 20.\nBut that\u0026rsquo;s a big capital gains event, you\u0026rsquo;re making 20-6 $ per stock, and you\u0026rsquo;ll owe that at the end of the year.\nMost brokerages have a \u0026ldquo;buy and sell immediately\u0026rdquo; thing, with a \u0026ldquo;sell for cover\u0026rdquo; option that means that you don\u0026rsquo;t actually have to put in the $6, they\u0026rsquo;ll lend you the $6 for 10 milliseconds, so you\u0026rsquo;ll just get $20-$6 per share in cash\nDoes the Options selling get reported in my W2? No. You have report that yourself. Because your company isn\u0026rsquo;t aware of your Option trading/sales. Only your brokerage knows of that.\nHow much time do I have to exercise my SOPs? Typically it\u0026rsquo;s good for 10 yrs after the grant date while you\u0026rsquo;re at the company.\nWhen you leave it\u0026rsquo;s 90 days. But companies are different. Also every individual\u0026rsquo;s contract is different. So ask your HR department about how much time you have to exercise it after you\u0026rsquo;ve left the company.\nYou\u0026rsquo;d usually exercise it when your stock is doing good.\nIs there a way for me to see how much I get if I exercise my SOPs? Go to your Stock option Plan section and open the exercise calculator:\n  From the drop down select exercise calculator  Then put in the number of options you want to exercise along with the current stock price:\n  Add the number of stocks and current stock price and hit Estimate (or update)  So if the Stock options were granted when stock price was $42.52 and now the current prices are $85.00 then if I exercise 500 options I\u0026rsquo;ll end up with 150 stocks. If I change the current price to $43.00 then I\u0026rsquo;ll end up with only 2 shares.\nTo be honest, I just know how to use calculator. I don\u0026rsquo;t know why it\u0026rsquo;s calculated this way\u0026hellip;\nTax Brackets 2021 Income tax From nerd wallet\n  This is for married filling jointly. For others see the link above  Capital Gain  Note Tax brackets may change every year/president.\nWhat\u0026rsquo;s the difference between capital gain taxes and income taxes?  Income tax is paid on earnings from employment, interest, dividends, royalties, or self-employment, whether it’s in the form of services, money, or property. Capital gains tax is paid on income that derives from the sale or exchange of an asset, such as a stock or property that’s categorized as a capital asset. For more see InvestoPedia\n The rate for capital gain tax rate is usually less that income tax rate.\nIf you hold the stock for less than one year, your gain will be short term, and you\u0026rsquo;ll owe ordinary income tax on it If you hold the stock for one year or more, your gain will be long term, meaning you\u0026rsquo;ll pay tax at the more favorable capital gains rate. For more see TurboTax\n(Future Question) What happens if I die? What should I do to make sure everything will get passed along correctly? I\u0026rsquo;ll ask and update the post when I find out.\nSummary The Fidelity gives you too many ways to see things. It gets confusing. But if you learn of this structure mentioned above and get to know to use the table format, then things will become easier.\n","permalink":"https://mfaani.com/posts/how-fidelity-stocks-work/","summary":"I know this blog is mainly an engineering blog, but this is something I struggled to learn and thought blogging my findings would be helpful for my friends who want to learn about their investments or evaluate and compare offers.\nThe following are a result of my findings after an hour long phone call with Fidelity representative. I opened the chat section in my browser and asked to speak with someone.","title":"How (Fidelity) Stocks Work?"},{"content":"1 - The sync closure func processTwoNumbers(_ num1: Int, _ num2: Int, handler: (Int, Int) -\u0026gt; ()) { handler(num1, num2) } processTwoNumbers(10, 22, handler: { num1, num2 in print(num1 + num2) }) In the above, the handler is called in a sync matter. Same as below\n(1...100).forEach { val in print(val) } print(101) Uniqueness: The block passed in is executed immediately at the call site.\n2 - The async closure — block isn\u0026rsquo;t stored. enum Result { case success(Data?) case failure(Error?) } func startFlow(handler: @escaping (Result) -\u0026gt; ()) { let url = URL(string: \u0026#34;https://i.imgur.com/7oZTmIC.jpeg\u0026#34;)! let task = URLSession.shared.dataTask(with: url) { data, response, error in if error == nil { handler(.success(data)) } else { handler(.failure(error)) } } task.resume() } Uniqueness: handler block is called by the startFlow function. Execution of the block is within the function itself. It gets called after some asynchronous operation.\n3 - The async closure — block is stored. So you\u0026rsquo;d have the following definitions:\nenum Result { case success case error } class FlowManager { var _exitHandler: ((Result) -\u0026gt; ())? func startFlow(exitHandler: @escaping o(Result) -\u0026gt; ()) { _exitHandler = exitHandler } func handleFlowExit(result: Result) { _exitHandler?(result) } } 💡Uniqueness: exitHandler is not called by the startFlow function! exitHandler is called later. You don\u0026rsquo;t know really when. You\u0026rsquo;d have to look where it gets stored. And then where that stored block gets executed.\nThe usage would be something like:\nclass Controller { var flowManager = FlowManager() func start() { flowManager.startFlow { [weak self] result in switch result { case .success: self?.showSuccessScreen() // Line A case .error: self?.showFailureScreen // Line B } } } func showSuccessScreen() { } func showFailureScreen() { } } Ask yourself: When do Lines A and B get executed? Obviously, unlike the first example, they do not get executed before start() is finished.\nMore importantly, unlike the second example, the execution of the block passed in, is not controlled by the startFlow function. Instead the block is executed whenever handleFlowExit is called. But that\u0026rsquo;s not clear from the call site in Controller. One would only know of that if they looked into the implementation of FlowManager.startFlow().\nWhy would you want to do something like this? When you want to tie blocks of code together — even though they\u0026rsquo;re not chained after another i.e. they\u0026rsquo;re just semantically related, but one block will execute now, the other depending on UX / business logic will execute later.\nA real world example would be:\nYou start a flow. Your flow has five view controllers. At then end of the fifth view controller, you pop the viewcontroller and come all the way back to your root viewcontroller. Your handleFlowExit is called then, while its block is defined within the startFlow function.\nCould the code be written differently? Sure. It\u0026rsquo;s just that the event is multiple classes away from the class that can actually handle the event and you didn\u0026rsquo;t architecture things to be in a delegate based way.\nSo why did I mention all this? To say that the following signature:\nfunc startFlow(handler: @escaping (Result) -\u0026gt; ()) { can operate differently between the second and third example. Having good documentation becomes key for differentiating and clarifying when the callback is called by the function itself vs another function/event.\nSo how is this related to background tasks? Well it just so happens that background tasks are of the third way.\nThe expirationHandler is NOT executed by beginBackgroundTask(expirationHandler:) function.\nRather the block gets stored. And executed later — if the timeout was near and you haven\u0026rsquo;t called endBackgroundTask(_:) yet. I took my many many looks at:\nvar backgroundTaskID: UIBackgroundTaskIdentifier = .invalid backgroundTaskID = UIApplication.shared.beginBackgroundTask() { UIApplication.shared.endBackgroundTask(backgroundTaskID) } And always thought UIApplication.shared.beginBackgroundTask() is getting executed similar to our second example i.e. its block is about to get executed and the delay is just do due general async mechanics. That\u0026rsquo;s incorrect. It\u0026rsquo;s better read as such:\nUIApplication.shared.tellOSIhaveATask(expirationHandler_whichGetsStored_andToBeCalledIfNeeded_inAbout30Seconds: { immediatelyStopTask() UIApplication.shared.endBackgroundTask(backgroundTaskID) }) tldr Think of expirationHandler as a parameter that gets passed around. Its behavior is semantically different from most other completionHandlers iOS has\nUIApplication internal implementation From my understanding the internal architecture of UIApplication is something like this:\nimport Foundation typealias UIBackgroundTaskIdentifier = String class UIApplication { var _expirationHandler: (() -\u0026gt; Void)? var currentBackgroundTaskID: String? func beginBackgroundTask(expirationHandler handler: (() -\u0026gt; Void)? = nil) -\u0026gt; UIBackgroundTaskIdentifier { _expirationHandler = handler scheduleExpirationCheck() return UUID().uuidString } // After 25 seconds, checks to see if task is expired. Otherwise it will call the expirationHandler.  // The `expirationHandler` should call `endBackgroundTask`. Nonetheless due to programming mistake it\u0026#39;s possible that it doesn\u0026#39;t get called.  func scheduleExpirationCheck() { Timer.scheduledTimer(withTimeInterval: 25, repeats: false) { [weak self] _ in guard let self = self else { return } if self.currentBackgroundTaskID == \u0026#34;invalid\u0026#34; { // task has already ended. We don\u0026#39;t need to end it. } else if let currentBackgroundTaskID = self.currentBackgroundTaskID { // task took too long. Let\u0026#39;s call its expirationHandler before we suspend the app self._expirationHandler?() self.terminateAppIfNeeded() } } } // Marks a taskIdentifier as invalid.  func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) { currentBackgroundTaskID = \u0026#34;invalid\u0026#34; } // Is called 5 seconds after the expirationHandler. It terminates the app if user didn\u0026#39;t end the task in their expirationHandler. func terminateAppIfNeeded() { Timer.scheduledTimer(withTimeInterval: 5, repeats: false) { [weak self] _ in guard let self = self else { return } if self.currentBackgroundTaskID == \u0026#34;invalid\u0026#34; { // good! programmer called `endBackgroundTask(self.currentBackgroundTaskID)` from within their `expirationHandler` } else { // Terrible code! fatalError(\u0026#34;terminate the app, as 25 + 5 seconds has passed but the user never told the OS that the task has ended!\u0026#34;) } } } } How are things different in +iOS13? Apple has provided two additional ways to do things while the app is in background:\n  The BGAppRefreshTaskRequest class replaces UIKit’s older background app refresh (launch app in background) functionality. The BGProcessingTaskRequest class lets you request extended background execution time in an unknown time in future, typically overnight where other apps aren\u0026rsquo;t in foreground any more and user doesn\u0026rsquo;t care much about their battery.  The purpose of the existing beginBackgroundTask(expirationHandler:) is to \u0026lsquo;raise an immediate \u0026ldquo;don’t suspend me\u0026rdquo; assertion\u0026rsquo; to the OS. Typically good for only 30 seconds.\n Summary Not every block is to be considered a completionHandler. Some blocks get stored for later execution. Some blocks may never get executed. It all just depends on the logic. The expirationHandler parameter of the beginBackgroundTask is a block that gets stored and may or may not get executed later.\nMUST Also see Apple forums: UIApplication Background Task Notes. It addresses a lot of commons questions.\n The name background task is somewhat misappropriate. Specifically, beginBackgroundTask(expirationHandler:) doesn’t actually start any sort of background task, but rather it tells the system that you have started some ongoing work that you want to continue even if your app is in the background. You still have to write the code to create and manage that work. So it’s best to think of the background task API as raising a “don’t suspend me” assertion.\n ","permalink":"https://mfaani.com/posts/uiapplication-backgroundtasks-through-the-lens-of-closures/","summary":"1 - The sync closure func processTwoNumbers(_ num1: Int, _ num2: Int, handler: (Int, Int) -\u0026gt; ()) { handler(num1, num2) } processTwoNumbers(10, 22, handler: { num1, num2 in print(num1 + num2) }) In the above, the handler is called in a sync matter. Same as below\n(1...100).forEach { val in print(val) } print(101) Uniqueness: The block passed in is executed immediately at the call site.\n2 - The async closure — block isn\u0026rsquo;t stored.","title":"UIApplication BackgroundTasks Through The Lens of Closures"},{"content":"I used to find it super hard to read git syntax. I didn\u0026rsquo;t know what what --, \u0026lt;\u0026gt;, [], ..., |, (), etc meant. Turns out, it\u0026rsquo;s a whole lot easier than you think.\nThe synopsis contains a list of most likely forms you\u0026rsquo;d try. But sometimes the synopsis is just one big long synopsis where all the options are thrown in at once. This can make it hard to understand the different ways of using the options and commands.\nSyntax Guide:  [ ] makes things optional. \u0026lt; \u0026gt; are just placeholders. ... just means one or more. | means: OR ( ) just groups things together for easier readings. -b, -d, --oneline, --add and so on are known as options  Beyond that, if you scroll down, you\u0026rsquo;d see each page usually (but not always) has the following sections, structure.\nSections  Name Description Options: Commands Examples Notes See Also  Within the Options sections, the placeholders are better described. Example: the following placeholders are extracted from git checkout page:\n  git checkout placeholders. These are within the Options sections.  With this knowledge let\u0026rsquo;s go through some examples:\nFirst Example git branch (-d | -D) [-r] \u0026lt;branchname\u0026gt;…​ Translates to:\n git branch either -d or -D optional -r one or more branch names  Meaning, all of the following are accepted:\ngit branch -d branchA git branch -d branchA branchB branchX git branch -d -r branchA branchP While the following aren\u0026rsquo;t accepted:\ngit branch -d -D branch # can\u0026#39;t provide both -d -D together git branch -d # must provide at least one branch name Second Example: git remote set-branches [--add] \u0026lt;name\u0026gt; \u0026lt;branch\u0026gt;…​ Translates to:\n git remote set-branches optional --add must include name (of the repo) one or more branch names  Meaning, all of the following are accepted:\ngit remote set-branches --add origin branchA git remote set-branches origin branchA git remote set-branches origin branchA branchB git remote set-branches --add origin branchA branchB \u0026lt;a lot more branches\u0026gt; branchX While the following aren\u0026rsquo;t accepted:\ngit remote set-branches --add origin upstream branchA # you can\u0026#39;t do multiple repos. Only multiple branches git remote set-branches --add origin # you must specify a branch name Now you might be asking what does \u0026lt;name\u0026gt; refer to. The answer is, you have to go into the command\u0026rsquo;s page and see the description of the command you\u0026rsquo;re using. E.g. the description of: set-branch can be found from here\n Changes the list of branches tracked by the named remote. This can be used to track a subset of the available remote branches after the initial setup for a remote. The named branches will be interpreted as if specified with the -t option on the git remote add command line. With --add, instead of replacing the list of currently tracked branches, adds to that list.\n Third Example: git diff [\u0026lt;options\u0026gt;] [\u0026lt;commit\u0026gt;] [--] [\u0026lt;path\u0026gt;…​] Translates to:\n git diff Any number of options. This is an odd one, because it\u0026rsquo;s not marked with ..., but the placeholder itself conveys that there can be multiple options. Typically options can be multiple. a commit (SHA) -- is just a separator added at the end of options. You don\u0026rsquo;t need need it unless git needs it to disambiguate your command. zero or more paths.  Meaning, all of the following are accepted:\ngit diff . # diff on the current directory git --name-only 97c8fa32a some/directory # shows the file name of every thing changed (from only inside some/directory) between the current index and the `97c8fa32a` commit  Forth Example git diff [\u0026lt;options\u0026gt;] \u0026lt;commit\u0026gt;…​\u0026lt;commit\u0026gt; [--] [\u0026lt;path\u0026gt;…​] So now you saw this and didn\u0026rsquo;t know what \u0026lt;commit\u0026gt;…​\u0026lt;commit\u0026gt; means. It\u0026rsquo;s easier to figure out, because we know what the other parts of this form mean. All we have to do is, search for it within the docs of git diff. If we do see we find:\n This form is to view the changes on the branch containing and up to the second \u0026lt;commit\u0026gt;, starting at a common ancestor of both \u0026lt;commit\u0026gt;. [skipping some parts of doc] .You can omit any one of , which has the same effect as using HEAD instead.\n Meaning the following is allowed:\ngit diff --name-only 5a189bf...e48224a git diff --name-only 5a189bf...e48224a -- some/directory git diff --name-only 5a189bf...e48224a -- some/directory1 some/directory2 💡💡💡 Surprisingly the followings are also allowed:\ngit diff --name-only 887bbeab6...3.29.0.1616171962 git diff --name-only 887bbeab6...branch32A The 👆 are allowed, because both a tag and branch point to a specific commit, so they both satisfy as a \u0026lt;commit\u0026gt;.\nFifth Example git log [\u0026lt;options\u0026gt;] [\u0026lt;revision-range\u0026gt;] [[--] \u0026lt;path\u0026gt;...] Translates to:\n git log Any number of options. This is an odd one, because it\u0026rsquo;s not marked with ..., but the placeholder itself conveys that there can be multiple options. Typically options can be multiple. revision-range: Git allows you to refer to a single commit, set of commits, or range of commits in a number of ways. They aren’t necessarily obvious but are helpful to know. For more on that see here and here. When no \u0026lt;revision-range\u0026gt; is specified, it defaults to HEAD (i.e. the whole history leading to the current commit). origin..HEAD specifies all the commits reachable from the current commit (i.e. HEAD), but not from origin. -- is just a separator added — if you want to specify one or more paths. You don\u0026rsquo;t need need it unless git needs it to disambiguate your command. Zero or more paths.  Meaning the following is allowed:\ngit log git log -10 git log cj0ds9232..adfs923kcv git log -S \u0026#34;plus sign\u0026#34; -- Documentation/git-branch.txt It bit explanation for the last item: The -S:\n It is useful when you’re looking for an exact block of code (like a struct), and want to know the history of that block since it first came into being\n Ultimately what this does is, searches in all your commits at the given path and tries to find mentions of \u0026ldquo;plus sign\u0026rdquo; in the contents of your commit. Hat tip to user Torek. See here\nOther notes:   To familiarize yourself more with jargon used in the pages. See git glossary 📖.\n  Understanding what the placeholder is about is key. Take your time and search on the doc page (or on the internet) for it. Common placeholders are:\n \u0026lt;path\u0026gt; \u0026lt;pathspec\u0026gt; \u0026lt;branch\u0026gt; \u0026lt;repo-name\u0026gt; \u0026lt;tree-ish\u0026gt; \u0026lt;commit\u0026gt;    Options and commands are different.\n Options are prefixed with - or --. Comamnds don\u0026rsquo;t have any - or -- before them. git add, git log, git diff, git remote, etc are all different commands. Some commands have sub-commands e.g. git remote has different commands:  git remote add git remote remove git remote rename      Every git command has various options. If you don\u0026rsquo;t set them, then git will fallback to its defaults. A lot of times the current commit, branch, directory get used if you dont' specify antything.\n  The position of ... is important. Example: \u0026lt;branch\u0026gt;... is different from [\u0026lt;branch\u0026gt;...]\n \u0026lt;branch\u0026gt;... means one or more branches. [\u0026lt;branch\u0026gt;...] means optionally 1 or more branches. i.e. zero or more branches.    The git doc pages are not identically structured. If you can\u0026rsquo;t understand the docs, it\u0026rsquo;s not your fault. It\u0026rsquo;s because the documentation is written by lots of different people.\n  Also see: How do I read git synopsis documentation?\n","permalink":"https://mfaani.com/posts/how-to-read-git-documentation/","summary":"I used to find it super hard to read git syntax. I didn\u0026rsquo;t know what what --, \u0026lt;\u0026gt;, [], ..., |, (), etc meant. Turns out, it\u0026rsquo;s a whole lot easier than you think.\nThe synopsis contains a list of most likely forms you\u0026rsquo;d try. But sometimes the synopsis is just one big long synopsis where all the options are thrown in at once. This can make it hard to understand the different ways of using the options and commands.","title":"How to Read Git Documentation"},{"content":"When you get on a plane, the crew members make sure everyone that checked in, is actually on the flight. If someone doesn\u0026rsquo;t board, then the plane either stays until they board or remove them and their luggages from the plane. It\u0026rsquo;s a safety check.\nCocoaPods does something similar. But before we dive deep into what a Manifest.lock file is, we should learn about the two schools of thoughts when it comes to dependencies.\nCommitting /Pods folder Commit the Podfile, Podfile.lock and /Pods folder.\nPros:\n Anyone can download the project and even without having CocoaPods they can run the app. It\u0026rsquo;s easier to see all the code at once, but also you get to see changes/updates to the pods. This becomes more vital if you own the pod yourself. Engineers will only need to have CocoaPods installed if they ever needed to bump a pod version.  Cons: Your repo size would go up, because it contains all these libraries.\nWhen is pod install required?  If you bumped a pod version. The intent is to propagate the changes to both the Podfile.lock and /Pods  Not committing the /Pods folder Commit the Podfile, Podfile.lock only. Don\u0026rsquo;t commit the /Pods folder.\nPros: Your repo size is smaller.\nCons:\n Every person needs to have CocoaPods installed. And before they do anything, they have to do pod install to make sure they have all the necessary code pulled in. It\u0026rsquo;s a tedious and repetitive process as everyone in your team has to do it. The extra process also makes it slightly more difficult for junior engineers to learn about the project.  When is pod install required?  After you clone the repo. If you bumped a pod version and want to update the Podfile.lock so everyone else gets those exact updates. After you pulled down the repo and see changes in the Podfile.lock and need pull the current version of all dependencies.   To be clear, this post isn\u0026rsquo;t about which approach is better. It\u0026rsquo;s just about explaining the differences. For more on the comparison of the two approaches, see CocoaPods and Lockfiles - video\nYou might be thinking now how do we make sure everyone has all the correct pods installed. It\u0026rsquo;s simple, the Podfile.lock gives you the version used for every pod. Think of the Podfile.lock as a snapshot of all your versions.\nSo when does the Manifest.lock come into play? It\u0026rsquo;s mainly used to protect you when your intent is \u0026lsquo;After you pulled down the repo and see changes in the Podfile.lock and need to match your latest snapshot\u0026rsquo;.\n The Podfile.lock is your repo\u0026rsquo;s snapshot. The Manifest.lock is your local machine\u0026rsquo;s snapshot.  If these two files don\u0026rsquo;t match, then Xcode will throw a build time error.\nTo be clear those two files always have to always match, it\u0026rsquo;s just that there\u0026rsquo;s a higher probability of things not matching when two engineers/macs are involved. Meaning one engineer has done pod install on their mac and has updated the Podfile.lock and now you have to do another pod install on your own mac. When you run pod install on your mac, it affects your Manifest.lock.\nTricks You can achieve CI Caching if you don\u0026rsquo;t commit the pods directory i.e.:\n Cache the workspace and pod directory. When pulling a cache, can compare Podfile.lock -\u0026gt; Manifest.lock. If they match: you can skip pod install, saving a bunch of time. else: do pod install  This saves you time. Something like:\ndiff Pods/Manifest.lock Podfile.lock \u0026gt;/dev/null || bundle exec pod install --repo-update Why would the lock files not be in sync though? In theory this should never happen. Except when it does.\n If the CocoaPods/Xcode version is different between the two machines. Also might be that during some merge conflict on the Podfile or Podfile.lock things got messed up.  How does Xcode check your lock files? CocoaPods adds a shell script into the Build Phases:\n  Xcode - lock file checks during build phase  If the lock files don\u0026rsquo;t match, then Xcode will throw the following error:\n error: The sandbox is not in sync with the Podfile.lock. Run \u0026lsquo;pod install\u0026rsquo; or update your CocoaPods installation.\n Docs: Docs from CocoaPods repo:\n Manifest.lock: A file contained in the Pods folder that keeps track of the pods installed in the local machine. This files is used once the exact versions of the Pods has been computed to detect if that version is already installed. This file is not intended to be kept under source control and is a copy of the Podfile.lock.\n Summary  Manifest.lock is per machine, while Podfile.lock is per project. Manifest.lock should never be committed, while Podfile.lock must always be committed. Manifest.lock should always match the Podfile.lock. Otherwise you\u0026rsquo;ll get a build error. Committing the /Pods folder depends on your team\u0026rsquo;s decision. It\u0026rsquo;s optional.  Acknolwedgements Shout out to Olivier Halligon and Zac West for sharing their amazing insight.\n","permalink":"https://mfaani.com/posts/what-is-manifest.lock-file/","summary":"When you get on a plane, the crew members make sure everyone that checked in, is actually on the flight. If someone doesn\u0026rsquo;t board, then the plane either stays until they board or remove them and their luggages from the plane. It\u0026rsquo;s a safety check.\nCocoaPods does something similar. But before we dive deep into what a Manifest.lock file is, we should learn about the two schools of thoughts when it comes to dependencies.","title":"What is Manifest.lock File?"},{"content":"I was using Xcode\u0026rsquo;s View Hierarchy and noticed this UITransitionView in my view hierarchy.\n... UIWindow UITransitionView FooVC UITransitionView BarVC This was odd because I was expected a view hierarchy as such:\n... UIWindow UITransitionView FooVC BarVC What made it more perplexing was that the canvas was showing things correct, but the View Hierarchy didn\u0026rsquo;t make sense.\nTo be clear on jargon, the following is the name of each section of Apple\u0026rsquo;s view debugger:\n I googled Apple documents, but found nothing on UITransitionView. It\u0026rsquo;s private API. I had suspicions for why I was seeing it\u0026hellip; So I opened up a sample project to test things out.\nThe following is the view hierarchy I got for presenting the yellow VC on another VC.\n  Seems that the presented and presenting stacks are on different `UITransitionView`s  Expanded Hierarchy is as such:\n Conclusion If you you\u0026rsquo;re seeing multiple UITransitionView it\u0026rsquo;s likely because you\u0026rsquo;re presenting one viewcontroller over another. One UITransitionView is for the presenting nav stack while the other is for the presented nav stack.\n","permalink":"https://mfaani.com/posts/what-is-uitransitionview-about/","summary":"I was using Xcode\u0026rsquo;s View Hierarchy and noticed this UITransitionView in my view hierarchy.\n... UIWindow UITransitionView FooVC UITransitionView BarVC This was odd because I was expected a view hierarchy as such:\n... UIWindow UITransitionView FooVC BarVC What made it more perplexing was that the canvas was showing things correct, but the View Hierarchy didn\u0026rsquo;t make sense.\nTo be clear on jargon, the following is the name of each section of Apple\u0026rsquo;s view debugger:","title":"What is UITransitionView about?"},{"content":"Edit Ever since I\u0026rsquo;ve upgraded to Xcode 16.1 I\u0026rsquo;ve experienced significant issues with finding callers. Yet aside from Xcode being broken, this post is still applicable for having a correct understanding of when / why / how Xcode should and shouldn\u0026rsquo;t work.\nSample Project Download the sample project if you want. You don\u0026rsquo;t have to though. The project doesn\u0026rsquo;t even need to be ran. It\u0026rsquo;s just provided for context.\nI\u0026rsquo;m going to discuss two gotchas I hade with Xcode. For quite some time I thought a fix was coming, then I realized this is because I didn\u0026rsquo;t fully understand the difference between an interface (or protocol) with a concrete type.\nFind Call Hierarchy not working: Have you ever right clicked on a function -\u0026gt; Find -\u0026gt; Find Call Hierarchy, but then wondered why Xcode doesn\u0026rsquo;t show you where the function is getting called from?\n  Xcode - Find Call Hierarchy doesn\u0026#39;t show callers  The reason that such a complication exists is Swift Protocols. The compiler is very precise.\n When you try to look up \u0026lsquo;Find Call Hierarchy\u0026rsquo; on a concrete type, Xcode tries to look things up by finding a call to that very implementation. When you try to look up \u0026lsquo;Find Call Hierarchy\u0026rsquo; on a protocol type, Xcode tries to look things up by finding a call to all conforming implementations.  class Logger: LoggerProtocol { func performLog() { print(\u0026#34;Production Code\u0026#34;) } } Trying to find all callers of performLog from above results in Xcode looking up references for Logger.performLog. Usually there aren\u0026rsquo;t much callers that directly hit a concrete implemenation of a function. Xcode found zero callers!\n  Xcode - Find call hierarchy on a class/concrete type, doesn\u0026#39;t find callers  While if you try finding the caller by write clicking the function signature under the protocol then things are different:\nprotocol LoggerProtocol { func performLog() } It\u0026rsquo;s because Xcode will look up references for LoggerProtocol.performLog (vs. Logger.performLog).\nUsually there are a lot of conforming types that implement a protocol function. In this sample project that I wrote, Xcode only found one caller:\n  Xcode - Find call hierarchy on a protocol type,can find callers  Jump to Definition not working: Similarly, have you ever right clicked on a function call -\u0026gt; Jump to Definition -\u0026gt; Then get prompted with too many definitions?\n  Xcode - Jump to Definition hell!  When it comes to protocols, the compiler just can\u0026rsquo;t tell which adaptation of a protocol gets used e.g. upon tapping \u0026lsquo;Jump to Definition\u0026rsquo; on performLog in the snippet below, Xcode will show all conformances (production adoption, test-code adoption, another production adoption, etc.) to that protocol requirement.\nstruct ViewModel { var logger: LoggerProtocol func executeLog() { logger.performLog() } } Xcode leaves it up to you to decide which class definition/implementation is the one you\u0026rsquo;re looking for.\n  Xcode - leaves it up to the developer to decide which implementation they want  Usually you don\u0026rsquo;t want the protocol definition nor want the mock implementation. Rather you just want one of the concrete implementations that is for production code.\nRuntime advantages During runtime Xcode can actually identify the implementation is uses. Obviously if that wasn\u0026rsquo;t the case then Xcode won\u0026rsquo;t know which implementation to execute. The advantage of this is that if you use Xcode\u0026rsquo;s Step Into ( ) on a function then it will take you to the right implementation.\n  Xcode - Step into on `perfromLog` takes you to correct implementation  Will take you to:\n  Xcode - found implementation  Conclusion: Xcode can\u0026rsquo;t automagically identify which implementation of a protocol is used. Additionally when a concrete type is never made part of the contract/API/signature, then it won\u0026rsquo;t be used in Xcode\u0026rsquo;s lookups that happen against the protocol type. Using this insight we can better understand the behavior of Xcode and use its tools in the right way.\n","permalink":"https://mfaani.com/posts/why-cant-xcode-show-caller/","summary":"Edit Ever since I\u0026rsquo;ve upgraded to Xcode 16.1 I\u0026rsquo;ve experienced significant issues with finding callers. Yet aside from Xcode being broken, this post is still applicable for having a correct understanding of when / why / how Xcode should and shouldn\u0026rsquo;t work.\nSample Project Download the sample project if you want. You don\u0026rsquo;t have to though. The project doesn\u0026rsquo;t even need to be ran. It\u0026rsquo;s just provided for context.\nI\u0026rsquo;m going to discuss two gotchas I hade with Xcode.","title":"Why can't Xcode show the caller?"},{"content":"I always thought that if you just use {get} on a protocol variable, then you can still set it i.e. it doesn\u0026rsquo;t matter if you give it a setter or not. That\u0026rsquo;s not true. It really depends on which compiler checks come in to place. Compiler checks are different depending on the type you want a variable to be.\nCan you guess which of the two snippets will compile?\nSnippet A\nprotocol Person { var age: Int { get } } class Adult: Person { var age = 20 } let a = Adult() a.age = 20 Snippet B\nprotocol Person { var age: Int { get } } class Adult: Person { var age = 10 } let a: Person = Adult() a.age = 20 Only snippet A compiles.\nSnippet B gives the following error:\n Cannot assign to property: \u0026lsquo;age\u0026rsquo; is a get-only property\n Why? In snippet A, when we write down let a = Adult() the compiler looks at the definition of Adult.age and sees it\u0026rsquo;s mutable so it allows it.\nWhere in snippet B we have let a: Person = Adult(), the compiler looks at the definition of Person.age and sees it\u0026rsquo;s an immutable property. So it disallows that modification.\nSummary If the type that the compiler has to check against is the Protocol type itsef, then the getter/setter used on a protocol must be adhered exactly\u0026hellip;\nThe compiler requires that you satisfy protocol requirements. As long as the class that adopts a protocol, conforms to its requirements then the compiler is happy. Yet the compiler will not assume the type of a variable to be its assignment type. It just so happens that the \u0026lsquo;assignment type\u0026rsquo; satisfies the \u0026lsquo;declared type\u0026rsquo;.\nTo understand the jargon used:\nvar x: SomeType = SomeClass() /* | | declared type assignment type */ Credits Special thanks to Suyash Srijan who helped me figure this out.\n","permalink":"https://mfaani.com/posts/swift-protocol-compile-time-check/","summary":"I always thought that if you just use {get} on a protocol variable, then you can still set it i.e. it doesn\u0026rsquo;t matter if you give it a setter or not. That\u0026rsquo;s not true. It really depends on which compiler checks come in to place. Compiler checks are different depending on the type you want a variable to be.\nCan you guess which of the two snippets will compile?\nSnippet A","title":"Swift Protocol Compile Time Check"},{"content":"","permalink":"https://mfaani.com/posts/ios/foreground-vs-background-runtime/","summary":"","title":""},{"content":"  I’m Mohammad Faani; I’m a husband, a father, local neighborhood friend and a software engineer. I\u0026rsquo;ve answered a few questions on StackOverflow.com where I go by mfaani username. Originally my profile name was honey. It\u0026rsquo;s because honey, butter and Barbari Bread are proof that a benevolent deity exists who wants us to be happy. The other things that make me happy are family, rings and soccer (long time amateur soccer player and Arsenal fan). This blog is mainly a result of me wanting to add a personal touch to what I write and try something new.\nGet in touch via Twitter @mfaani or email (just replace the \u0026rsquo;m' in \u0026lsquo;mfaani\u0026rsquo; with \u0026lsquo;r\u0026rsquo; and send me an email at gmail)\nAlso in case you\u0026rsquo;re wondering the logo / favicon of the blog is:\n ","permalink":"https://mfaani.com/about/","summary":"I’m Mohammad Faani; I’m a husband, a father, local neighborhood friend and a software engineer","title":"About"}]