Storyboard IBOutlet / IBAction Exists
ID |
swift.storyboard_iboutlet_exists |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Swift |
Tags |
reliability, suspicious-code |
Description
Reports <outlet>, <outletCollection> and <action> connections in
.storyboard and .xib files whose name does not match any property
or method declared on the owning customClass. Interface Builder
binds these connections at runtime via Key-Value Coding; a name that
the destination class does not declare raises
setValue:forUndefinedKey: (outlets) or
-[Class doesNotRecognizeSelector:] (actions) when the scene is
instantiated, and the app crashes on the first navigation to it.
<viewController customClass="HomeViewController" ...>
<connections>
<outlet property="missingLabel" destination="abc"/> <!-- FLAW: not on HomeViewController -->
<action selector="missingTapped:" destination="abc"/> <!-- FLAW: not on HomeViewController -->
<outlet property="titleLabel" destination="def"/> <!-- OK -->
</connections>
</viewController>
Rationale
Connection names are typed as strings in the XIB/storyboard XML and are not validated by the Swift compiler. Renames, deletions, and copy-paste from other scenes silently leave stale outlet / action references behind. The bug only surfaces the first time the screen is presented — usually in QA, occasionally in production.
The rule resolves each connection’s owner by walking the XML tree
upward to the nearest ancestor with a customClass attribute, then
checks the Swift type model for a matching property (outlet) or
method (action). Selector arguments are stripped — multi-arg
selectors like tableView:didSelectRowAtIndexPath: are matched
against the method name tableView.
Connections whose owner cannot be resolved are skipped: external
modules (customModuleProvider != "target") are out of view, and
missing customClass references are already reported by
swift.storyboard_custom_class_exists.