"Skipping the unreadable item was right. Dropping the count was the bug."
Swift Errors Are Values You Can Design
A Swift function that can fail says throws, and the caller must try. Since Swift 6 you can also write typed throws — throws(InboxError) — so the caller knows the exact set of failures without casting. An error type is a place to put the words a person needs: which file, which entitlement, which remedy. CustomStringConvertible or LocalizedError turns that into a message you can show.
try? is the escape hatch that converts any error into nil. It belongs where failure genuinely does not matter. Everywhere else it is the error-handling twin of the quiet else { return } from the previous lesson.
The Queue That Reported Itself Empty
A per-item capture queue listed its folder like this: decode each manifest, and continue past any that failed. Keep reading past a bad item — that part was right; it is what a folder-per-item store buys you. But a manifest that would not decode did not fail: it became invisible. The files stayed on disk, every count said the item was not there, nothing logged, and the app showed an empty queue.
It was not hypothetical. The first time the item type gains a field, every manifest written before that moment stops decoding at once (the next lesson shows why). On a phone where that queue is the only copy of captures made with no network, an app update would have emptied it and said nothing. The fix kept skipping and stopped hiding: the listing returns the readable items and the unreadable ones with a reason each, and the app shows "N captures this build cannot read — still on this device, untouched".
The Share Extension That Always Succeeded
A share extension saved with try? and then called completeRequest no matter what happened. Every failure — a file that would not load, a folder that was missing — reported success to iOS and to the user. The rule the family took from it: an extension must not be able to fail silently in any direction. A refusal is visible in the sheet, an error is an alert, a hang is a timeout that becomes an alert.
Some Failures Swift Cannot Catch
Swift do/catch handles Swift errors. It does not catch an Objective-C NSException. A family dictation app called AVAudioEngine.prepare() before the engine had any tap installed; on macOS 26 that raises an exception inside AVFoundation, and the app died with SIGABRT about half a second after launch. No catch could have helped. The defence is calling Objective-C APIs in the order and state their documentation requires.