Actualización sin cambiar de app: Share Extension "Quick Update" + clipboard auto-advance

El dolor: para actualizar cada source hay que saltar a la app del banco, copiar/
memorizar el valor y volver. iOS no permite ventanas flotantes editables (PiP es
solo vídeo), así que se resuelve con las dos vías sancionadas:

Share Extension (target nuevo PortfolioJournalQuickUpdate):
- Al compartir texto desde CUALQUIER app aparece "Portfolio Journal": mini-form
  sobre la app anfitriona con el importe pre-parseado del texto compartido y la
  siguiente source pendiente del mes preseleccionada. Guardar → sigues en el banco.
- Sin Core Data en la extensión: la app publica un espejo de sources activas en el
  App Group (SharedQuickUpdateSync.refreshMirror) y la extensión encola
  PendingQuickUpdate; la app los convierte en Snapshots reales al activarse
  (ingestPending). ExtSharedBridge duplica el contrato (keep-in-sync comentado).
- Parser de importes tolerante a locales (12.345,67 € / $1,234.56 / 1234,5).
- UI localizada en los 7 idiomas (lproj propios de la extensión).
- pbxproj: target app-extension completo (sync group, embed, dependency, configs)
  replicando el patrón del widget. Entitlements solo con App Group.

Clipboard auto-advance en Quick Update:
- Al volver a la app con un importe copiado: banner de un tap "Pegar X en <source>"
  que rellena la siguiente source vacía y avanza el foco a la siguiente.
- Badge NEXT en la fila activa; no repite sugerencias del mismo clipboard.

