Files
InvestmentTrackerApp/PortfolioJournal/Views/Onboarding/OnboardingICloudCheckView.swift
T
alexandrev-tibco 10f6d0ca20 Release 1.2.1: iCloud sync improvements + ASO multilingual metadata
iCloud sync:
- Force viewContext.refreshAllObjects() on remote change notifications so
  data from other devices is picked up immediately without app restart
- Call refreshFromCloudKit() on foreground to merge any changes made while
  the device was inactive
- Wait up to 10s for initial CloudKit sync on launch before showing onboarding
  (shows "Checking iCloud..." during the wait)
- New OnboardingICloudCheckView: shown on fresh installs with iCloud available,
  lets user restore from iCloud before starting onboarding from scratch

Localization:
- Added de, fr, it, ja, pt-BR lproj folders
- New iCloud onboarding strings in en + es-ES (+ button literals)

ASO metadata (fastlane):
- Updated en-US: new subtitle, keywords, description (fixed "no cloud sync"
  claim), promotional text, release notes
- Added full metadata for es-ES, de-DE, fr-FR, it, ja, pt-BR (63 files total)
- All keyword fields validated ≤100 Unicode chars

Infrastructure:
- Gemfile + Gemfile.lock for fastlane
- Scripts/archive_and_upload_appstore.sh for CI/CD

Version: 1.2.1 (build 7)

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:54:35 +01:00

142 lines
4.7 KiB
Swift

import SwiftUI
/// Shown before onboarding on a fresh install when iCloud is available.
/// Lets the user choose between restoring from iCloud or starting fresh.
struct OnboardingICloudCheckView: View {
/// Called when the user decides to start fresh (no iCloud restore).
let onSkip: () -> Void
@AppStorage("cloudSyncEnabled") private var cloudSyncEnabled = false
@State private var showRestartPrompt = false
var body: some View {
if showRestartPrompt {
restartPromptView
} else {
checkView
}
}
// MARK: - Check View
private var checkView: some View {
VStack {
Spacer()
VStack(spacing: 28) {
ZStack {
Circle()
.fill(Color.appPrimary.opacity(0.12))
.frame(width: 140, height: 140)
Circle()
.fill(Color.appPrimary.opacity(0.22))
.frame(width: 100, height: 100)
Image(systemName: "icloud.fill")
.font(.system(size: 50))
.foregroundColor(.appPrimary)
}
VStack(spacing: 14) {
Text(String(localized: "icloud_check_title"))
.font(.title.weight(.bold))
.multilineTextAlignment(.center)
Text(String(localized: "icloud_check_description"))
.font(.body)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal, 40)
}
}
Spacer()
Spacer()
VStack(spacing: 12) {
Button {
cloudSyncEnabled = true
showRestartPrompt = true
} label: {
Label("Restore from iCloud", systemImage: "icloud.and.arrow.down")
.font(.headline)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding()
.background(Color.appPrimary)
.cornerRadius(AppConstants.UI.cornerRadius)
}
Button {
onSkip()
} label: {
Text("Start Fresh")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
.padding(.horizontal, 24)
.padding(.bottom, 40)
}
.background(AppBackground())
}
// MARK: - Restart Prompt View
private var restartPromptView: some View {
VStack {
Spacer()
VStack(spacing: 28) {
ZStack {
Circle()
.fill(Color.positiveGreen.opacity(0.12))
.frame(width: 140, height: 140)
Circle()
.fill(Color.positiveGreen.opacity(0.22))
.frame(width: 100, height: 100)
Image(systemName: "checkmark.icloud.fill")
.font(.system(size: 50))
.foregroundColor(.positiveGreen)
}
VStack(spacing: 14) {
Text(String(localized: "icloud_enabled_title"))
.font(.title.weight(.bold))
.multilineTextAlignment(.center)
Text(String(localized: "icloud_enabled_description"))
.font(.body)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal, 40)
}
}
Spacer()
Spacer()
// "Got it" just acknowledges the user must close and reopen manually.
// The button stays active so it doesn't look broken.
Button {
// No-op: user needs to close and reopen the app.
// Nothing to navigate to; this session has no CloudKit container.
} label: {
Text("Got it")
.font(.headline)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding()
.background(Color.positiveGreen)
.cornerRadius(AppConstants.UI.cornerRadius)
}
.padding(.horizontal, 24)
.padding(.bottom, 40)
}
.background(AppBackground())
}
}
#Preview {
OnboardingICloudCheckView(onSkip: {})
}