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. 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: js 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 loading, 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/ https://elangbamjohnson.github.io/ Repo's here https://github.com/elangbamjohnson/AIAnalyzer https://github.com/elangbamjohnson/AIAnalyzer