cd /news/developer-tools/building-aianalyzer-a-swift-static-a… · home topics developer-tools article
[ARTICLE · art-80056] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=↑ positive

Building AIAnalyzer: A Swift Static Analysis Tool That Knows When to Trust Its Own AI

A developer with 18 years of iOS experience built AIAnalyzer, a Swift CLI tool that detects architectural code smells and offers AI-generated fix suggestions through a hybrid system that decides whether to trust a fast local model or escalate to a stronger cloud one. The tool uses four provider types behind a shared protocol, including a hybrid mode that scores confidence locally and falls back to Gemini only when needed. When run against its own source, AIAnalyzer found the exact architectural smell it was built to catch.

read4 min views1 publishedJul 30, 2026

I've spent 18 years shipping iOS apps — enterprise VoIP, automotive, retail — and one problem kept resurfacing regardless of the domain: codebases quietly rot, and Swift never had great tooling to catch it early. Most static analysis tools are style linters, not architecture watchdogs. Nothing was really looking for God Objects, tangled responsibilities, or classes that had silently outgrown their purpose.

So I built AIAnalyzer — a Swift CLI, distributed via Homebrew, that does two things: detects real architectural smells using SwiftSyntax, and offers AI-generated fix suggestions through a system that decides, in real time, whether to trust a fast local model or escalate to a stronger cloud one.

The problem with "just use AI for everything"

Bolting an LLM onto a dev tool is easy. Bolting one on responsibly is a different problem. Two extremes are both bad:

Cloud-only: great quality, but every request leaves the machine. For teams under compliance constraints — banks, healthcare, defense — this is a non-starter.

Local-only: private and fast, but smaller on-device models sometimes give shallow or wrong suggestions, and you have no way of knowing when that's happening.

I wanted a third option: a system that tries local first, evaluates its own output quality, and only spends the cloud API call when it actually needs to.

The architecture

AIAnalyzer's AI layer has four provider types behind a shared AIProvider protocol:

swift

public protocol AIProvider {

    /// Requests a refactoring suggestion based on the provided context.
    /// - Parameter context: The issue and source code details.
    /// - Returns: An `AISuggestion` containing the AI's response.
    /// - Throws: An `AIProviderError` if the request fails.
    func suggest(for context: AIRequestContext) async throws -> AISuggestion
}

A factory dynamically builds the right provider based on configuration:

swift

let provider: AIProvider

switch configuration.serviceConfig.providerType {
case .gemini:
    // Cloud provider
    provider = GeminiProvider(apiKey: apiKey, model: configuration.serviceConfig.model)

case .ollama:
    // Local provider backed by Ollama, running a full LLM on-device
    provider = OllamaProvider(endpoint: configuration.serviceConfig.ollamaEndpoint, modelName: configuration.serviceConfig.ollamaModel)

case .local:
    // Local Core ML provider — a lighter-weight on-device option with heuristic fallback
    provider = LocalLLMProvider(modelPath: configuration.localModelConfig.localModelPath, modelName: configuration.localModelConfig.localModelName)

case .hybrid:
    // Orchestrator combining a local preference with an optional cloud fallback
    provider = HybridAIProvider(
        localPreferred: localPreferred,
        localFallback: localFallback,
        cloud: cloud,
        preferLocal: true
    )
}

Gemini — cloud API, highest quality, requires network + key

Ollama — full LLM running fully on-device via Ollama, zero network calls

Local (Core ML) — a lighter-weight on-device option with its own heuristic fallback, useful when you want on-device inference without running a full Ollama-hosted model

Hybrid — attempts a local-preferred provider first, scores confidence based on output characteristics, escalates to Gemini only when confidence is low, and falls back to a local-fallback or static message if all else fails

The interesting engineering problem isn't "call an LLM" — it's the confidence heuristic that decides whether to trust the local pass, and having two different flavors of local (a full LLM via Ollama, and a lighter Core ML path) gives the Hybrid mode real flexibility depending on what's available on a given machine. That layered fallback is the actual differentiator, and it's the part I'd want to keep iterating on if this becomes a bigger project.

Turning the tool on itself

Once detection worked, I ran AIAnalyzer against its own source — and found the exact smell it was built to catch. AnalyzerApp.swift had grown to 695 lines, quietly doing argument parsing, config , AI provider construction, and orchestration all at once. A God Object, in the God Object detector's own codebase.

Fixing it meant:

Migrating hand-rolled CLI parsing to Apple's swift-argument-parser

Splitting responsibilities into AnalyzerCommand, EnvironmentResolver, AISuggesterFactory, and AnalysisOrchestrator

Along the way, I also found and fixed a real false-positive bug: type classification was using substring matching on class names, so DetailViewControllerDelegate was getting misclassified as an oversized ViewController. Fixed by combining syntactic inheritance checks with strict suffix matching instead of loose contains() checks.

What shipped

59 passing tests, including boundary and integration cases

Fully automated release pipeline — tagging a version builds the binary, computes the checksum, and pushes the Homebrew formula update via GitHub Actions with zero manual steps

SARIF output, so findings integrate directly into GitHub code scanning

Publicly installable: brew install elangbamjohnson/tap/aianalyzer

What I'd tell someone building something similar

If you're integrating AI into a dev tool, the confidence-based fallback pattern is worth the extra complexity. It's tempting to just pick cloud or local and move on, but the real value — for privacy-conscious teams especially — is in a system that's honest about when it doesn't know enough, rather than confidently returning a weak local answer or defaulting to the network every single time.

If you want to see more of what I've been building — including a

deeper case study on this project —

my portfolio's at (https://elangbamjohnson.github.io/)

Repo's here (https://github.com/elangbamjohnson/AIAnalyzer)

── more in #developer-tools 4 stories · sorted by recency
── more on @aianalyzer 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/building-aianalyzer-…] indexed:0 read:4min 2026-07-30 ·