UIKit and Swift Fundamentals
🟪 Syntax · 🟦 Types/APIs · 🟩 Correct pattern · 🟥 Bug · 🟨 Gotcha · 🧠 Explanation
Frame versus bounds
framedescribes a view's rectangle in its superview's coordinate system.boundsdescribes the view's own internal coordinate system.bounds.originchanges which internal portion is visible; scroll views use this behavior.- Transforms make
frameless reliable as a representation of the original geometry because it is the axis-aligned enclosing rectangle.
Interview example:
Moving a view changes its frame. Changing its bounds origin changes the content coordinate system without necessarily moving the view in its parent.
Auto Layout priorities and ambiguity
Content hugging resists a view growing beyond its intrinsic content size; compression resistance resists shrinking below it. Both use priorities, so constraints can express which view should yield when space is limited. Required constraints that cannot all be satisfied are unsatisfiable; constraints that leave multiple valid frames are ambiguous. Inspect the view hierarchy and constraint priorities in Xcode's View Debugger, then fix the smallest incorrect or missing relationship rather than lowering priorities indiscriminately.
For custom views, provide a meaningful intrinsic content size only when the view can determine one, and avoid fighting explicit constraints with frame mutations. Test Dynamic Type, localization, and narrow widths because those change the available space.
Horizontal collection layout
For a basic horizontal list, configure a flow layout:
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 12
layout.minimumInteritemSpacing = 12
layout.itemSize = CGSize(width: 180, height: 120)For self-sizing cells, define constraints that fully determine the cell's size and use an estimated size carefully. For Pinterest-style or irregular layouts, subclass UICollectionViewLayout or use a suitable compositional layout.
A custom layout must correctly implement:
prepare()collectionViewContentSize- layout attributes for a rect
- item attributes
- invalidation behavior
Test empty data, rotation, dynamic type, safe-area changes, reuse, and large datasets.
Horizontal and vertical product sections
Use compositional layout when a screen combines vertically scrolling sections with horizontally scrolling product groups. Each section can define its own item, group, orthogonal scrolling behavior, spacing, and supplementary views.
Avoid nested scroll views unless necessary; compositional layout usually provides better reuse and coordination.
Computed properties
A getter-only computed property cannot be assigned to:
var requiredHeight: CGFloat {
isCollapsed ? 128 : 248
}Change the source state (isCollapsed) or add deliberate storage/setter semantics. Do not add a setter merely to silence the compiler if the value is derived.
For UI naming, collapsed/expanded is usually clearer than shrink/grow when describing state.
Struct versus class
Choose a struct when:
- The value represents data rather than identity.
- Independent copies are desirable.
- Shared mutable state is unnecessary.
- Value semantics improve local reasoning.
Choose a class when:
- Reference identity matters.
- Multiple owners intentionally observe the same mutable instance.
- Inheritance or Objective-C interoperability is required.
- The framework requires a class, such as many UIKit delegates/controllers.
Changing a class to a struct is not only syntax. Review identity checks, weak references, mutation behavior, inheritance, Objective-C exposure, and copying cost.
Nested types in extensions
Swift extensions can add nested types, computed properties, methods, protocol conformances, and initializers. They cannot add stored instance properties.
extension WebView {
final class Coordinator: NSObject, WKNavigationDelegate {
// Nested helper scoped under WebView.
}
}This is useful for namespacing a representable coordinator near its parent type.
Getting a class/type name
extension NSObject {
static var className: String {
String(describing: self)
}
var className: String {
String(describing: type(of: self))
}
}For pure Swift types, prefer a protocol extension or direct String(describing:) instead of forcing NSObject inheritance.
Force unwrap and force cast
Replace ! and as! using the contract that best matches the situation:
guard letfor required early validationif letfor optional branches- optional chaining for best-effort access
??for a meaningful default- throwing initializers/functions for recoverable failure
preconditiononly for programmer invariantsas?for conditional downcasting
Do not replace every force unwrap with a meaningless default. That can hide invalid state and make bugs harder to detect.
ARC and lazy properties
Classes use reference semantics and ARC. Structs do not participate in reference counting directly, though they can contain references. A lazy stored closure can create a cycle when it captures its owning instance strongly.
Asset catalog vector preservation
preserves-vector-representation tells the asset pipeline to retain vector information for a vector-compatible asset instead of only producing fixed raster renditions. This can help the system scale the image cleanly at runtime. Validate rendering, tinting, memory, and platform behavior; not every source asset or usage gains the same benefit.
Swift Testing and XCTest
Xcode 16 supports Swift Testing alongside XCTest.
Swift Testing uses constructs such as @Test and #expect, while XCTest remains important for existing suites, UI testing, and framework compatibility. The important interview point is test design: isolate responsibilities, inject dependencies, and make failures expressive.
Search bar updates
For UIKit, UISearchBarDelegate can forward text changes to a view model. For reactive code, publish query changes and debounce them. Avoid sending a request for every keystroke, and cancel or supersede stale searches.
Loop complexity
- A single loop over
nelements: usually O(n) - Two independent sequential loops: O(n) + O(n) = O(n)
- Nested loops over
n: usually O(n^2) - Loop repeatedly halving the input: O(log n)
if/else: the condition and selected branch determine the cost; the control statement itself is typically O(1)whileandrepeat-while: complexity depends on how the condition progresses;repeat-whileruns at least once
