You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Documented the background-tap dismisser rule in keyboard-avoidance — a root-view tap-to-dismiss recognizer needs a second guard beyond cancelsTouchesInView = false: it must refuse touches landing on a UIControl. The gesture's action runs before the delayed touchesEnded reaches the control, so endEditing's layout pass writes the control's new frame to the model layer and UIControl.endTracking then resolves the touch-up as .touchUpOutside — the tap is silently swallowed and the user has to tap the button twice, more reliably the bigger the keyboard-driven shift. Includes the shouldReceive predicate walking the superview chain, why deferring the dismissal couples correctness to run-loop ordering, and why the predicate belongs in a testable static function.
Documented the animated-reconfigure trap in diffable-data-source — an animated apply carries structural changes only; reconfigureItems rides a second, non-animated apply, because a cellProvider running inside the animation transaction can leave a UIStackView arranged subview's hide / unhide half-applied (view.isHidden == true → out of layout at x = -width and out of the accessibility tree, while layer.isHidden == false keeps painting). Includes the pixels-present / AX-element-absent diagnostic, the lldb flag comparison, why the cell-side performWithoutAnimation "fix" is wrong, and why the pin belongs in UI automation rather than a unit test. Corrected the Common Mistakes row that previously prescribed folding reconfigureItems into the same apply; list-composition → "First-Apply Animation Gate" now points at it.
Fixed Crashlytics dSYM guidance in build-phases that taught the single-dSYM trap — the section recommended upload-symbols --build-phase and explicitly said not to pass dSYM paths manually, but that mode resolves only ${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME} and ignores every other dSYM in the folder (verified empirically against upload-symbols 3.21). Every dynamic dependency produces its own dSYM, and in a Composition-Root layout those frameworks hold nearly all real code — so a project following the guide uploaded almost nothing and earned guaranteed missing-dSYM console warnings once real crashes arrived. Pass the whole DWARF_DSYM_FOLDER_PATH directory instead: directory arguments are searched recursively, covering app + framework dSYMs in one call. The sample now fails the archive when the tool is missing rather than warning (the old fallback contradicted the file's own principle #3), and gates collection off in Debug via FirebaseCrashlyticsCollectionEnabled, since Debug builds produce no dSYM at all and their crashes are permanently unsymbolicatable console noise.
Added animated-image-playback reference — traps for a UIImageView subclass that drives its own frame engine (SDWebImage's SDAnimatedImagePlayer, or CADisplayLink + frame buffer). Overriding isAnimating freezes the subclass's own rendering: UIKit's image-display path consults it first and early-returns before _setImageViewContents:, so every super.image = frame updates the model value only and the screen holds the first frame forever. SDAnimatedImageView gets away with the override solely because it renders through displayLayer: — the override and the rendering path are a load-bearing pair, and copying the API surface without the rendering half ships the freeze. Also covers the visibility-pause freeze (UICollectionView pools offscreen cells by setting hidden in place, so no descendant hook fires on scroll-back) with a .common-mode watchdog resume, and a four-step probe ladder whose layer-property toggle is the discriminator that turns "frames delivered but screen frozen" into one specific UIKit code path.
Rewrote makefile around the decisions a Makefile author actually faces, not just a target list. New: recipe-vs-script split with a concrete threshold (~10 lines, loops, AppleScript, CI reuse) and the reason it matters ($$ escaping, no shellcheck, no sh -x, no standalone run); simulator-vs-physical-device targeting; DerivedData strategy as an explicit trade-off table, including that sharing the IDE cache forbids -skipMacroValidation and CODE_SIGNING_ALLOWED=NO because they change the build-settings fingerprint and force full rebuilds; streaming output via tee + grep --line-buffered under set -o pipefail, and why xcbeautify --quieter swallows errors that share a task with a warning; parameterised targets (ONLY=, CONFIG=) instead of a target per scope.
Hardened build-result verification across makefile and xcodebuild-error-detection — green now requires exit 0 and the success marker present and no crash marker. Checking only for BUILD FAILED passes a build that died before compiling anything (missing scheme, locked DerivedData), since such a log carries no failure line either. Dropped bare crashed from the crash pattern: the host app's own runtime logging shares the stream and reddens every noisy run. Logs are now named after the working directory so sibling git worktrees stop clobbering each other's results.
Replaced simctl install booted in makefile with explicit UDID resolution — booted picks whichever simulator happens to be up, so an iPhone sim from another project makes an iPad-only app fail to install with an error naming the app rather than the device. SIM_NAME/SIM_OS now feed both the destination string and a runtime-scoped awk lookup, so a same-named device under another runtime cannot win. Boot via simctl bootstatus -b, which waits for readiness; plain boot returns early and installs in that window fail intermittently.
Documented physical-device workflows in makefile — sourceable selection script with DEVICE_UDID= override, gitignored cache treating a disconnected device as a miss, and live xctrace probe. A selection script must never read without a TTY: agents and CI have none, so gate prompts on [ -t 0 ], auto-select when unambiguous, and otherwise fail with the candidate list and the command to pick one. Also covers devicectl install/launch, -allowProvisioningUpdates, and stating a project's no-simulator constraint (device-only binary slices) in the Makefile header.
Added xcode-close.applescript — closes only this repo's Xcode workspace windows before tuist generate. Matches by file path, not window title: a title prefix also closes MyAppTweak when regenerating MyApp, and cannot tell two checkouts of one repo apart. Carries the two Xcode-scripting constraints: repeat with d in (get documents) yields a nested specifier Xcode resolves wrongly (distinct workspaces all report the same file), and documents is z-ordered so closing shifts later indices — index explicitly and iterate backwards.
Rewrote Makefile.template to match: verification-carrying xc macro, UDID-resolving run, parameterised test, DerivedData strategy switch documented inline, and both version sources (xcconfig / Project.swift) kept as alternatives.
Fixed IDE DerivedData resolution in makefile, Makefile.template, and xcode-project-setup — the warnings target matched -name '<project>-*' | head -1, but the directory suffix hashes the workspace's absolute path, so a stale entry accumulates for every location the project has occupied (git worktrees, a /tmp clone for a baseline comparison, a moved repo). A real machine had four DingPOS-* directories, two pointing at /tmp checkouts; head -1 returns whichever the filesystem lists first, so the target could silently clean-build a different checkout and report its warnings. Now matches each candidate's recorded WorkspacePath from info.plist, which is deterministic and degrades to empty for the existing guard.
Resolved a contradiction in xcode-project-setup's DerivedData gotcha row, which prescribed pointing -derivedDataPath at "the same location Xcode GUI uses (e.g. project-local .derivedData/)" — two different locations. The actual failure is CLI and IDE disagreeing; the fix is to apply one strategy consistently, so the row now points at the new trade-off section. xcode-project-setup § Warning Detection also notes that shared-cache projects need no warnings target at all.
Corrected the CODE_SIGNING_ALLOWED=NO mechanism in makefile, Makefile.template, and xcodebuild-error-detection. The previous text said the flag "strips the application-identifier entitlement". Measured across three separate apps: simulator builds carry an empty entitlements dictionary either way, so that was never the distinguishing factor. What the flag actually does is skip the CodeSign build phase, leaving only the linker's ad-hoc signature — codesign -dvvv reports flags=0x20002(adhoc,linker-signed) with it versus flags=0x2(adhoc) without — and simulator securityd rejects Keychain calls from a linker-signed-only binary with -34018. The conclusion (never on an installable build) was right; the stated mechanism was wrong, and it pointed anyone diagnosing the bug at an entitlement that is legitimately absent. Also extended the rule to test targets, whose bundle is installed too, and added that the flag must be consistent across every script sharing one DerivedData path.
Fixed a live bug in the shipped Makefile.templatehelp target — the awk's final clause !/^##/{d=""} cleared the pending description on any non-## line, so the ordinary shape of a ## doc line, then a # comment explaining the why, then the target, dropped that target from the menu. The template's own clean target was invisible in make help because of it. Added /^#/{next} and documented it as the third awk trap alongside the colon strip and the multi-target regex. Failure mode is nasty because the target still works — it is only missing from the menu.
Sharpened the crash pattern in makefile and xcodebuild-error-detection — a bare SIGABRT is not safe either. Firebase Crashlytics prints The signal SIGABRT has a non-Crashlytics handler on every green run, so the pattern reddened whole passing suites. Anchor to how a crash is reported (Restarting after unexpected exit|Crashed:|due to (signal )?(SIGABRT|SIGSEGV)|terminated due to) and grep a known-passing log before widening it.