Native iOS
Architecture.
Structure is a product decision. The architecture choices made before the first screen is built determine whether a product can evolve — or whether it has to be rewritten.
MVVM is a broad pattern. How you implement it determines everything. Here is exactly how Morton Software Group structures the three layers across every product.
Plain Swift structs representing data from external sources. Codable for automatic JSON decoding. No business logic, no formatting, no UI concerns. A model is data — nothing else.
Each external data source has its own typed model. AQI data, fire detection data, and weather data are separate structs — never combined into a generic dictionary.
@MainActor ObservableObject classes that own business logic, manage state, and coordinate with Services. @Published properties drive UI updates. Singleton shared instances prevent duplicate API calls across screens.
Cache TTL logic lives here — not in the View, not in the Service. The ViewModel decides when data is fresh enough to serve from memory versus when to refetch.
SwiftUI Views are thin, declarative, and dumb. They observe ViewModel state via @StateObject or @EnvironmentObject and render accordingly. No network calls, no business logic, no direct model access.
A View should be replaceable without touching anything outside itself. If changing a View requires touching a ViewModel or Service, the View has too much responsibility.
Every external data source in a Morton Software Group application is backed by a protocol — making the service swappable, testable, and isolated from the ViewModel that consumes it. This is a representative example of how the service layer is structured.
Good architecture is as much about what you refuse to do as what you do. These are the patterns Morton Software Group explicitly avoids in production codebases — and what we use instead.
The traditional Apple MVC pattern encourages ViewControllers to handle network calls, data formatting, navigation logic, and UI updates simultaneously. In practice this produces ViewControllers with hundreds of lines of mixed-responsibility code that is impossible to test and painful to modify.
SwiftUI makes it easy to write conditional logic, data formatting, and decision trees directly in the view body. This is a trap — logic in views cannot be tested, cannot be reused, and couples the display layer to the data layer in ways that make both harder to change.
Completion-handler-based async code creates nested callback pyramids that are difficult to read, impossible to reason about, and painful to debug when errors need to propagate through multiple layers. Pre-Swift Concurrency codebases are full of this pattern.
The exclamation mark operator in Swift is a statement that a value will never be nil — a guarantee the compiler cannot verify. Every force unwrap is a deferred crash waiting for a production edge case that wasn't tested. One nil from an API response brings down the app.
Making a network request every time a view appears — including on tab switches, scroll events, and background-to-foreground transitions — creates unnecessary latency, drains battery, and hammers external APIs with redundant requests that return identical data.
Updating UI from a background thread causes runtime warnings at best and unpredictable crashes at worst — often appearing only under specific timing conditions that don't reproduce in development. The bug is architectural, not incidental.
Abstract architecture principles only matter when they are applied to real products. Here is how these specific patterns appear in Morton Software Group applications.
An environmental intelligence product integrating five federal data sources requires an architecture that can handle parallel async fetches, independent caching per data source, graceful degradation when one source fails, and a composite decision engine that synthesizes results into a single status.
The specific architectural decisions that make this work:
A precision timing game requires an architecture where game state transitions are immediate, score calculations are deterministic, and UI rendering never competes with game logic on the main thread. The architecture must guarantee that a tap at the wrong millisecond is the player's fault — not a threading issue.
The specific architectural decisions that make this work:
Architecture Cost
Compounds.
The cost of bad architecture is not paid at launch. It is paid in every feature added after launch — in the extra time it takes to implement changes safely, in the bugs introduced by modifications to code that was never designed to be modified, and in the eventual decision to rewrite rather than refactor.
Morton Software Group builds architecture for the twelfth month of a product's life, not the first. The right structure at the start is the most cost-effective decision in iOS development — because every subsequent decision is made in the context of it.