Add debug bypass subscription toggle, tests, and data layer improvements

- Add runtime toggle in Settings (DEBUG only) to bypass subscription/hide trial banner
- IAPManager.bypassSubscription is now a @Published var persisted via UserDefaults
- Hide upgrade banner in SettingsTabView and trial warnings when bypass is enabled
- Add FeelsTests directory with integration tests
- Update DataController, DataControllerGET, DataControllerUPDATE
- Update Xcode project and scheme configuration
- Update localization strings and App Store screen docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-02-15 17:12:56 -06:00
parent 7c142568be
commit 7639f881da
14 changed files with 1064 additions and 34 deletions

View File

@@ -7,8 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
1C0DAB51279DB0FB003B1F21 /* Feels/Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Feels/Localizable.xcstrings */; };
1C0DAB52279DB0FB003B1F22 /* Feels/Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Feels/Localizable.xcstrings */; };
06E4767B5977FAC8B644FC92 /* IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CFAE86F485C853DB3239DD9 /* IntegrationTests.swift */; };
1C0DAB51279DB0FB003B1F21 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Localizable.xcstrings */; };
1C0DAB52279DB0FB003B1F22 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 1C0DAB50279DB0FB003B1F21 /* Localizable.xcstrings */; };
1C9566442EF8F5F70032E68F /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = 1C9566432EF8F5F70032E68F /* Algorithms */; };
1CB4D0A028787D8A00902A56 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CB4D09F28787D8A00902A56 /* StoreKit.framework */; };
1CD90B07278C7DE0001C4FEA /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B06278C7DE0001C4FEA /* Tests_iOS.swift */; };
@@ -24,6 +25,10 @@
1CDEFBBF2F3B8736006AE6A1 /* Configuration.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */; };
1CDEFBC02F3B8736006AE6A1 /* Configuration.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */; };
46F07FA9D330456697C9AC29 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD90B47278C7E7A001C4FEA /* WidgetKit.framework */; };
4F1C717B7747918A459322CB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4D304CD05CC7C662CCD7DCB /* Foundation.framework */; };
54259F7B3F4E959B3F4055E4 /* StreakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29E2A2FC314F88244CA946BF /* StreakTests.swift */; };
9559409B5AEEAB40EBCB6AF9 /* VoteLogicsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD717F91BD65382B7DDFE3C4 /* VoteLogicsTests.swift */; };
EEB21B1CAA8EAEB497BD9FB3 /* DataControllerCRUDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5566271983AEDF1D33C34FE6 /* DataControllerCRUDTests.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -48,6 +53,13 @@
remoteGlobalIDString = 1CD90B44278C7E7A001C4FEA;
remoteInfo = FeelsWidgetExtension;
};
A0B973C7674930232515563A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 1CD90AE6278C7DDF001C4FEA /* Project object */;
proxyType = 1;
remoteGlobalIDString = 1CD90AF4278C7DE0001C4FEA;
remoteInfo = "Feels (iOS)";
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -65,7 +77,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1C0DAB50279DB0FB003B1F21 /* Feels/Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Feels/Localizable.xcstrings; sourceTree = "<group>"; };
1C0DAB50279DB0FB003B1F21 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Feels/Localizable.xcstrings; sourceTree = "<group>"; };
1CB4D09F28787D8A00902A56 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.5.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
1CD90AF5278C7DE0001C4FEA /* Feels.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Feels.app; sourceTree = BUILT_PRODUCTS_DIR; };
1CD90AFB278C7DE0001C4FEA /* Feels.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Feels.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -87,12 +99,18 @@
1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "Feels (iOS)Dev.entitlements"; sourceTree = "<group>"; };
1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = "<group>"; };
1E594AEAB5F046E3B3ED7C47 /* Feels Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Feels Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
B60015D02A064FF582E232FD /* Feels Watch App/Feels Watch AppDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Feels Watch App/Feels Watch AppDebug.entitlements"; sourceTree = "<group>"; };
B8AB4CD73C2B4DC89C6FE84D /* Feels Watch App/Feels Watch App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Feels Watch App/Feels Watch App.entitlements"; sourceTree = "<group>"; };
29E2A2FC314F88244CA946BF /* StreakTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = StreakTests.swift; sourceTree = "<group>"; };
5566271983AEDF1D33C34FE6 /* DataControllerCRUDTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = DataControllerCRUDTests.swift; sourceTree = "<group>"; };
9CFAE86F485C853DB3239DD9 /* IntegrationTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = IntegrationTests.swift; sourceTree = "<group>"; };
B60015D02A064FF582E232FD /* Feels Watch AppDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Feels Watch App/Feels Watch AppDebug.entitlements"; sourceTree = "<group>"; };
B8AB4CD73C2B4DC89C6FE84D /* Feels Watch App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Feels Watch App/Feels Watch App.entitlements"; sourceTree = "<group>"; };
DA0D74ACDD741CFA1F14F50F /* FeelsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FeelsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
DD717F91BD65382B7DDFE3C4 /* VoteLogicsTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = VoteLogicsTests.swift; sourceTree = "<group>"; };
F4D304CD05CC7C662CCD7DCB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
1C000C162EE93AE3009C9ED5 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
1C000C162EE93AE3009C9ED5 /* Exceptions for "Shared" folder in "FeelsWidgetExtension" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
"Color+Codable.swift",
@@ -122,7 +140,7 @@
);
target = 1CD90B44278C7E7A001C4FEA /* FeelsWidgetExtension */;
};
2166CE8AA7264FC2B4BFAAAC /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
2166CE8AA7264FC2B4BFAAAC /* Exceptions for "Shared" folder in "Feels Watch App" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Models/Mood.swift,
@@ -137,12 +155,52 @@
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
1C00073D2EE9388A009C9ED5 /* Shared */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (2166CE8AA7264FC2B4BFAAAC /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 1C000C162EE93AE3009C9ED5 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Shared; sourceTree = "<group>"; };
1C0009922EE938FC009C9ED5 /* FeelsWidget2 */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = FeelsWidget2; sourceTree = "<group>"; };
579031D619ED4B989145EEB1 /* Feels Watch App */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = "Feels Watch App"; sourceTree = "<group>"; };
1C00073D2EE9388A009C9ED5 /* Shared */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
2166CE8AA7264FC2B4BFAAAC /* Exceptions for "Shared" folder in "Feels Watch App" target */,
1C000C162EE93AE3009C9ED5 /* Exceptions for "Shared" folder in "FeelsWidgetExtension" target */,
);
explicitFileTypes = {
};
explicitFolders = (
);
path = Shared;
sourceTree = "<group>";
};
1C0009922EE938FC009C9ED5 /* FeelsWidget2 */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
explicitFileTypes = {
};
explicitFolders = (
);
path = FeelsWidget2;
sourceTree = "<group>";
};
579031D619ED4B989145EEB1 /* Feels Watch App */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
explicitFileTypes = {
};
explicitFolders = (
);
path = "Feels Watch App";
sourceTree = "<group>";
};
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
0DC68E3188164EBC373A6BF3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4F1C717B7747918A459322CB /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
1CD90AF2278C7DE0001C4FEA /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -199,9 +257,9 @@
1CD90AE5278C7DDF001C4FEA = {
isa = PBXGroup;
children = (
B8AB4CD73C2B4DC89C6FE84D /* Feels Watch App/Feels Watch App.entitlements */,
B60015D02A064FF582E232FD /* Feels Watch App/Feels Watch AppDebug.entitlements */,
1C0DAB50279DB0FB003B1F21 /* Feels/Localizable.xcstrings */,
B8AB4CD73C2B4DC89C6FE84D /* Feels Watch App.entitlements */,
B60015D02A064FF582E232FD /* Feels Watch AppDebug.entitlements */,
1C0DAB50279DB0FB003B1F21 /* Localizable.xcstrings */,
1CDEFBBE2F3B8736006AE6A1 /* Configuration.storekit */,
1CD90B6A278C7F75001C4FEA /* Feels (iOS).entitlements */,
1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */,
@@ -216,6 +274,7 @@
1CD90B11278C7DE0001C4FEA /* Tests macOS */,
1CD90B46278C7E7A001C4FEA /* Frameworks */,
1CD90AF6278C7DE0001C4FEA /* Products */,
38D005587E22737DC6291955 /* FeelsTests */,
);
sourceTree = "<group>";
};
@@ -228,6 +287,7 @@
1CD90B02278C7DE0001C4FEA /* Tests iOS.xctest */,
1CD90B0E278C7DE0001C4FEA /* Tests macOS.xctest */,
1CD90B45278C7E7A001C4FEA /* FeelsWidgetExtension.appex */,
DA0D74ACDD741CFA1F14F50F /* FeelsTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -265,10 +325,31 @@
1CD90B6B278C7F78001C4FEA /* CloudKit.framework */,
1CD90B47278C7E7A001C4FEA /* WidgetKit.framework */,
1CD90B49278C7E7A001C4FEA /* SwiftUI.framework */,
88F4C25CA0D11FB136B0B8A6 /* iOS */,
);
name = Frameworks;
sourceTree = "<group>";
};
38D005587E22737DC6291955 /* FeelsTests */ = {
isa = PBXGroup;
children = (
5566271983AEDF1D33C34FE6 /* DataControllerCRUDTests.swift */,
9CFAE86F485C853DB3239DD9 /* IntegrationTests.swift */,
29E2A2FC314F88244CA946BF /* StreakTests.swift */,
DD717F91BD65382B7DDFE3C4 /* VoteLogicsTests.swift */,
);
name = FeelsTests;
path = FeelsTests;
sourceTree = "<group>";
};
88F4C25CA0D11FB136B0B8A6 /* iOS */ = {
isa = PBXGroup;
children = (
F4D304CD05CC7C662CCD7DCB /* Foundation.framework */,
);
name = iOS;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -367,8 +448,6 @@
1C0009922EE938FC009C9ED5 /* FeelsWidget2 */,
);
name = FeelsWidgetExtension;
packageProductDependencies = (
);
productName = FeelsWidgetExtension;
productReference = 1CD90B45278C7E7A001C4FEA /* FeelsWidgetExtension.appex */;
productType = "com.apple.product-type.app-extension";
@@ -389,12 +468,28 @@
579031D619ED4B989145EEB1 /* Feels Watch App */,
);
name = "Feels Watch App";
packageProductDependencies = (
);
productName = "Feels Watch App";
productReference = 1E594AEAB5F046E3B3ED7C47 /* Feels Watch App.app */;
productType = "com.apple.product-type.application";
};
B375A511826E3AB53E2CF51A /* FeelsTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 611E0B1E1241C11626465A8D /* Build configuration list for PBXNativeTarget "FeelsTests" */;
buildPhases = (
681C769809C145ECC6A2AE8B /* Sources */,
0DC68E3188164EBC373A6BF3 /* Frameworks */,
AE59E2C6BF9FA0FBBE07A123 /* Resources */,
);
buildRules = (
);
dependencies = (
946F2D1B29B91CD7DB732908 /* PBXTargetDependency */,
);
name = FeelsTests;
productName = FeelsTests;
productReference = DA0D74ACDD741CFA1F14F50F /* FeelsTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -425,6 +520,10 @@
B1DB9E6543DE4A009DB00916 = {
CreatedOnToolsVersion = 15.0;
};
B375A511826E3AB53E2CF51A = {
CreatedOnToolsVersion = 16.0;
TestTargetID = 1CD90AF4278C7DE0001C4FEA;
};
};
};
buildConfigurationList = 1CD90AE9278C7DDF001C4FEA /* Build configuration list for PBXProject "Feels" */;
@@ -456,6 +555,7 @@
1CD90AFA278C7DE0001C4FEA /* Feels (macOS) */,
1CD90B01278C7DE0001C4FEA /* Tests iOS */,
1CD90B0D278C7DE0001C4FEA /* Tests macOS */,
B375A511826E3AB53E2CF51A /* FeelsTests */,
);
};
/* End PBXProject section */
@@ -472,7 +572,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1C0DAB51279DB0FB003B1F21 /* Feels/Localizable.xcstrings in Resources */,
1C0DAB51279DB0FB003B1F21 /* Localizable.xcstrings in Resources */,
1CDEFBBF2F3B8736006AE6A1 /* Configuration.storekit in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -503,7 +603,14 @@
buildActionMask = 2147483647;
files = (
1CDEFBC02F3B8736006AE6A1 /* Configuration.storekit in Resources */,
1C0DAB52279DB0FB003B1F22 /* Feels/Localizable.xcstrings in Resources */,
1C0DAB52279DB0FB003B1F22 /* Localizable.xcstrings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
AE59E2C6BF9FA0FBBE07A123 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -556,6 +663,17 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
681C769809C145ECC6A2AE8B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
EEB21B1CAA8EAEB497BD9FB3 /* DataControllerCRUDTests.swift in Sources */,
06E4767B5977FAC8B644FC92 /* IntegrationTests.swift in Sources */,
54259F7B3F4E959B3F4055E4 /* StreakTests.swift in Sources */,
9559409B5AEEAB40EBCB6AF9 /* VoteLogicsTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -574,6 +692,12 @@
target = 1CD90B44278C7E7A001C4FEA /* FeelsWidgetExtension */;
targetProxy = 1CD90B54278C7E7A001C4FEA /* PBXContainerItemProxy */;
};
946F2D1B29B91CD7DB732908 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = "Feels (iOS)";
target = 1CD90AF4278C7DE0001C4FEA /* Feels (iOS) */;
targetProxy = A0B973C7674930232515563A /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
@@ -1020,6 +1144,24 @@
};
name = Release;
};
298BB8B78DE12C7707210902 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = QND55P4443;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
PRODUCT_BUNDLE_IDENTIFIER = com.88oakapps.feels.tests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Feels.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Feels";
};
name = Debug;
};
67FBFEE92D1D4F8BBFBF7B1D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1052,6 +1194,25 @@
};
name = Release;
};
C9B28244C1A36D4F2FE7E61A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = QND55P4443;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 26.0;
PRODUCT_BUNDLE_IDENTIFIER = com.88oakapps.feels.tests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Feels.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Feels";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -1118,6 +1279,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
611E0B1E1241C11626465A8D /* Build configuration list for PBXNativeTarget "FeelsTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C9B28244C1A36D4F2FE7E61A /* Release */,
298BB8B78DE12C7707210902 /* Debug */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */

View File

@@ -32,9 +32,9 @@
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1CD90B01278C7DE0001C4FEA"
BuildableName = "Tests iOS.xctest"
BlueprintName = "Tests iOS"
BlueprintIdentifier = "B375A511826E3AB53E2CF51A"
BuildableName = "FeelsTests.xctest"
BlueprintName = "FeelsTests"
ReferencedContainer = "container:Feels.xcodeproj">
</BuildableReference>
</TestableReference>