feat(ios): 添加用药计划Widget小组件支持

- 创建medicineExtension小组件,支持iOS桌面显示用药计划
- 实现App Group数据共享机制,支持主应用与小组件数据同步
- 添加AppGroupUserDefaultsManager原生模块,提供跨应用数据访问能力
- 添加WidgetManager和WidgetCenterHelper,实现小组件刷新控制
- 在medications页面和Redux store中集成小组件数据同步逻辑
- 支持实时同步今日用药状态(待服用/已服用/已错过)到小组件
- 配置App Group entitlements (group.com.anonymous.digitalpilates)
- 更新Xcode项目配置,添加WidgetKit和SwiftUI框架支持
This commit is contained in:
richarjiang
2025-11-14 08:51:02 +08:00
parent d282abd146
commit b0e93eedae
25 changed files with 1423 additions and 4 deletions

View File

@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 70;
objects = {
/* Begin PBXBuildFile section */
@@ -16,6 +16,12 @@
79B2CB702E7B954600B51753 /* OutLive-Bridging-Header.h in Sources */ = {isa = PBXBuildFile; fileRef = F11748442D0722820044C1D9 /* OutLive-Bridging-Header.h */; };
79B2CB732E7B954F00B51753 /* HealthKitManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 79B2CB712E7B954F00B51753 /* HealthKitManager.m */; };
79B2CB742E7B954F00B51753 /* HealthKitManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79B2CB722E7B954F00B51753 /* HealthKitManager.swift */; };
79E80BA42EC5D92A004425BE /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79E80BA32EC5D92A004425BE /* WidgetKit.framework */; };
79E80BA62EC5D92A004425BE /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79E80BA52EC5D92A004425BE /* SwiftUI.framework */; };
79E80BB72EC5D92B004425BE /* medicineExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 79E80BA22EC5D92A004425BE /* medicineExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
79E80BFF2EC5E127004425BE /* AppGroupUserDefaultsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 79E80BFC2EC5E127004425BE /* AppGroupUserDefaultsManager.m */; };
79E80C002EC5E127004425BE /* WidgetManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 79E80BFE2EC5E127004425BE /* WidgetManager.m */; };
79E80C522EC5E500004425BE /* WidgetCenterHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79E80C512EC5E500004425BE /* WidgetCenterHelper.swift */; };
91B7BA17B50D328546B5B4B8 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = B7F23062EE59F61E6260DBA8 /* PrivacyInfo.xcprivacy */; };
AE00ECEC9D078460F642F131 /* libPods-OutLive.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F6136AA7113B3D210693D88 /* libPods-OutLive.a */; };
B6B9273B2FD4F4A800C6391C /* BackgroundTaskBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B9273A2FD4F4A800C6391C /* BackgroundTaskBridge.swift */; };
@@ -24,6 +30,30 @@
F11748422D0307B40044C1D9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F11748412D0307B40044C1D9 /* AppDelegate.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
79E80BB52EC5D92B004425BE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 79E80BA12EC5D92A004425BE;
remoteInfo = medicineExtension;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
79E80BB82EC5D92B004425BE /* Embed Foundation Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
79E80BB72EC5D92B004425BE /* medicineExtension.appex in Embed Foundation Extensions */,
);
name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
08BACF4D920A957DC2FE4350 /* Pods-OutLive.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OutLive.debug.xcconfig"; path = "Target Support Files/Pods-OutLive/Pods-OutLive.debug.xcconfig"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* OutLive.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OutLive.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -36,6 +66,15 @@
792C52612EB05B8F002F3F09 /* NativeToastManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NativeToastManager.swift; path = OutLive/NativeToastManager.swift; sourceTree = "<group>"; };
79B2CB712E7B954F00B51753 /* HealthKitManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = HealthKitManager.m; path = OutLive/HealthKitManager.m; sourceTree = "<group>"; };
79B2CB722E7B954F00B51753 /* HealthKitManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HealthKitManager.swift; path = OutLive/HealthKitManager.swift; sourceTree = "<group>"; };
79E80BA22EC5D92A004425BE /* medicineExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = medicineExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
79E80BA32EC5D92A004425BE /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
79E80BA52EC5D92A004425BE /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
79E80BBE2EC5DC50004425BE /* medicineExtensionRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = medicineExtensionRelease.entitlements; sourceTree = "<group>"; };
79E80BFB2EC5E127004425BE /* AppGroupUserDefaultsManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppGroupUserDefaultsManager.h; sourceTree = "<group>"; };
79E80BFC2EC5E127004425BE /* AppGroupUserDefaultsManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppGroupUserDefaultsManager.m; sourceTree = "<group>"; };
79E80BFD2EC5E127004425BE /* WidgetManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WidgetManager.h; sourceTree = "<group>"; };
79E80BFE2EC5E127004425BE /* WidgetManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WidgetManager.m; sourceTree = "<group>"; };
79E80C512EC5E500004425BE /* WidgetCenterHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WidgetCenterHelper.swift; path = OutLive/WidgetCenterHelper.swift; sourceTree = "<group>"; };
9B6A6CEBED2FC0931F7B7236 /* Pods-OutLive.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OutLive.release.xcconfig"; path = "Target Support Files/Pods-OutLive/Pods-OutLive.release.xcconfig"; sourceTree = "<group>"; };
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = OutLive/SplashScreen.storyboard; sourceTree = "<group>"; };
B6B9273A2FD4F4A800C6391C /* BackgroundTaskBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BackgroundTaskBridge.swift; path = OutLive/BackgroundTaskBridge.swift; sourceTree = "<group>"; };
@@ -47,6 +86,20 @@
F11748442D0722820044C1D9 /* OutLive-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "OutLive-Bridging-Header.h"; path = "OutLive/OutLive-Bridging-Header.h"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
79E80BBB2EC5D92B004425BE /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = (
Info.plist,
);
target = 79E80BA12EC5D92A004425BE /* medicineExtension */;
};
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
79E80BA72EC5D92A004425BE /* medicine */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (79E80BBB2EC5D92B004425BE /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = medicine; sourceTree = "<group>"; };
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
@@ -57,6 +110,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
79E80B9F2EC5D92A004425BE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
79E80BA62EC5D92A004425BE /* SwiftUI.framework in Frameworks */,
79E80BA42EC5D92A004425BE /* WidgetKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -80,6 +142,8 @@
792C52582EA880A7002F3F09 /* StoreKit.framework */,
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
6F6136AA7113B3D210693D88 /* libPods-OutLive.a */,
79E80BA32EC5D92A004425BE /* WidgetKit.framework */,
79E80BA52EC5D92A004425BE /* SwiftUI.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@@ -102,6 +166,12 @@
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
79E80BFB2EC5E127004425BE /* AppGroupUserDefaultsManager.h */,
79E80BFC2EC5E127004425BE /* AppGroupUserDefaultsManager.m */,
79E80BFD2EC5E127004425BE /* WidgetManager.h */,
79E80BFE2EC5E127004425BE /* WidgetManager.m */,
79E80C512EC5E500004425BE /* WidgetCenterHelper.swift */,
79E80BBE2EC5DC50004425BE /* medicineExtensionRelease.entitlements */,
792C52602EB05B8F002F3F09 /* NativeToastManager.m */,
792C52612EB05B8F002F3F09 /* NativeToastManager.swift */,
79B2CB712E7B954F00B51753 /* HealthKitManager.m */,
@@ -110,6 +180,7 @@
B6B9273A2FD4F4A800C6391C /* BackgroundTaskBridge.swift */,
13B07FAE1A68108700A75B9A /* OutLive */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
79E80BA72EC5D92A004425BE /* medicine */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
80E2A1E8ECA8777F7264D855 /* ExpoModulesProviders */,
@@ -124,6 +195,7 @@
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* OutLive.app */,
79E80BA22EC5D92A004425BE /* medicineExtension.appex */,
);
name = Products;
sourceTree = "<group>";
@@ -169,27 +241,53 @@
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
8F598744CAFFA386101BCC07 /* [CP] Embed Pods Frameworks */,
54EB3ED8CF242B308A7FE01E /* [CP] Copy Pods Resources */,
79E80BB82EC5D92B004425BE /* Embed Foundation Extensions */,
);
buildRules = (
);
dependencies = (
79E80BB62EC5D92B004425BE /* PBXTargetDependency */,
);
name = OutLive;
productName = OutLive;
productReference = 13B07F961A680F5B00A75B9A /* OutLive.app */;
productType = "com.apple.product-type.application";
};
79E80BA12EC5D92A004425BE /* medicineExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 79E80BBC2EC5D92B004425BE /* Build configuration list for PBXNativeTarget "medicineExtension" */;
buildPhases = (
79E80B9E2EC5D92A004425BE /* Sources */,
79E80B9F2EC5D92A004425BE /* Frameworks */,
79E80BA02EC5D92A004425BE /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
79E80BA72EC5D92A004425BE /* medicine */,
);
name = medicineExtension;
productName = medicineExtension;
productReference = 79E80BA22EC5D92A004425BE /* medicineExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
83CBB9F71A601CBA00E9B192 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 2610;
LastUpgradeCheck = 1130;
TargetAttributes = {
13B07F861A680F5B00A75B9A = {
LastSwiftMigration = 1250;
};
79E80BA12EC5D92A004425BE = {
CreatedOnToolsVersion = 26.1;
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "OutLive" */;
@@ -206,6 +304,7 @@
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* OutLive */,
79E80BA12EC5D92A004425BE /* medicineExtension */,
);
};
/* End PBXProject section */
@@ -222,6 +321,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
79E80BA02EC5D92A004425BE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -374,6 +480,9 @@
buildActionMask = 2147483647;
files = (
792C52622EB05B8F002F3F09 /* NativeToastManager.m in Sources */,
79E80BFF2EC5E127004425BE /* AppGroupUserDefaultsManager.m in Sources */,
79E80C002EC5E127004425BE /* WidgetManager.m in Sources */,
79E80C522EC5E500004425BE /* WidgetCenterHelper.swift in Sources */,
792C52632EB05B8F002F3F09 /* NativeToastManager.swift in Sources */,
79B2CB732E7B954F00B51753 /* HealthKitManager.m in Sources */,
79B2CB742E7B954F00B51753 /* HealthKitManager.swift in Sources */,
@@ -385,8 +494,23 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
79E80B9E2EC5D92A004425BE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
79E80BB62EC5D92B004425BE /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 79E80BA12EC5D92A004425BE /* medicineExtension */;
targetProxy = 79E80BB52EC5D92B004425BE /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
@@ -456,6 +580,102 @@
};
name = Release;
};
79E80BB92EC5D92B004425BE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 756WVXJ6MT;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = medicine/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = medicine;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 26.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.digitalpilates.medicine;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
79E80BBA2EC5D92B004425BE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_ENTITLEMENTS = medicineExtensionRelease.entitlements;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = 756WVXJ6MT;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = medicine/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = medicine;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
IPHONEOS_DEPLOYMENT_TARGET = 26.1;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.digitalpilates.medicine;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
83CBBA201A601CBA00E9B192 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -586,6 +806,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
79E80BBC2EC5D92B004425BE /* Build configuration list for PBXNativeTarget "medicineExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
79E80BB92EC5D92B004425BE /* Debug */,
79E80BBA2EC5D92B004425BE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "OutLive" */ = {
isa = XCConfigurationList;
buildConfigurations = (