Xcode is Hot Garbage
Xcode is the worst code editor I have ever used, hands down, including NetBeans, and I firmly believe that anyone who says otherwise has Stockholm Syndrome or has never used a good editor/terminal/IDE/compiler setup. Here is a barely-edited list of my accumulated complaints, serving as a reference next time a long-time iOS dev asks me why I think it’s so awful.
- It crashes…
- at least once a day, every day.
- if you right-click a tab at the wrong time.
- so hard, that sometimes you can’t even boot it without digging around in
~/Libraryand/or.xcodeprojto delete random corrupted files.
- Canvas…
- fails randomly and can be fixed by randomly closing and reopening it and/or the tabs its on.
- doesn’t pick up any but the most cosmetic of changes without a full restart.
- is so unbelievably slow that it’s faster to rebuild the app onto my phone.
- Rename refactors…
- have such a flashy accordion animation that my 2025 computer bogs down to 3-5fps for precisely zero utility while I wait for it to complete.
- rarely work if they involve more than a couple files, tending to spin for a while before giving up.
- sometimes look like they’re working, renaming 49/50 instances before giving up because one file is in a weird state so now you have to do all 50 by hand.
- often close the tab you’re currently looking at.
- Coding assistant chat (oh boy)…
- pops in code in an animation that frequently gets stuck halfway through so I’m looking at a orange-purple rainbow of non-syntax-highlighted slop.
- is clearly getting lied to by Xcode, often stating the changes it made on the previous turn that I’m currently looking at are not there, so it reapplies them, doubling the time that everything takes and burning through my quota.
- usually stomps any tweaks you make to its changes, if you have the gall to ask for more edits afterwards, even if you preemptively point out that you made changes you’d like to keep.
- hangs, all the time but especially if you *gasp* scroll the window while it’s writing to it, often so hard that the only resolution is to kill Xcode.
- logs you out frequently and provides zero indication that this has happened until you try to continue a conversation, which fails with an error.
- is tacked on at a higher UI level than the entire existing sidebar, so everything else useful on the sidebar is buried an extra layer down.
- obscures code with a hovering status bar for the current conversation even when that conversation is idle and waiting for your input.
- seems to keep cancelled turns in the conversation, meaning if it goes off the rails you are forced to decide between (1) throwing out the entire conversation because it’s impossible to get back on track with that in the context or (2) letting it waste time and tokens churning before you revert everything, which more reliably keeps it from retrying the same garbage.
- may or may not remember any of the conversations you had in progress when Xcode opens or logs back in, which combines well with the frequent crashes, hangs, and log-outs.
- Find/replace…
- seems to be hardcoded to default to symbol-type search, which often cannot actually find the symbols.
- is constantly reindexing, burning CPU cycles and delaying other jobs supporting a broken feature.
- doesn’t retrigger a search when you switch the search mode, which seems like an obvious oversight, thereby requiring additional rounds of switching your hands between mouse and keyboard.
- Type-based navigation…
- like go-to-definition or “quick info” frequently silently fails for no discernable reason.
- fails if you have a legal function name with an illegal parameter list, which is incidentally one of the times it is most useful.
- frequently cannot locate every usage of a symbol across the project.
- frequently does not highlight every usage of a selected symbol within the current file.
- And just reams of missing or broken quality-of-life features:
- Format-on-save doesn’t exist, so formatting and linting are chores you have to remember.
- Linting errors are not shown inline like compiler errors, so failures are always a fun surprise.
- Inline compiler errors seem to dismiss themselves whenever they feel like it, no matter how long ago you fixed the problem or deleted the error from the sidebar.
- Cross-target compiler errors seem to stick around basically forever, regardless of how many builds, rebuilds, or cleans you do – fresh builds will sometimes bring the errors back even when they are successful. Sometimes this causes test builds to erroneously fail because they refuse to acknowledge changed function signatures, even when you’ve cleaned and rebuilt all targets.
- The missing multiple-cursor features mean I frequently copy an entire file to VS Code, edit it, and copy it back. This is still twice as fast and more reliable than the aforementioned garbage find-replace.
- Despite the hilariously large number of keyboard-bindable commands it is quite hostile to keyboard-only workflows for the core editing experience, even after binding the all-important “run command by name”.
- “Back” and “forward” navigation is per-file, not per-location, which makes it much less useful if you do crazy things like “jump to definition for a function in this file” and then want to go back to where you came from.
- Tab pinning behavior is inferior to VS Code; for one, it doesn’t pin a tab when you put a breakpoint in it which seems like a pretty obvious indication you intend to return to this file.
- Predictive autocomplete is so slow that it’s completely useless. The only thing I ever use it for is typing out a batch of
cases in aswitchbut even then it’s a tossup if it’s faster. - Parameterized tests cause the inline test-case-input and failed-assertion informational elements to appear at the top of the test case list and the function body, respectively, so long blocks of a parameterized tests will split this information many viewports apart.
- Changing branches causes tabs pointing to files that no longer exist to stick around but revert to the useless Start Page, meaning that a brief aside looking at another branch will throw out half of your view state. Sometimes this happens even if the file is still there, but changed.
- Leaving it idling in the background quadruples my idle CPU load and halves my battery life. It’s IDLE.
lldb-rpc-serverpegs a CPU in the background with near-100% reliability if you hit an uncaught exception and then attempt to “resume”. You will have no idea until your computer dies 30 minutes later.
As a wise man once said: The compiler is unable to type-check this expression in reasonable time; try breaking up the expression into distinct sub-expressions.