Script de upload: -allowProvisioningUpdates + API key también en el archive para
que el bundle id nuevo de la extensión se registre automáticamente.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
This commit is contained in:
alexandrev-tibco
2026-07-03 23:21:55 +02:00
parent f7d4fdbe2d
commit 386f4ff265
24 changed files with 836 additions and 3 deletions
+145
View File
@@ -15,6 +15,7 @@
0E53752F2F0FD09F00F31390 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E53752E2F0FD09F00F31390 /* CoreData.framework */; };
0E5375312F0FD12E00F31390 /* GoogleMobileAds in Frameworks */ = {isa = PBXBuildFile; productRef = 0E5375302F0FD12E00F31390 /* GoogleMobileAds */; };
0E5375352F0FD14000F31390 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 0E5375342F0FD14000F31390 /* FirebaseCrashlytics */; };
0EQUPD102F40000000000001 /* PortfolioJournalQuickUpdate.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 0EQUPD012F40000000000001 /* PortfolioJournalQuickUpdate.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -39,6 +40,13 @@
remoteGlobalIDString = 0E241E382F0DA93A00283E2F;
remoteInfo = PortfolioJournal;
};
0EQUPD112F40000000000001 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0E241E312F0DA93A00283E2F /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0EQUPD022F40000000000001;
remoteInfo = PortfolioJournalQuickUpdate;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -49,6 +57,7 @@
dstSubfolderSpec = 13;
files = (
0E241EE22F0DAA3E00283E2F /* PortfolioJournalWidgetExtension.appex in Embed Foundation Extensions */,
0EQUPD102F40000000000001 /* PortfolioJournalQuickUpdate.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
@@ -64,6 +73,8 @@
0E53752E2F0FD09F00F31390 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
0ETEST0002F31000000000001 /* PortfolioJournalTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PortfolioJournalTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
0EUITEST002F31000000001 /* PortfolioJournalUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PortfolioJournalUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
0EQUPD012F40000000000001 /* PortfolioJournalQuickUpdate.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = PortfolioJournalQuickUpdate.appex; sourceTree = BUILT_PRODUCTS_DIR; };
0EQUPD142F40000000000001 /* PortfolioJournalQuickUpdateExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PortfolioJournalQuickUpdateExtension.entitlements; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
@@ -88,6 +99,13 @@
);
target = 0E241ECB2F0DAA3C00283E2F /* PortfolioJournalWidgetExtension */;
};
0EQUPD132F40000000000001 /* Exceptions for "PortfolioJournalQuickUpdate" folder in "PortfolioJournalQuickUpdate" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = 0EQUPD022F40000000000001 /* PortfolioJournalQuickUpdate */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
@@ -118,6 +136,14 @@
path = PortfolioJournalUITests;
sourceTree = "<group>";
};
0EQUPD032F40000000000001 /* PortfolioJournalQuickUpdate */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
0EQUPD132F40000000000001 /* Exceptions for "PortfolioJournalQuickUpdate" folder in "PortfolioJournalQuickUpdate" target */,
);
path = PortfolioJournalQuickUpdate;
sourceTree = "<group>";
};
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
@@ -156,6 +182,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
0EQUPD052F40000000000001 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -163,8 +196,10 @@
isa = PBXGroup;
children = (
0E241EED2F0DAC7D00283E2F /* PortfolioJournalWidgetExtension.entitlements */,
0EQUPD142F40000000000001 /* PortfolioJournalQuickUpdateExtension.entitlements */,
0E241E3B2F0DA93A00283E2F /* PortfolioJournal */,
0E241ED22F0DAA3C00283E2F /* PortfolioJournalWidget */,
0EQUPD032F40000000000001 /* PortfolioJournalQuickUpdate */,
0ETEST0032F31000000000001 /* PortfolioJournalTests */,
0EUITEST032F31000000001 /* PortfolioJournalUITests */,
0E241ECD2F0DAA3C00283E2F /* Frameworks */,
@@ -177,6 +212,7 @@
children = (
0E241E392F0DA93A00283E2F /* PortfolioJournal.app */,
0E241ECC2F0DAA3C00283E2F /* PortfolioJournalWidgetExtension.appex */,
0EQUPD012F40000000000001 /* PortfolioJournalQuickUpdate.appex */,
0ETEST0002F31000000000001 /* PortfolioJournalTests.xctest */,
0EUITEST002F31000000001 /* PortfolioJournalUITests.xctest */,
);
@@ -210,6 +246,7 @@
);
dependencies = (
0E241EE12F0DAA3E00283E2F /* PBXTargetDependency */,
0EQUPD122F40000000000001 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
0E241E3B2F0DA93A00283E2F /* PortfolioJournal */,
@@ -293,6 +330,28 @@
productReference = 0EUITEST002F31000000001 /* PortfolioJournalUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
0EQUPD022F40000000000001 /* PortfolioJournalQuickUpdate */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0EQUPD072F40000000000001 /* Build configuration list for PBXNativeTarget "PortfolioJournalQuickUpdate" */;
buildPhases = (
0EQUPD042F40000000000001 /* Sources */,
0EQUPD052F40000000000001 /* Frameworks */,
0EQUPD062F40000000000001 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
0EQUPD032F40000000000001 /* PortfolioJournalQuickUpdate */,
);
name = PortfolioJournalQuickUpdate;
packageProductDependencies = (
);
productName = PortfolioJournalQuickUpdate;
productReference = 0EQUPD012F40000000000001 /* PortfolioJournalQuickUpdate.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -309,6 +368,9 @@
0E241ECB2F0DAA3C00283E2F = {
CreatedOnToolsVersion = 26.2;
};
0EQUPD022F40000000000001 = {
CreatedOnToolsVersion = 26.2;
};
0ETEST0012F31000000000001 = {
CreatedOnToolsVersion = 26.2;
TestTargetID = 0E241E382F0DA93A00283E2F;
@@ -341,6 +403,7 @@
targets = (
0E241E382F0DA93A00283E2F /* PortfolioJournal */,
0E241ECB2F0DAA3C00283E2F /* PortfolioJournalWidgetExtension */,
0EQUPD022F40000000000001 /* PortfolioJournalQuickUpdate */,
0ETEST0012F31000000000001 /* PortfolioJournalTests */,
0EUITEST012F31000000001 /* PortfolioJournalUITests */,
);
@@ -376,6 +439,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
0EQUPD062F40000000000001 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -407,6 +477,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
0EQUPD042F40000000000001 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -448,6 +525,11 @@
target = 0E241E382F0DA93A00283E2F /* PortfolioJournal */;
targetProxy = 0E481F2D2F2E958100CF94C5 /* PBXContainerItemProxy */;
};
0EQUPD122F40000000000001 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0EQUPD022F40000000000001 /* PortfolioJournalQuickUpdate */;
targetProxy = 0EQUPD112F40000000000001 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
@@ -808,6 +890,60 @@
};
name = Release;
};
0EQUPD082F40000000000001 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = PortfolioJournalQuickUpdateExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 46;
DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = PortfolioJournalQuickUpdate/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Portfolio Journal";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.4.1;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournal.QuickUpdate;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
0EQUPD092F40000000000001 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = PortfolioJournalQuickUpdateExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 46;
DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = PortfolioJournalQuickUpdate/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Portfolio Journal";
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.4.1;
PRODUCT_BUNDLE_IDENTIFIER = com.alexandrevazquez.PortfolioJournal.QuickUpdate;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -856,6 +992,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0EQUPD072F40000000000001 /* Build configuration list for PBXNativeTarget "PortfolioJournalQuickUpdate" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0EQUPD082F40000000000001 /* Debug */,
0EQUPD092F40000000000001 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */