This page describes how the WiseInvest iOS app is structured internally: SwiftUI entry points, dependency injection, services, data flow from backends to views, and how new features should hook into existing layers.
Source: WiseInvest/WiseInvestApp.swift, WiseInvest/ContentView.swift.
WiseInvestApp is the @main entry point and owns app-wide state objects:
StocksService — stock list/detail and live quote service.AuthStore — Cognito-backed authentication state.UserDataStore — user-scoped data store for watchlist, wealth dashboard, EPF data, and retirement info..environment(...) so any descendant view can access them via @Environment.WiseInvestApp also:
.preferredColorScheme(.dark)..tint(Theme.primary).authStore.bootstrap() once on launch using .task to restore any saved session.ContentView is the shell that decides which top-level content to show:
OPEN_STOCK environment variable is set, wraps StockDetailView in a NavigationStack and shows it directly (a development deep-link).TabView with five tabs:
HomeView (marketing/landing page).StocksListView (stock discovery and navigation into StockDetailView).WealthDashboardView (wealth overview, auth-gated with demo support).WatchlistView (synced, live-quoted watchlist, auth-gated).ProfileView (profile/settings placeholder).OPEN_TAB (via SIMCTL_CHILD_OPEN_TAB), which is helpful for iterative development.The view layer follows a SwiftUI-first composition approach with thin logic; heavy lifting happens in services and models.
Source: WiseInvest/Theme.swift, WiseInvest/Views/HeaderView.swift, WiseInvest/Views/WiseInvestWordmark.swift, StockPageTheme and shared Panel/TagChip components under Views/StockDetail/*.
Theme exposes app-wide colors (primary, background, card, border, etc.) and typography decisions that keep the home and stocks screens aligned with the web brand.HeaderView implements a reusable header bar with title, subtitle, and trailing actions; used in HomeView and StocksListView.StockPageTheme / StockTheme define colors and surfaces for the stock-detail page (paper background, surface cards, ink levels, gain/loss colors).Panel, TagChip, FlowLayout, and other helpers encapsulate common layout/visual patterns.HomeView — static marketing, minimal state, no backend calls.StocksListView — reads stocks from StocksService, manages list UI, search, sorting, and navigation.StockDetailView — reads detail/history from StocksService, merges in live quote data, coordinates auth-gated actions (watchlist, add to portfolio), and composes many sub-cards.WealthDashboardView — reads wealth, EPF, and retirement data from UserDataStore, switching between live vs demo payloads and showing error banners when wealth backend calls fail.WatchlistView — reads/syncs watchlist and dashboard state from UserDataStore, uses StocksService for batched live quotes, and owns UI state for quote loading and removal actions.LoginView / LoginRequiredView — orchestrate auth flows and gating while delegating API work to AuthStore and CognitoAuth.All of these screens stay relatively thin by pushing network, parsing, and persistence work into dedicated services and models described next.
Source: WiseInvest/Services/APIClient.swift.
Role:
Key details:
bucket = "s3financialvisualizationdata11322-dev" and region = "ap-south-1" to build bucketBase.stockDataBase for stock_page_data.logoURL(for:), logoPNGURL(for:), historyURL(for:), stockDetailURL(for:).fetchData(_ url: URL, cacheBust: Bool):
_t query parameter and uses .reloadIgnoringLocalCacheData for metadata endpoints that change in place.OSLog (apiLog).fetchJSON<T: Decodable>(_ url: URL, sanitizeNaN: Bool, cacheBust: Bool):
fetchData.: NaN with : null (to keep JSONDecoder happy when S3 objects contain unquoted NaN values).Source: WiseInvest/Services/StocksService.swift.
Role:
Responsibility mapping:
List view data (StocksListView):
stocks(for index: StockIndex) async throws -> [Stock].cache[index.value] exists, return it.IndexMetadata from metadata/{index.metadataFile} using APIClient.bucketBase.metadata.latestFilePath (S3-style URI) into a bucket key.IndexTreeRoot.[Stock] with formatted fields.Stock detail (StockDetailView):
detail(for symbol: String) and history(for symbol: String) fetch their respective JSON payloads from S3 using APIClient and StockDetail / StockHistory.mergedDetail(for symbol: String)/liveQuote(for symbol: String) combine static detail with live heatmap data:
StockDetail from the live node.isLiveData = true with a liveTimestamp.Heatmap/live quotes (WatchlistView, StockDetailView):
treeCache and dumpCache with a TTL (dumpTTL = 5 minutes).nifty50Tree():
APIClient.bucketBase/metadata/nifty50_metadata.json and HeatmapMetadata.bucketKey to locate the tree.liveQuote(for symbol:) first searches the NIFTY 50 tree, then falls back to the full-market dump.quotes(for symbols:) is purpose-built for watchlist use:
dumpQuote.The views never form URLs or parse S3 paths directly; they depend entirely on StocksService and models.
Source: WiseInvest/Services/CognitoAuth.swift, WiseInvest/Services/AuthStore.swift, WiseInvest/Views/Auth/LoginView.swift.
The authentication stack is split into:
CognitoAuth — stateless, low-level client that understands Cognito's REST API and PKCE flows.AuthStore — stateful, observable store that the UI talks to.CognitoConfig): region, user pool ID, client ID, IDP endpoint URL, OAuth domain, redirect URI, and scopes.call(_ target: String, _ body: [String: Any]):
X-Amz-Target to the IDP operation (e.g. AWSCognitoIdentityProviderService.InitiateAuth).JSONSerialization for request/response.__type and message fields and wraps them as AuthError.cognito(type:message:) with user-friendly messages.tokens(from:existingRefresh:) builds AuthTokens from Cognito responses, capturing expiry and refresh token.signIn and refresh operations.signIn, refresh, signUp, confirmSignUp, resendConfirmationCode, forgotPassword, confirmForgotPassword.getUser(accessToken:) for fetching user profile attributes into a UserProfile struct.globalSignOut.googleAuthURL(codeVerifier:state:) builds a PKCE-compliant OAuth URL against the Cognito Hosted UI.makeCodeVerifier() generates a URL-safe, base64-encoded verifier string.exchangeCode(_:codeVerifier:) exchanges an authorization code + verifier for tokens.State enum and optional AuthTokens.bootstrap() is intended to be called once on launch:
TokenStore.load().CognitoAuth.refresh.UserProfile via CognitoAuth.getUser.signedOut if anything fails.validAccessToken() is the main API used by other services:
nil.CognitoAuth.refresh).signIn, adopt(tokens:), and signOut wrap CognitoAuth calls and are invoked from LoginView.Design considerations:
CognitoAuth and AuthStore boundary; other features (watchlist, wealth) only care about whether a valid accessToken is available.AuthStore.profile.Source: WiseInvest/Services/WealthAPI.swift, WiseInvest/Models/WealthData.swift, WiseInvest/Models/EPFData.swift, UserDataStore.
WealthAPI is the sole entry point to https://wealth.wiseinvestbackend.in.
Authorization: Bearer header with a token sourced from AuthStore.validAccessToken().Decodable structs close to the raw API responses.CodingKeys.fetchDashboard(token:) function encapsulates the full multi-endpoint fetch and mapping into WealthData.UserDataStore (see Services/UserDataService.swift) orchestrates:
WealthDashboardView when authenticated).reloadDashboard(auth:).WealthData and WealthAPI.RetirementSummary.dashboardLoaded, dashboardError, and liveWealth to the UI.EPFData).WatchlistView and StockDetailView.Design-wise, this keeps backend specifics and mapping logic centralized; WealthDashboardView and related cards remain simple consumers of a single WealthData value.
This section highlights how to safely evolve key areas.
ContentView.Tab with a new case and update the TabView body to include a new view.WiseInvest/Views/ for the new tab.@Environment(StocksService.self), @Environment(AuthStore.self), or @Environment(UserDataStore.self) as needed rather than adding new singletons.StockDetail, StockHistory, LiveQuote, or Index* models with @Flex/@Safe wrappers.StocksService instead of calling APIClient directly from views.APIClient.Decodable model in WiseInvest/Models.StocksService method that fetches & caches the new data.WealthAPI response models with new fields and map them into WealthData in assemble(...).WiseInvest/Views/Wealth/ that take WealthData slices.WealthDashboardView focused on layout and gating, not raw JSON parsing.AuthError mappings to keep user-facing error messages accurate.UserProfile and AuthTokens if Cognito responses change.LoginView copy and fields, but keep the underlying state machine (Mode/Step) intact where possible.SIMCTL_CHILD_OPEN_TAB and SIMCTL_CHILD_OPEN_STOCK when running via xcrun simctl or Xcode's environment overrides to jump directly into the area you are iterating on.HomeView, StocksListView, WatchlistView, LoginView, WealthDashboardView cards) and generally inject lightweight demo services like StocksService() or .demo data.