AI Code Review for Swift: What to Look For
AI code review for Swift: force unwraps, retain cycles in closures, main-actor violations, and Sendable conformance. What the Swift compiler accepts that still breaks at runtime, and how AST-level cross-file analysis finds it.
AI code review for Swift has a specific shape, because Swift's type system is strong enough to remove some bug classes entirely while leaving others fully intact. Optionals mean a nil cannot surprise you unless you asked for it with !. Value semantics remove a large family of aliasing bugs. Strict concurrency checking catches data races the compiler can prove.
What is left is a review surface dominated by four things: force unwraps, memory cycles in closures, threading and actor-isolation mistakes, and ordinary logic. None of them are caught by a clean build.
TL;DR — AI Code Review for Swift
- Force unwraps (
!) are the highest-value thing to review. They compile, and they crash.- Retain cycles hide in closures.
selfcaptured strongly in an escaping closure is a leak the compiler permits.- Concurrency is the modern Swift review surface: main-actor violations,
Sendableconformance, and unstructuredTasklifetimes.- Swift is one of ten languages with a dedicated AST codewalker in Macroscope, enabling lower-latency reviews.
- Cross-file analysis matters because Swift spreads behavior across protocols, extensions, and conformances in separate files.
- SwiftLint still earns its place. AI review is for what needs judgment, not for what a linter already flags.
What Is AI Code Review for Swift?
AI code review for Swift is automated analysis of Swift pull requests targeting the defects the compiler permits: force-unwrap crashes, reference cycles, actor-isolation and threading errors, and logic mistakes.
The category it does not need to spend much effort on is null safety, because optionals already handle it. That is a meaningful reallocation of attention compared with reviewing Objective-C, Python, or JavaScript.
Force Unwraps: The Main Event
foo! compiles cleanly and terminates the process if foo is nil. So does array[index] out of range, try! on a throwing call, and a failed as! cast.
Whether any given force unwrap is acceptable is entirely contextual, which is exactly why it needs a reviewer rather than a linter rule:
UIImage(named: "logo")!on an asset shipped in the bundle is defensible.- The same call on a filename from a server response is a crash waiting for a bad deploy.
try!on decoding a bundled JSON fixture is fine. On a network payload it is an outage.
A reviewer that flags every ! produces noise your team will mute. A reviewer that traces whether the value can originate from input, I/O, or configuration is doing the actual work.
Retain Cycles in Closures
Swift uses ARC, so there is no garbage collector to paper over a cycle. The classic form:
networkClient.fetch { response in
self.items = response.items // strong capture of self
}
If the closure is escaping and stored, self and the closure keep each other alive. The fix is [weak self] and a guard, and the compiler has no opinion either way.
Reviewing this well requires knowing whether the closure escapes and whether the object is long-lived, which is often defined in a different file from the call. That is a cross-file question.
Concurrency: Where Swift Review Has Moved
Modern Swift concurrency is where most of the interesting review now lives.
Main-actor violations. UI updates must happen on the main actor. Strict concurrency checking catches many cases, but code that reaches UIKit or SwiftUI state through indirection, or older code bridging to callbacks, can still do the wrong thing.
Sendable conformance. A type marked Sendable is a promise that it is safe to pass across concurrency domains. Adding the conformance to silence a warning, on a type holding mutable reference state, converts a compile-time complaint into a runtime data race. A Sendable conformance is an assertion, and reviewing it means checking the assertion is true — much like an unsafe block in Rust.
Unstructured Task lifetimes. A detached Task that nobody cancels keeps running after the view that started it is gone. That is a leak and sometimes a crash, and it compiles.
Actor reentrancy. An actor's state can change across an await inside one of its own methods, so invariants checked before the suspension may not hold after it.
Objective-C Interop: Optionals That Lie
Any Swift codebase with Objective-C in it, which is most iOS codebases of any age, has a category of bug that pure-Swift projects do not.
When Objective-C headers lack nullability annotations, the bridge imports types as implicitly unwrapped optionals (String!). These behave like non-optionals at the call site and crash like force unwraps at runtime. The compiler will not warn you, because as far as Swift is concerned the header promised a value.
Two review habits follow:
- Treat any
!type coming from a bridged header as an unannotated optional, not as a guarantee. The correct handling is usually to bind it withif letat the boundary rather than trusting it inward. - Watch for annotations added to silence warnings. Marking an ObjC header
nonnullis an assertion about code the Swift compiler cannot see. If the Objective-C implementation can still returnnil, the annotation has converted a warning into a crash. This is the same shape as an incorrectSendableconformance: a promise the type system now believes.
Bridged collections carry a milder version of the problem. An NSArray imported as [Any] loses element-type information, so what looks like a typed collection in Swift may contain anything the Objective-C side put there.
Why AST-Level Parsing Matters for Swift
Swift is hard to analyze textually. Protocol extensions mean a method's implementation may live nowhere near its call. Generics and associated types mean the same source line means different things per instantiation. Result builders make SwiftUI view bodies structurally unlike ordinary code.
Macroscope ships dedicated AST codewalkers for ten languages — Go, TypeScript, JavaScript, Python, Java, Kotlin, Swift, Rust, Ruby, and Vue.js — enabling lower-latency reviews, with every other language fully supported via the agentic engine. Macroscope also shipped code review pipeline improvements for Swift in July 2026 aimed at catching more idiomatic issues in the language.
Cross-File Analysis in iOS Codebases
Swift codebases distribute behavior deliberately: a protocol in one file, a default implementation in an extension in another, a conformance in a third, the call site in a fourth. A reviewer reading only the diff sees one of those four.
The consequential Swift bug is usually of the form "this conformance no longer satisfies what a distant caller assumes," which is invisible without following the call. Macroscope's cross-file analysis targets exactly this, and it is the capability behind its 48% detection rate on a 118-bug benchmark across 8 languages.
Encoding iOS Team Conventions
Mature iOS codebases accumulate rules that are local and real: no force unwraps outside tests, all view models must be @MainActor, no DispatchQueue.main.async in new code, networking only through the shared client.
Check Run Agents let you write these as markdown reviewers with include and exclude globs, so a rule for your app target does not fire on generated code or a sample project. Each runs as its own GitHub check, advisory or blocking independently.
What AI Review Does Not Replace
- SwiftLint handles mechanical style and idiom rules faster and more cheaply. Run it. AI review is for what needs cross-file context or judgment.
- The type system is your first reviewer. If an invariant fits in a type, put it there instead of documenting it for a reviewer.
- Tests are the only demonstration that behavior is correct.
- Instruments finds the leak. A reviewer flags the likely cycle; a profiler proves it.
Frequently Asked Questions
Does Macroscope support Swift?
Yes. Swift is one of ten languages with a dedicated AST codewalker, alongside Go, TypeScript, JavaScript, Python, Java, Kotlin, Rust, Ruby, and Vue.js. Macroscope shipped code review pipeline improvements for Swift in July 2026 targeting more idiomatic issues.
What Swift bugs does the compiler not catch?
Force-unwrap crashes from !, try!, as!, and out-of-range indexing; retain cycles from strong self capture in escaping closures; incorrect Sendable conformances; unstructured Task lifetimes; actor reentrancy across await; and all ordinary logic errors.
Is every force unwrap in Swift a bug?
No, and flagging all of them is noise. A force unwrap on a bundled asset or a compile-time constant is reasonable. It is a bug when the value can come from input, I/O, or configuration, where the correct behavior is to handle nil.
Should I use SwiftLint or AI code review?
Both. SwiftLint is faster and cheaper for mechanical rules. AI review handles what needs judgment or cross-file context, such as whether a force unwrap is reachable from a server response.
Can AI code review catch Swift concurrency bugs?
It can flag the common patterns: main-actor violations reached through indirection, Sendable conformances added to silence warnings on types holding mutable reference state, and detached tasks nobody cancels. Strict concurrency checking catches what it can prove; the rest is review.
Can I write custom Swift review rules?
Yes. Check Run Agents are markdown reviewers with include/exclude path globs, so you can scope a rule to your app target and exclude generated code, then run it as its own GitHub check.
Does AI review work on SwiftUI code?
Yes, and result builders are a good argument for AST-level parsing: a SwiftUI view body is structurally unlike ordinary imperative Swift, so a reviewer that parses the language handles it more reliably than one pattern-matching text.
