Skip to content
Platforms & CRMPrototype2026

Evolve Estates iOS

A native iOS shell for a Florida real-estate brokerage, hosting the Dream Home Designer web app, buyer calculators, accounts, and a client portal, not yet in the App Store.

156
tests across Swift package, app, UI and Node layers
9
buyer calculators ported from the web app's JS engine
32
fixture cases cross-checking the Swift port against the JS engine's own output
ArchitecturePrivate internal tool
  1. 01
    Interface
    • Swift
    • SwiftUI
    • Swift Package Manager
  2. 02
    Edge
    • Cloudflare Pages Functions (cons…
  3. 03
    Application
    • App Store Connect API
    • Node.js (fixture generation, con…
  4. 04
    Operations
    • XCTest / XCUITest
  5. 05
    Supporting
    • WKWebView
    • xcodegen
    • Keychain Services

Evolve Estates iOS holds live data, so this shows the verified technology stack by layer rather than a screenshot. Layers, not connections — which service calls which is not something a dependency list can prove. Hosts, ports and topology are deliberately absent.

Problem

A Florida brokerage's marketing videos drive traffic to a web app, Dream Home Designer, that already converts a viewer to a lead in under thirty seconds with no install step. The repo's own governing spec records the counter-case in detail before building anything: comparable real-estate agent apps hold a tiny fraction of their user base as App Store reviews, and a large national real-estate marketing platform spent roughly a billion dollars on brand marketing across 2024 and 2025 before cutting hundreds of millions a year in spend because it eroded profit. Building a native app as a lead-generation bet is a weak business case on the evidence available, and the spec says so before laying out any architecture.

The reason to build anyway is narrower: an install gives the brokerage a persistent surface for video calls to action that a bookmarked URL does not, and a native shell can host phases the web app cannot promise on its own, like a client portal and later seller and investor tools, without a second web codebase. The spec treats that as a bet worth testing in parallel with a cheaper alternative, a web-only conversion path on the existing site measuring whether an app produces more leads than the website already does, rather than as a foregone conclusion. This project is the native side of that bet, ordered so the reusable, Apple-independent work (the calculators, the avatar router, the lead client) ships value even if the app itself never reaches the App Store.

What was built

Four tabs render in SwiftUI: Home is the hub with an avatar-aware greeting and entry points to the other tools; Design loads the live Dream Home Designer inside a WKWebView, authenticated through a 60-second handoff code when the device holds a token and anonymous otherwise; Tools is a segmented shelf of monthly-payment and affordability calculators built on a Swift package, BuyerCalculators, that reproduces the web app's JS math statement for statement; Account handles magic-link sign-in, sign-out on the device, and account deletion. A fifth tab, My Home, renders only for a signed-in account whose /api/me response carries a live portal grant, and reuses the same WebView type as the Design tab rather than a second implementation.

Every network request carries an app-key header and a client identifier, and the bearer token lives in the Keychain with the most restrictive accessibility class rather than in UserDefaults or a plist. County tax and insurance rates are fetched from the web app's Cloudflare origin at launch and cached, with a bundled JSON file as a cold-offline fallback that a test pins byte-equal to the source data file in the sibling web repo, so the two copies cannot silently drift. County data ships from the origin rather than the binary specifically so a millage change reaches users without an App Store release.

Technical approach

The calculator port is built to be provably identical to the web version rather than merely similar. Sources/BuyerCalculators is pure Swift with no network or UI dependency, every Florida constant is injected by the caller exactly as the JavaScript version does it, and a separate command-line harness, calc-check, replays a 32-case fixture generated by actually running the JS engine and comparing the Swift output at roughly four units in the last place. The rule stated in the README is that any change to the JS engine requires regenerating the fixture and rerunning the check before the two are trusted to agree again; a mismatch on a case that was not a deliberate JS change means the web and native calculators have diverged, which the repo calls the exact failure this package exists to prevent.

The most consequential engineering decision in the repo is a security fix, not a feature. The sign-in link parser, NativeAPI.token(fromSignInLink:), is the only gate between a URL a person was handed and an authentication credential, and the first implementation checked only the callback path and the token's character set, not the link's host. A link on an attacker's own domain carrying the attacker's own valid magic-link token passed that check, was exchanged with the real backend, and signed the victim into the attacker's account, a forced-login vulnerability the design spec documents by name with the exact malicious URL shape that exposes it (the real host of a URL with an @ in it is the part after the @, not before). The fix, and the invariant now stated for any future auth entry point, is that a sign-in link must be https and match the app's configured origin host, case-insensitively, before its token is ever exchanged. Two related gaps were closed in the same pass: the WebView's JavaScript bridge originally acted on messages from any frame on any origin, and an off-origin page reached by a scripted redirect, rather than a tapped link, was not diverted to Safari. Both decisions now live as pure, unit-testable functions in App/WebPolicy.swift, specifically because the WebKit types that would otherwise carry this logic, WKScriptMessage and WKFrameInfo, cannot be constructed inside a test, and a security policy that only exists inside a delegate callback is a policy nothing can verify.

A second engineering constraint runs through the client-portal feature: a native build must never be able to show a user a capability they are not entitled to, even by forcing a debug flag. The My Home tab's presence is driven by a server-reported portal flag on the signed-in account, and a -portal true launch argument used for automation can force the flag locally, but cannot actually unlock the underlying web routes, which independently refuse a caller with no real grant. The repo treats this as the reason the debug override is safe to ship: the worst it can do is show an empty tab, never real client data. Every stated invariant in the repo, including this one, the host-pinning check, and a rule that a build with no app key must send zero leads to the production CRM, carries a note that it was mutation-tested: the check was deliberately deleted to confirm the test suite goes red, then restored.

Creative approach

Craft

The app inherited a naming mistake and the correction is treated as load-bearing enough to document at length rather than quietly fixed. An early pass named the product after its most visible feature, Dream Home Designer, and wrote both the in-app copy and the App Store listing draft around floor plans. HANDOFF.md and the listing draft both record the correction and the reasoning: the app is the hub for the brokerage's video-driven audience, of which the Designer is one entry point among calculators, accounts, and a planned client portal, seller tools, and property search. Naming the whole app after one feature would have boxed out every later phase and forfeited any App Store ranking the earlier name accumulated if it were ever renamed. The fix was more than a string change: five separate mentions inside the app's copy and listing draft referred back to the old framing and had to be corrected together so no page stated something false about what the app was called.

Reframe

The product insight that reordered the whole build plan is that the app's headline feature does not work on the device the app ships on. The design spec's own verification found that the embedded 2D floor-plan editor is desktop-first: it shows a banner telling touch users to find a desk, tracks only a single pointer, and zooms only on a trackpad's ctrl-plus-wheel gesture, which a phone's two-finger pinch never fires. That finding, sourced from reading the actual editor code rather than assuming the web app 'just works' in a WebView, became the single hard prerequisite the spec puts ahead of every other native task: touch gestures had to be built into the shared web editor first, in the sibling repo, before the native shell's headline tab was usable for its own users on their own devices.

Process and what failed

The account-deletion copy shipped, then was corrected under a stated new rule: any future edit to what the deletion dialog claims needs a human sign-off, not just a passing test suite, because the sentence has to be legally accurate about what a seven-year transaction record retention means and a green test only proves the words match a blocklist, not that the claim is true. A related decision was made and then explicitly reversed on the record: the original spec absolutely banned collecting a phone number, and the owner overrode that the same day the spec was written, choosing to accept a call-back number in exchange for adding the associated legal consent language rather than keeping the stricter rule. The design spec preserves the original rule and its reversal side by side, with an instruction not to "restore" the no-phone rule later, because a future session reading only the older sentence would otherwise silently regress a decision that was already made deliberately.

Outcome

The app is not on the App Store. The developer account is active; what stands between the build and submission are owner steps recorded in the handoff: completing signing setup, creating the App Store Connect app record, and adding the support mail aliases Apple's review uses. No signing certificate or provisioning profile exists yet. A first Simulator build succeeded, all four tabs render, and the app functions signed out and, with a locally configured app key, signed in.

A client-portal branch landed four of its six planned tasks: the server-driven portal flag, the My Home tab itself, the portal screen, and corrected deletion copy, closing one specific App Store review scenario (that the tab must be absent, not merely disabled, for an account without a grant) with a dedicated UI test. Two tasks on that branch are explicitly blocked, not skipped: removing analytics declarations from the app's privacy manifest is deliberately held until a corresponding analytics-suppression change in the sibling web repo is actually deployed to production, not merely merged, and an end-to-end verification checklist has nothing to check until that same deploy and a set of owner-controlled flags exist. A submission pack, including App Store listing copy, review notes, and real device screenshots, is written and ready, but no screenshot yet shows a calculator's result on screen, a gap the repo attributes to a Simulator scripting limitation rather than a missing feature. The client portal is explicitly scoped out of the 1.0 release regardless of when Apple activation completes.