Files
InvestmentTrackerApp/CLAUDE.md
T
alexandrev-tibco 7d2f605c16 quitar AdMob, Firebase Analytics y ATT de la app
"Portfolio Journal sin AdMob ni Google: es la única forma de que 'sin
rastreo' sea verdad, y el ingreso por anuncios es despreciable" — la ficha
de App Store prometía "sin analíticas, sin rastreo, sin venta de datos"
mientras la app enlazaba GoogleMobileAds y FirebaseAnalytics, pedía permiso
de rastreo al arrancar y mandaba el importe del saldo a GA4 en el evento
snapshot_added.

SDKs fuera del proyecto (project.pbxproj): paquetes firebase-ios-sdk y
swift-package-manager-google-mobile-ads, productos FirebaseCore,
FirebaseAnalytics, GoogleMobileAds y FirebaseCrashlytics, más la fase de
build "Upload dSYMs to Crashlytics". Package.resolved se queda sin nada que
resolver. Con la fase de script fuera, ENABLE_USER_SCRIPT_SANDBOXING vuelve
a YES en el target app (estaba en NO solo por Crashlytics).

Código borrado:
- Services/AdMobService.swift entero — con él se van el flujo UMP,
  BannerAdView, BannerAdCoordinator y la llamada a
  ATTrackingManager.requestTrackingAuthorization().
- Services/FirebaseService.swift entero y sus 40 llamadas. Se borra en vez
  de dejarse como capa vacía: una clase llamada FirebaseService en una app
  que presume de no llevar Firebase es exactamente la clase de detalle que
  vuelve a colarse en una auditoría dentro de un año.
- El banner y su safeAreaInset en ContentView (bannerInsetView): las cinco
  pestañas ya no reservan 50 pt al pie.
- La entrada "Manage Ad Consent" de Ajustes y el @EnvironmentObject
  adMobService de SettingsView, ContentView y PortfolioJournalApp.
- AppConstants: bloque AdMob, bannerAdHeight, adConsentObtained,
  Features.enableAnalytics.
- SettingsViewModel.toggleAnalytics y analyticsEnabled — el flag no estaba
  conectado a nada ni tenía control en la interfaz. El atributo
  AppSettings.enableAnalytics se queda en CoreData con un comentario: no
  vale la pena una migración y un deploy de esquema CloudKit por borrarlo.
- Premium ya no promete "sin anuncios": fuera PremiumFeature.noAds, la
  entrada de IAPService.premiumFeatures y paywallBenefits, y las claves
  feature_no_ads / paywall_benefit_noads de los 7 idiomas. No se toca ni el
  precio ni el producto.

Info.plist: fuera NSUserTrackingUsageDescription, GADApplicationIdentifier,
GADDelayAppMeasurementInit y los 65 SKAdNetworkItems. Borrados también
GoogleService-Info.plist y los scripts Scripts/analyze_ga4.py y
Scripts/analyze_crashlytics.py, que ya no tienen de dónde leer.

Closes #50

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F1u4K16xy7eQVtgsYNZ9Vn
2026-09-18 16:09:04 +02:00

5.0 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Portfolio Journal is a native iOS investment portfolio tracker built with Swift and SwiftUI. It helps users track investments, monitor performance with charts and predictions, set financial goals, and maintain an investment journal.

Target: iOS 17.6+ (widget supports iOS 16.0+)

Build Commands

# Open project in Xcode
open PortfolioJournal.xcodeproj

# Build for Debug
xcodebuild -scheme PortfolioJournal -configuration Debug build

# Build for Release
xcodebuild -scheme PortfolioJournal -configuration Release build

# Run on simulator
xcodebuild -scheme PortfolioJournal -configuration Debug -destination 'platform=iOS Simulator,name=iPhone 15' build

# Clean build
xcodebuild -scheme PortfolioJournal clean

Architecture

The app uses Clean Architecture with MVVM pattern:

PortfolioJournal/
├── App/                    # Entry point: PortfolioJournalApp.swift, AppDelegate, ContentView
├── Models/CoreData/        # Core Data entities and CoreDataStack singleton
├── Repositories/           # Data access layer with @MainActor CRUD operations
├── Services/               # Business logic (CalculationService, PredictionEngine, IAPService, etc.)
├── ViewModels/             # MVVM view models for each feature
├── Views/                  # SwiftUI views organized by feature
│   ├── Dashboard/          # Main dashboard with evolution charts
│   ├── Charts/             # Financial visualizations (allocation, performance, drawdown)
│   ├── Sources/            # Investment source management
│   ├── Goals/              # Goal tracking and progress
│   ├── Journal/            # Journal entries
│   ├── Settings/           # App settings and import/export
│   ├── Security/           # Face ID/PIN lock (AppLockView)
│   └── Components/         # Shared UI components
├── Utilities/              # Helpers: KeychainService, FreemiumValidator, formatters, extensions
└── Resources/              # Info.plist, PrivacyInfo.xcprivacy, assets

PortfolioJournalWidget/     # iOS Home Screen Widget (WidgetKit)
PortfolioJournalWatch/      # watchOS app (read-only portfolio view)
PortfolioJournalWatchWidget/# watchOS complications (WidgetKit)
Shared/                     # Types shared between iOS app and watch targets

Apple Watch

The watch app and its complications are read-only; every edit still happens on the iPhone. App Groups are not shared between iOS and watchOS, so the watch cannot read the Core Data store the iOS widget reads. Instead:

  • WatchSyncService (iOS) builds a WatchPortfolioSnapshot and pushes it as the WatchConnectivity application context. It is triggered from CoreDataStack.refreshWidgetData(), the same hook that reloads the iOS widget.
  • WatchDataStore (watchOS) receives it, caches it in the watch App Group via WatchSnapshotCache, and reloads the complication timelines.
  • The complication extension only reads that cache — it never talks to WatchConnectivity itself.

Watch targets keep their own *.lproj/Localizable.strings with just their keys, like the other extensions.

Core Data Model

Key entities: Account, InvestmentSource, Snapshot, Category, Goal, Asset, Transaction, AppSettings, PremiumStatus

Data flows through CoreDataStack singleton which manages CloudKit sync and AppGroup shared container for widget access.

Key Services

  • CalculationService: Portfolio metrics, returns calculation, allocation analysis
  • PredictionEngine: Investment forecasting algorithms with caching
  • IAPService: StoreKit 2 in-app purchases
  • DiagnosticsCollector: on-device MetricKit crash/hang reports (nothing is uploaded)
  • ImportService/ExportService: CSV data import/export
  • AppLockService: Biometric/PIN security via Keychain

Dependencies

No third-party packages. The app deliberately links nothing outside Apple's SDKs: AdMob, Firebase Analytics and Crashlytics were removed in 1.7.0 so that the "no tracking, no analytics" claim on the App Store listing is literally true. Do not reintroduce an advertising, analytics or crash-reporting SDK.

Native frameworks: SwiftUI, Combine, CoreData, CloudKit, WidgetKit, StoreKit 2, LocalAuthentication, MetricKit

App Initialization Flow

PortfolioJournalApp (@main)
  └── AppDelegate (MetricKit diagnostics, Notifications init)
  └── ContentView
      ├── OnboardingView (first launch)
      ├── AppLockView (if security enabled)
      └── TabBar: Dashboard | Sources | Goals | Journal | Settings

Localization

Supported languages: English (en.lproj), Spanish (es-ES.lproj)

Development Notes

  • Use SampleDataService to generate demo data for testing
  • Premium features are gated via FreemiumValidator
  • Widget shares data through AppGroup container
  • Sensitive data stored in Keychain via KeychainService