1.4.1 (build 46): fix cambio de Chart Type roto por el cache de snapshots
El fetch del cache dependía del timeRange activo (monthsLimit = timeRange.months), así que al reutilizarlo entre chart types/rangos (optimización del build 45) los charts recibían historia truncada — Year vs Year o "All" con solo 12 meses, charts vacíos o incompletos al cambiar de tipo. Fix: el cache siempre guarda la historia completa (maxHistoryMonths) y el recorte por rango se hace en memoria (el filtro por cutoffDate ya existía justo después). Bonus: hiddenHistoryMonths ahora se calcula contra la ventana completa (teaser más preciso). Verificado en iPad Pro 13" simulador: Evolution 12M → All → Compare → Period vs Period → Evolution, todos renderizan con datos. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
This commit is contained in:
@@ -459,7 +459,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = PortfolioJournal/PortfolioJournalDebug.entitlements;
|
CODE_SIGN_ENTITLEMENTS = PortfolioJournal/PortfolioJournalDebug.entitlements;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 45;
|
CURRENT_PROJECT_VERSION = 46;
|
||||||
DEVELOPMENT_ASSET_PATHS = PortfolioJournal/Assets.xcassets;
|
DEVELOPMENT_ASSET_PATHS = PortfolioJournal/Assets.xcassets;
|
||||||
DEVELOPMENT_TEAM = 2825Q76T7H;
|
DEVELOPMENT_TEAM = 2825Q76T7H;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -498,7 +498,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = PortfolioJournal/PortfolioJournal.entitlements;
|
CODE_SIGN_ENTITLEMENTS = PortfolioJournal/PortfolioJournal.entitlements;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 45;
|
CURRENT_PROJECT_VERSION = 46;
|
||||||
DEVELOPMENT_ASSET_PATHS = PortfolioJournal/Assets.xcassets;
|
DEVELOPMENT_ASSET_PATHS = PortfolioJournal/Assets.xcassets;
|
||||||
DEVELOPMENT_TEAM = 2825Q76T7H;
|
DEVELOPMENT_TEAM = 2825Q76T7H;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
@@ -655,7 +655,7 @@
|
|||||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||||
CODE_SIGN_ENTITLEMENTS = PortfolioJournalWidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = PortfolioJournalWidgetExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 45;
|
CURRENT_PROJECT_VERSION = 46;
|
||||||
DEVELOPMENT_ASSET_PATHS = PortfolioJournalWidget/Assets.xcassets;
|
DEVELOPMENT_ASSET_PATHS = PortfolioJournalWidget/Assets.xcassets;
|
||||||
DEVELOPMENT_TEAM = 2825Q76T7H;
|
DEVELOPMENT_TEAM = 2825Q76T7H;
|
||||||
GENERATE_INFOPLIST_FILE = NO;
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
@@ -688,7 +688,7 @@
|
|||||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||||
CODE_SIGN_ENTITLEMENTS = PortfolioJournalWidgetExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = PortfolioJournalWidgetExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 45;
|
CURRENT_PROJECT_VERSION = 46;
|
||||||
DEVELOPMENT_ASSET_PATHS = PortfolioJournalWidget/Assets.xcassets;
|
DEVELOPMENT_ASSET_PATHS = PortfolioJournalWidget/Assets.xcassets;
|
||||||
DEVELOPMENT_TEAM = 2825Q76T7H;
|
DEVELOPMENT_TEAM = 2825Q76T7H;
|
||||||
GENERATE_INFOPLIST_FILE = NO;
|
GENERATE_INFOPLIST_FILE = NO;
|
||||||
@@ -719,7 +719,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 45;
|
CURRENT_PROJECT_VERSION = 46;
|
||||||
DEVELOPMENT_TEAM = 2825Q76T7H;
|
DEVELOPMENT_TEAM = 2825Q76T7H;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
@@ -743,7 +743,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 45;
|
CURRENT_PROJECT_VERSION = 46;
|
||||||
DEVELOPMENT_TEAM = 2825Q76T7H;
|
DEVELOPMENT_TEAM = 2825Q76T7H;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
@@ -766,7 +766,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 45;
|
CURRENT_PROJECT_VERSION = 46;
|
||||||
DEVELOPMENT_TEAM = 2825Q76T7H;
|
DEVELOPMENT_TEAM = 2825Q76T7H;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
@@ -789,7 +789,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 45;
|
CURRENT_PROJECT_VERSION = 46;
|
||||||
DEVELOPMENT_TEAM = 2825Q76T7H;
|
DEVELOPMENT_TEAM = 2825Q76T7H;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
|
|||||||
@@ -448,17 +448,19 @@ class ChartsViewModel: ObservableObject {
|
|||||||
sources = sourceRepository.sources.filter { shouldIncludeSource($0) }
|
sources = sourceRepository.sources.filter { shouldIncludeSource($0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
let monthsLimit = timeRange.months ?? maxHistoryMonths
|
|
||||||
let sourceIds = sources.compactMap { $0.id }
|
let sourceIds = sources.compactMap { $0.id }
|
||||||
|
|
||||||
// Performance: Reuse cached snapshots when possible
|
// Performance: cache the FULL history once and slice by time range in memory.
|
||||||
|
// The cache is shared across chart types and ranges, so it must never be
|
||||||
|
// fetched with a range-dependent months limit (that truncated Year vs Year
|
||||||
|
// and "All" after visiting a chart with a shorter range).
|
||||||
var snapshots: [Snapshot]
|
var snapshots: [Snapshot]
|
||||||
if let cached = cachedSnapshots {
|
if let cached = cachedSnapshots {
|
||||||
snapshots = cached
|
snapshots = cached
|
||||||
} else {
|
} else {
|
||||||
snapshots = snapshotRepository.fetchSnapshots(
|
snapshots = snapshotRepository.fetchSnapshots(
|
||||||
for: sourceIds,
|
for: sourceIds,
|
||||||
months: monthsLimit
|
months: maxHistoryMonths
|
||||||
)
|
)
|
||||||
hiddenHistoryMonths = freemiumValidator.hiddenHistoryMonths(in: snapshots)
|
hiddenHistoryMonths = freemiumValidator.hiddenHistoryMonths(in: snapshots)
|
||||||
snapshots = freemiumValidator.filterSnapshots(snapshots)
|
snapshots = freemiumValidator.filterSnapshots(snapshots)
|
||||||
|
|||||||
@@ -151,10 +151,42 @@ final class PortfolioJournalUITests: XCTestCase {
|
|||||||
}
|
}
|
||||||
Thread.sleep(forTimeInterval: 3.0)
|
Thread.sleep(forTimeInterval: 3.0)
|
||||||
|
|
||||||
let shot = XCTAttachment(screenshot: capture.screenshot())
|
func snap(_ name: String) {
|
||||||
shot.name = "Charts_Redesign"
|
let shot = XCTAttachment(screenshot: capture.screenshot())
|
||||||
shot.lifetime = .keepAlways
|
shot.name = name
|
||||||
add(shot)
|
shot.lifetime = .keepAlways
|
||||||
|
add(shot)
|
||||||
|
}
|
||||||
|
|
||||||
|
snap("01_Evolution_default")
|
||||||
|
|
||||||
|
// Switch period to All (exercises the shared snapshot cache across ranges)
|
||||||
|
let allSegment = capture.buttons["All"].firstMatch
|
||||||
|
if allSegment.waitForExistence(timeout: 3) {
|
||||||
|
allSegment.tap()
|
||||||
|
Thread.sleep(forTimeInterval: 2.0)
|
||||||
|
snap("02_Evolution_All")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch chart type to a free multi-control chart and back (cache reuse path)
|
||||||
|
let compareTile = capture.buttons["Compare"].firstMatch
|
||||||
|
if compareTile.waitForExistence(timeout: 3) {
|
||||||
|
compareTile.tap()
|
||||||
|
Thread.sleep(forTimeInterval: 2.0)
|
||||||
|
snap("03_Compare")
|
||||||
|
}
|
||||||
|
let periodTile = capture.buttons["Period vs Period"].firstMatch
|
||||||
|
if periodTile.waitForExistence(timeout: 3) {
|
||||||
|
periodTile.tap()
|
||||||
|
Thread.sleep(forTimeInterval: 2.0)
|
||||||
|
snap("04_PeriodVsPeriod")
|
||||||
|
}
|
||||||
|
let evolutionTile = capture.buttons["Evolution"].firstMatch
|
||||||
|
if evolutionTile.waitForExistence(timeout: 3) {
|
||||||
|
evolutionTile.tap()
|
||||||
|
Thread.sleep(forTimeInterval: 2.0)
|
||||||
|
snap("05_Evolution_back")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Captures full-screen screenshots of the main tabs with demo data for App Store
|
/// Captures full-screen screenshots of the main tabs with demo data for App Store
|
||||||
|
|||||||
Reference in New Issue
Block a user