feat: add widget extension — small, medium, large
- KisaniCalWidget: 3 sizes sharing the app's design language Small: date badge + kisaniCAL. wordmark + task count Medium: badge + task list (3 items) + workout row Large: badge + full task list (6 items) + workout row - WidgetDataStore (Shared/): lightweight Codable types for App Group UserDefaults cross-process data exchange - WidgetBridge: TaskViewModel/WorkoutViewModel extensions that sync data into App Group and call WidgetCenter.reloadAllTimelines() - ContentView: calls syncWidget() on appear and scene becoming active - project.yml + entitlements updated; project regenerated via xcodegen
This commit is contained in:
@@ -10,7 +10,12 @@
|
|||||||
096804560A2F0A7D74E64780 /* TaskItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC2AFB4FA765383740767CB /* TaskItem.swift */; };
|
096804560A2F0A7D74E64780 /* TaskItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC2AFB4FA765383740767CB /* TaskItem.swift */; };
|
||||||
13E4B9854F595394FC9D5912 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5E179A9189B0A8C3F856F6 /* ContentView.swift */; };
|
13E4B9854F595394FC9D5912 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5E179A9189B0A8C3F856F6 /* ContentView.swift */; };
|
||||||
1A22EE21460821170E44B1DF /* ExerciseModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5722CC4B59E3939724142710 /* ExerciseModels.swift */; };
|
1A22EE21460821170E44B1DF /* ExerciseModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5722CC4B59E3939724142710 /* ExerciseModels.swift */; };
|
||||||
|
3256F64040E5BBCD30644047 /* WidgetDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3364C849866EAAA5FEB46A1 /* WidgetDataStore.swift */; };
|
||||||
3C793FD5DA00D3E9C0D51FEC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 72FDF9C8DD37134576356B89 /* Assets.xcassets */; };
|
3C793FD5DA00D3E9C0D51FEC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 72FDF9C8DD37134576356B89 /* Assets.xcassets */; };
|
||||||
|
46D78859DD34FDE6172F213D /* KisaniCalWidget.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 363924D4E7E82486E23C1C57 /* KisaniCalWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
4F829CF0B3BD04CD2EAB8C73 /* WidgetBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8934E4F21B90C9650F71D0A2 /* WidgetBridge.swift */; };
|
||||||
|
67CE1747E5DB3FDA79D0FDFD /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 106EEF572C6F8990408329F0 /* OnboardingView.swift */; };
|
||||||
|
6921CB73A3257502FF778381 /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE2D50B4B4AC227BD21F4B60 /* SplashView.swift */; };
|
||||||
7CEBC340BFA9238D121946AC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 57714B79AFFF60DA90EB86E3 /* Preview Assets.xcassets */; };
|
7CEBC340BFA9238D121946AC /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 57714B79AFFF60DA90EB86E3 /* Preview Assets.xcassets */; };
|
||||||
8BE9D3D650B0F06169EC7048 /* SharedComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A4491BFA50721082024756 /* SharedComponents.swift */; };
|
8BE9D3D650B0F06169EC7048 /* SharedComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A4491BFA50721082024756 /* SharedComponents.swift */; };
|
||||||
9070521B1D36A5551976C275 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADF6CCD95A587E26E30F5712 /* CalendarView.swift */; };
|
9070521B1D36A5551976C275 /* CalendarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ADF6CCD95A587E26E30F5712 /* CalendarView.swift */; };
|
||||||
@@ -19,35 +24,74 @@
|
|||||||
A3B2D6622A2EE35C8D5A3C9B /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72308FEE0226F45414C04DDD /* SettingsView.swift */; };
|
A3B2D6622A2EE35C8D5A3C9B /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72308FEE0226F45414C04DDD /* SettingsView.swift */; };
|
||||||
A9FF93259AE8FF0ABF69D71A /* DesignTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD4A35C0E1270E2E15C03F23 /* DesignTokens.swift */; };
|
A9FF93259AE8FF0ABF69D71A /* DesignTokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD4A35C0E1270E2E15C03F23 /* DesignTokens.swift */; };
|
||||||
BEAFF968632A34C70B11C5AC /* MatrixView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44530A77DF12A17E52AAF34 /* MatrixView.swift */; };
|
BEAFF968632A34C70B11C5AC /* MatrixView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D44530A77DF12A17E52AAF34 /* MatrixView.swift */; };
|
||||||
|
C1B3FC29820FCAF069D2C6FE /* KisaniCalWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 825C519927190A5CDB3CD4A8 /* KisaniCalWidget.swift */; };
|
||||||
C79C33BE2802E81AA00175CC /* TodayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C786EBC7DF879D64EB28165E /* TodayView.swift */; };
|
C79C33BE2802E81AA00175CC /* TodayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C786EBC7DF879D64EB28165E /* TodayView.swift */; };
|
||||||
|
CF130E143FC6FFE2FE853FB6 /* WidgetDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3364C849866EAAA5FEB46A1 /* WidgetDataStore.swift */; };
|
||||||
D591A72235A53D4038FBC2B4 /* KisaniCalApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001F67ADC72FBAEC03EB7E01 /* KisaniCalApp.swift */; };
|
D591A72235A53D4038FBC2B4 /* KisaniCalApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001F67ADC72FBAEC03EB7E01 /* KisaniCalApp.swift */; };
|
||||||
E7F1A2B3C4D5E6F7A8B9C0D1 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A1B2C3D4E5F6A7B8C9D0E1 /* NotificationManager.swift */; };
|
EF03EC9F974B14D100DD5528 /* NotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93D045FE3DEB1D22D908A29F /* NotificationManager.swift */; };
|
||||||
B1C2D3E4F5A6B7C8D9E0F1A2 /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2D3E4F5A6B7C8D9E0F1A2B3 /* OnboardingView.swift */; };
|
|
||||||
E2F3A4B5C6D7E8F9A0B1C2D3 /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3E4F5A6B7C8D9E0F1A2B3C4 /* SplashView.swift */; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
D4A1B57264F223C8871E1BC9 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = AF6DE7A812408E3742522E90 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = 532C52E96C1EE1E30A7E9157;
|
||||||
|
remoteInfo = KisaniCalWidget;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
B3BE3000B362E4DDC4EE9E76 /* Embed Foundation Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
46D78859DD34FDE6172F213D /* KisaniCalWidget.appex in Embed Foundation Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed Foundation Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
001F67ADC72FBAEC03EB7E01 /* KisaniCalApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KisaniCalApp.swift; sourceTree = "<group>"; };
|
001F67ADC72FBAEC03EB7E01 /* KisaniCalApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KisaniCalApp.swift; sourceTree = "<group>"; };
|
||||||
|
106EEF572C6F8990408329F0 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
|
||||||
|
1AA7498EFED7692022F3E7E1 /* KisaniCal.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = KisaniCal.entitlements; sourceTree = "<group>"; };
|
||||||
20DAD771EFCC8B3B1DBD4DD6 /* WorkoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutView.swift; sourceTree = "<group>"; };
|
20DAD771EFCC8B3B1DBD4DD6 /* WorkoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WorkoutView.swift; sourceTree = "<group>"; };
|
||||||
23A4491BFA50721082024756 /* SharedComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedComponents.swift; sourceTree = "<group>"; };
|
23A4491BFA50721082024756 /* SharedComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedComponents.swift; sourceTree = "<group>"; };
|
||||||
|
363924D4E7E82486E23C1C57 /* KisaniCalWidget.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = KisaniCalWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
5722CC4B59E3939724142710 /* ExerciseModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExerciseModels.swift; sourceTree = "<group>"; };
|
5722CC4B59E3939724142710 /* ExerciseModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExerciseModels.swift; sourceTree = "<group>"; };
|
||||||
57714B79AFFF60DA90EB86E3 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
57714B79AFFF60DA90EB86E3 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||||
72308FEE0226F45414C04DDD /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
72308FEE0226F45414C04DDD /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||||
72FDF9C8DD37134576356B89 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
72FDF9C8DD37134576356B89 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
|
74EE7C2E74094E3E27FD78E4 /* KisaniCalWidget.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = KisaniCalWidget.entitlements; sourceTree = "<group>"; };
|
||||||
768D4E314252E2A90A06CCF2 /* AddExerciseSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddExerciseSheet.swift; sourceTree = "<group>"; };
|
768D4E314252E2A90A06CCF2 /* AddExerciseSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddExerciseSheet.swift; sourceTree = "<group>"; };
|
||||||
9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KisaniCal.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
825C519927190A5CDB3CD4A8 /* KisaniCalWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KisaniCalWidget.swift; sourceTree = "<group>"; };
|
||||||
|
8934E4F21B90C9650F71D0A2 /* WidgetBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetBridge.swift; sourceTree = "<group>"; };
|
||||||
|
93D045FE3DEB1D22D908A29F /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
||||||
|
9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = KisaniCal.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
A3364C849866EAAA5FEB46A1 /* WidgetDataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetDataStore.swift; sourceTree = "<group>"; };
|
||||||
ADF6CCD95A587E26E30F5712 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = "<group>"; };
|
ADF6CCD95A587E26E30F5712 /* CalendarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarView.swift; sourceTree = "<group>"; };
|
||||||
BC5E179A9189B0A8C3F856F6 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
BC5E179A9189B0A8C3F856F6 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||||
BD4A35C0E1270E2E15C03F23 /* DesignTokens.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DesignTokens.swift; sourceTree = "<group>"; };
|
BD4A35C0E1270E2E15C03F23 /* DesignTokens.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DesignTokens.swift; sourceTree = "<group>"; };
|
||||||
|
BE2D50B4B4AC227BD21F4B60 /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = "<group>"; };
|
||||||
C786EBC7DF879D64EB28165E /* TodayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayView.swift; sourceTree = "<group>"; };
|
C786EBC7DF879D64EB28165E /* TodayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayView.swift; sourceTree = "<group>"; };
|
||||||
C2D3E4F5A6B7C8D9E0F1A2B3 /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = "<group>"; };
|
|
||||||
D3E4F5A6B7C8D9E0F1A2B3C4 /* SplashView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = "<group>"; };
|
|
||||||
D44530A77DF12A17E52AAF34 /* MatrixView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixView.swift; sourceTree = "<group>"; };
|
D44530A77DF12A17E52AAF34 /* MatrixView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixView.swift; sourceTree = "<group>"; };
|
||||||
DCC2AFB4FA765383740767CB /* TaskItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskItem.swift; sourceTree = "<group>"; };
|
DCC2AFB4FA765383740767CB /* TaskItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskItem.swift; sourceTree = "<group>"; };
|
||||||
F7A1B2C3D4E5F6A7B8C9D0E1 /* NotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManager.swift; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
10E8E769F7D262FF425B460B /* KisaniCalWidget */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
74EE7C2E74094E3E27FD78E4 /* KisaniCalWidget.entitlements */,
|
||||||
|
825C519927190A5CDB3CD4A8 /* KisaniCalWidget.swift */,
|
||||||
|
);
|
||||||
|
path = KisaniCalWidget;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
21B93C269F283F11B415B18C /* Components */ = {
|
21B93C269F283F11B415B18C /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -56,6 +100,14 @@
|
|||||||
path = Components;
|
path = Components;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
2217B7EEC45957B820311EC7 /* Shared */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A3364C849866EAAA5FEB46A1 /* WidgetDataStore.swift */,
|
||||||
|
);
|
||||||
|
path = Shared;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
23CBCF100C5EF55E737379CA /* Models */ = {
|
23CBCF100C5EF55E737379CA /* Models */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -69,6 +121,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
F70DA4746C68CD405435DAB6 /* KisaniCal */,
|
F70DA4746C68CD405435DAB6 /* KisaniCal */,
|
||||||
|
10E8E769F7D262FF425B460B /* KisaniCalWidget */,
|
||||||
|
2217B7EEC45957B820311EC7 /* Shared */,
|
||||||
51BD1B5DEDE9FAD9CA2FF6DA /* Products */,
|
51BD1B5DEDE9FAD9CA2FF6DA /* Products */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -77,6 +131,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */,
|
9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */,
|
||||||
|
363924D4E7E82486E23C1C57 /* KisaniCalWidget.appex */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -87,11 +142,11 @@
|
|||||||
768D4E314252E2A90A06CCF2 /* AddExerciseSheet.swift */,
|
768D4E314252E2A90A06CCF2 /* AddExerciseSheet.swift */,
|
||||||
ADF6CCD95A587E26E30F5712 /* CalendarView.swift */,
|
ADF6CCD95A587E26E30F5712 /* CalendarView.swift */,
|
||||||
D44530A77DF12A17E52AAF34 /* MatrixView.swift */,
|
D44530A77DF12A17E52AAF34 /* MatrixView.swift */,
|
||||||
|
106EEF572C6F8990408329F0 /* OnboardingView.swift */,
|
||||||
72308FEE0226F45414C04DDD /* SettingsView.swift */,
|
72308FEE0226F45414C04DDD /* SettingsView.swift */,
|
||||||
|
BE2D50B4B4AC227BD21F4B60 /* SplashView.swift */,
|
||||||
C786EBC7DF879D64EB28165E /* TodayView.swift */,
|
C786EBC7DF879D64EB28165E /* TodayView.swift */,
|
||||||
20DAD771EFCC8B3B1DBD4DD6 /* WorkoutView.swift */,
|
20DAD771EFCC8B3B1DBD4DD6 /* WorkoutView.swift */,
|
||||||
C2D3E4F5A6B7C8D9E0F1A2B3 /* OnboardingView.swift */,
|
|
||||||
D3E4F5A6B7C8D9E0F1A2B3C4 /* SplashView.swift */,
|
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -112,22 +167,16 @@
|
|||||||
path = Theme;
|
path = Theme;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
A1B2C3D4E5F6070A8B9C0D1E /* Managers */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
F7A1B2C3D4E5F6A7B8C9D0E1 /* NotificationManager.swift */,
|
|
||||||
);
|
|
||||||
path = Managers;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
F70DA4746C68CD405435DAB6 /* KisaniCal */ = {
|
F70DA4746C68CD405435DAB6 /* KisaniCal */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
72FDF9C8DD37134576356B89 /* Assets.xcassets */,
|
72FDF9C8DD37134576356B89 /* Assets.xcassets */,
|
||||||
BC5E179A9189B0A8C3F856F6 /* ContentView.swift */,
|
BC5E179A9189B0A8C3F856F6 /* ContentView.swift */,
|
||||||
|
1AA7498EFED7692022F3E7E1 /* KisaniCal.entitlements */,
|
||||||
001F67ADC72FBAEC03EB7E01 /* KisaniCalApp.swift */,
|
001F67ADC72FBAEC03EB7E01 /* KisaniCalApp.swift */,
|
||||||
|
8934E4F21B90C9650F71D0A2 /* WidgetBridge.swift */,
|
||||||
21B93C269F283F11B415B18C /* Components */,
|
21B93C269F283F11B415B18C /* Components */,
|
||||||
A1B2C3D4E5F6070A8B9C0D1E /* Managers */,
|
FB9BF734B9E493EEB09ACE21 /* Managers */,
|
||||||
23CBCF100C5EF55E737379CA /* Models */,
|
23CBCF100C5EF55E737379CA /* Models */,
|
||||||
9981C53A4E263A0DBC3DF194 /* Preview Content */,
|
9981C53A4E263A0DBC3DF194 /* Preview Content */,
|
||||||
E0A03E79A679740978E61BF1 /* Theme */,
|
E0A03E79A679740978E61BF1 /* Theme */,
|
||||||
@@ -136,6 +185,14 @@
|
|||||||
path = KisaniCal;
|
path = KisaniCal;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
FB9BF734B9E493EEB09ACE21 /* Managers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
93D045FE3DEB1D22D908A29F /* NotificationManager.swift */,
|
||||||
|
);
|
||||||
|
path = Managers;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -145,10 +202,12 @@
|
|||||||
buildPhases = (
|
buildPhases = (
|
||||||
82EA49FE155821D424C46912 /* Sources */,
|
82EA49FE155821D424C46912 /* Sources */,
|
||||||
E1F1A3EE4469B851B972E4AE /* Resources */,
|
E1F1A3EE4469B851B972E4AE /* Resources */,
|
||||||
|
B3BE3000B362E4DDC4EE9E76 /* Embed Foundation Extensions */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
3FA0FA885B1A4FC539D57049 /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = KisaniCal;
|
name = KisaniCal;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
@@ -157,6 +216,23 @@
|
|||||||
productReference = 9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */;
|
productReference = 9BD1F84B4F45B12CB9B9F1B2 /* KisaniCal.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
532C52E96C1EE1E30A7E9157 /* KisaniCalWidget */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 6B8469B838ACDD00336D1CAD /* Build configuration list for PBXNativeTarget "KisaniCalWidget" */;
|
||||||
|
buildPhases = (
|
||||||
|
B3D66A55B74C7B9AE6099149 /* Sources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = KisaniCalWidget;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = KisaniCalWidget;
|
||||||
|
productReference = 363924D4E7E82486E23C1C57 /* KisaniCalWidget.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
@@ -169,6 +245,9 @@
|
|||||||
297C0C2BFBA10AB52D5D49CE = {
|
297C0C2BFBA10AB52D5D49CE = {
|
||||||
ProvisioningStyle = Automatic;
|
ProvisioningStyle = Automatic;
|
||||||
};
|
};
|
||||||
|
532C52E96C1EE1E30A7E9157 = {
|
||||||
|
ProvisioningStyle = Automatic;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = EF1C4F3BDA4AA7E008FC2CDE /* Build configuration list for PBXProject "KisaniCal" */;
|
buildConfigurationList = EF1C4F3BDA4AA7E008FC2CDE /* Build configuration list for PBXProject "KisaniCal" */;
|
||||||
@@ -186,6 +265,7 @@
|
|||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
297C0C2BFBA10AB52D5D49CE /* KisaniCal */,
|
297C0C2BFBA10AB52D5D49CE /* KisaniCal */,
|
||||||
|
532C52E96C1EE1E30A7E9157 /* KisaniCalWidget */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
@@ -214,28 +294,47 @@
|
|||||||
1A22EE21460821170E44B1DF /* ExerciseModels.swift in Sources */,
|
1A22EE21460821170E44B1DF /* ExerciseModels.swift in Sources */,
|
||||||
D591A72235A53D4038FBC2B4 /* KisaniCalApp.swift in Sources */,
|
D591A72235A53D4038FBC2B4 /* KisaniCalApp.swift in Sources */,
|
||||||
BEAFF968632A34C70B11C5AC /* MatrixView.swift in Sources */,
|
BEAFF968632A34C70B11C5AC /* MatrixView.swift in Sources */,
|
||||||
|
EF03EC9F974B14D100DD5528 /* NotificationManager.swift in Sources */,
|
||||||
|
67CE1747E5DB3FDA79D0FDFD /* OnboardingView.swift in Sources */,
|
||||||
A3B2D6622A2EE35C8D5A3C9B /* SettingsView.swift in Sources */,
|
A3B2D6622A2EE35C8D5A3C9B /* SettingsView.swift in Sources */,
|
||||||
8BE9D3D650B0F06169EC7048 /* SharedComponents.swift in Sources */,
|
8BE9D3D650B0F06169EC7048 /* SharedComponents.swift in Sources */,
|
||||||
|
6921CB73A3257502FF778381 /* SplashView.swift in Sources */,
|
||||||
096804560A2F0A7D74E64780 /* TaskItem.swift in Sources */,
|
096804560A2F0A7D74E64780 /* TaskItem.swift in Sources */,
|
||||||
E7F1A2B3C4D5E6F7A8B9C0D1 /* NotificationManager.swift in Sources */,
|
|
||||||
C79C33BE2802E81AA00175CC /* TodayView.swift in Sources */,
|
C79C33BE2802E81AA00175CC /* TodayView.swift in Sources */,
|
||||||
|
4F829CF0B3BD04CD2EAB8C73 /* WidgetBridge.swift in Sources */,
|
||||||
|
3256F64040E5BBCD30644047 /* WidgetDataStore.swift in Sources */,
|
||||||
9E3F966F6B6AF2E4F8109E51 /* WorkoutView.swift in Sources */,
|
9E3F966F6B6AF2E4F8109E51 /* WorkoutView.swift in Sources */,
|
||||||
B1C2D3E4F5A6B7C8D9E0F1A2 /* OnboardingView.swift in Sources */,
|
);
|
||||||
E2F3A4B5C6D7E8F9A0B1C2D3 /* SplashView.swift in Sources */,
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
B3D66A55B74C7B9AE6099149 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
C1B3FC29820FCAF069D2C6FE /* KisaniCalWidget.swift in Sources */,
|
||||||
|
CF130E143FC6FFE2FE853FB6 /* WidgetDataStore.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
3FA0FA885B1A4FC539D57049 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = 532C52E96C1EE1E30A7E9157 /* KisaniCalWidget */;
|
||||||
|
targetProxy = D4A1B57264F223C8871E1BC9 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
0CEA6637F45C32A237FBA20D /* Release */ = {
|
0CEA6637F45C32A237FBA20D /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = KisaniCal/KisaniCal.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = K8BLMMR883;
|
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Kisani Cal";
|
INFOPLIST_KEY_CFBundleDisplayName = "Kisani Cal";
|
||||||
@@ -314,6 +413,40 @@
|
|||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
47C8DA15444717168992CD67 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = KisaniCalWidget/KisaniCalWidget.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_NSExtensionPointIdentifier = "com.apple.widgetkit-extension";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.widget;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 5.9;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
9F668AA275094D23AACA2F61 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_ENTITLEMENTS = KisaniCalWidget/KisaniCalWidget.entitlements;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_NSExtensionPointIdentifier = "com.apple.widgetkit-extension";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.kutesir.KisaniCal.widget;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 5.9;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
BD527AA54887489CC665FB9D /* Debug */ = {
|
BD527AA54887489CC665FB9D /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
@@ -382,9 +515,9 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = KisaniCal/KisaniCal.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
DEVELOPMENT_TEAM = K8BLMMR883;
|
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Kisani Cal";
|
INFOPLIST_KEY_CFBundleDisplayName = "Kisani Cal";
|
||||||
@@ -410,6 +543,15 @@
|
|||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
6B8469B838ACDD00336D1CAD /* Build configuration list for PBXNativeTarget "KisaniCalWidget" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
47C8DA15444717168992CD67 /* Debug */,
|
||||||
|
9F668AA275094D23AACA2F61 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Debug;
|
||||||
|
};
|
||||||
B60FDD2C88D1D33CF1EC02B3 /* Build configuration list for PBXNativeTarget "KisaniCal" */ = {
|
B60FDD2C88D1D33CF1EC02B3 /* Build configuration list for PBXNativeTarget "KisaniCal" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
|||||||
7
KisaniCal.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
KisaniCal.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
||||||
@@ -73,12 +73,19 @@ struct ContentView: View {
|
|||||||
OnboardingView(isPresented: $showOnboarding)
|
OnboardingView(isPresented: $showOnboarding)
|
||||||
.onDisappear { hasOnboarded = true }
|
.onDisappear { hasOnboarded = true }
|
||||||
}
|
}
|
||||||
.onAppear { if !hasOnboarded { showOnboarding = true } }
|
.onAppear {
|
||||||
|
if !hasOnboarded { showOnboarding = true }
|
||||||
|
taskVM.syncWidget()
|
||||||
|
workoutVM.syncWidget()
|
||||||
|
}
|
||||||
.onChange(of: scenePhase) { phase in
|
.onChange(of: scenePhase) { phase in
|
||||||
if phase == .active {
|
if phase == .active {
|
||||||
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)
|
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)
|
||||||
|
taskVM.syncWidget()
|
||||||
|
workoutVM.syncWidget()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onChange(of: taskVM.tasks.count) { _ in taskVM.syncWidget() }
|
||||||
.onChange(of: workoutVM.doneSets) { _ in
|
.onChange(of: workoutVM.doneSets) { _ in
|
||||||
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)
|
NotificationManager.shared.reschedule(workoutVM: workoutVM, taskVM: taskVM)
|
||||||
}
|
}
|
||||||
|
|||||||
10
KisaniCal/KisaniCal.entitlements
Normal file
10
KisaniCal/KisaniCal.entitlements
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.com.kutesir.KisaniCal</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
41
KisaniCal/WidgetBridge.swift
Normal file
41
KisaniCal/WidgetBridge.swift
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import WidgetKit
|
||||||
|
|
||||||
|
// Syncs app ViewModels into App Group UserDefaults so the widget can read them,
|
||||||
|
// then asks WidgetKit to reload all timelines.
|
||||||
|
|
||||||
|
extension TaskViewModel {
|
||||||
|
func syncWidget() {
|
||||||
|
let cal = Calendar.current
|
||||||
|
let start = cal.startOfDay(for: Date())
|
||||||
|
let end = cal.date(byAdding: .day, value: 1, to: start)!
|
||||||
|
|
||||||
|
let entries = tasks
|
||||||
|
.filter { !$0.isComplete }
|
||||||
|
.prefix(10)
|
||||||
|
.map { t in
|
||||||
|
WidgetDataStore.WidgetTask(
|
||||||
|
id: t.id,
|
||||||
|
title: t.title,
|
||||||
|
dueDate: t.dueDate,
|
||||||
|
isToday: t.dueDate.map { $0 >= start && $0 < end } ?? false,
|
||||||
|
isOverdue: t.dueDate.map { $0 < start } ?? false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
WidgetDataStore.saveTasks(Array(entries))
|
||||||
|
WidgetCenter.shared.reloadAllTimelines()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension WorkoutViewModel {
|
||||||
|
func syncWidget() {
|
||||||
|
let weekday = Calendar.current.component(.weekday, from: Date())
|
||||||
|
let todayProgram = program(for: Date())
|
||||||
|
let workout = WidgetDataStore.WidgetWorkout(
|
||||||
|
name: activeProgram?.name ?? "Workout",
|
||||||
|
sectionsCount: activeProgram?.sections.count ?? 0,
|
||||||
|
hasWorkoutToday: todayProgram != nil
|
||||||
|
)
|
||||||
|
WidgetDataStore.saveWorkout(workout)
|
||||||
|
WidgetCenter.shared.reloadAllTimelines()
|
||||||
|
}
|
||||||
|
}
|
||||||
10
KisaniCalWidget/KisaniCalWidget.entitlements
Normal file
10
KisaniCalWidget/KisaniCalWidget.entitlements
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>group.com.kutesir.KisaniCal</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
414
KisaniCalWidget/KisaniCalWidget.swift
Normal file
414
KisaniCalWidget/KisaniCalWidget.swift
Normal file
@@ -0,0 +1,414 @@
|
|||||||
|
import WidgetKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// MARK: - Colors (no UIKit dependency)
|
||||||
|
|
||||||
|
private enum WC {
|
||||||
|
static let accent = Color(red: 232/255, green: 98/255, blue: 42/255)
|
||||||
|
static let green = Color(red: 62/255, green: 207/255, blue: 142/255)
|
||||||
|
static let blue = Color(red: 77/255, green: 157/255, blue: 224/255)
|
||||||
|
|
||||||
|
static func bg(_ s: ColorScheme) -> Color {
|
||||||
|
s == .dark ? Color(red: 9/255, green: 9/255, blue: 9/255)
|
||||||
|
: Color(red: 245/255, green: 243/255, blue: 240/255)
|
||||||
|
}
|
||||||
|
static func surface(_ s: ColorScheme) -> Color {
|
||||||
|
s == .dark ? Color(red: 19/255, green: 19/255, blue: 19/255)
|
||||||
|
: Color(red: 255/255, green: 253/255, blue: 250/255)
|
||||||
|
}
|
||||||
|
static func text(_ s: ColorScheme) -> Color {
|
||||||
|
s == .dark ? Color(red: 240/255, green: 237/255, blue: 232/255)
|
||||||
|
: Color(red: 26/255, green: 23/255, blue: 20/255)
|
||||||
|
}
|
||||||
|
static func text2(_ s: ColorScheme) -> Color { text(s).opacity(0.48) }
|
||||||
|
static func text3(_ s: ColorScheme) -> Color { text(s).opacity(0.30) }
|
||||||
|
static func border(_ s: ColorScheme) -> Color {
|
||||||
|
s == .dark ? Color.white.opacity(0.07) : Color.black.opacity(0.09)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Timeline Entry
|
||||||
|
|
||||||
|
struct KisaniEntry: TimelineEntry {
|
||||||
|
let date: Date
|
||||||
|
let todayTasks: [WidgetDataStore.WidgetTask]
|
||||||
|
let overdueTasks: [WidgetDataStore.WidgetTask]
|
||||||
|
let workout: WidgetDataStore.WidgetWorkout?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Provider
|
||||||
|
|
||||||
|
struct KisaniProvider: TimelineProvider {
|
||||||
|
func placeholder(in context: Context) -> KisaniEntry {
|
||||||
|
KisaniEntry(
|
||||||
|
date: Date(),
|
||||||
|
todayTasks: previewTasks,
|
||||||
|
overdueTasks: [],
|
||||||
|
workout: WidgetDataStore.WidgetWorkout(
|
||||||
|
name: "Shoulders & Arms", sectionsCount: 4, hasWorkoutToday: true)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSnapshot(in context: Context, completion: @escaping (KisaniEntry) -> Void) {
|
||||||
|
completion(entry())
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTimeline(in context: Context, completion: @escaping (Timeline<KisaniEntry>) -> Void) {
|
||||||
|
let next = Calendar.current.date(byAdding: .minute, value: 30, to: Date())!
|
||||||
|
completion(Timeline(entries: [entry()], policy: .after(next)))
|
||||||
|
}
|
||||||
|
|
||||||
|
private func entry() -> KisaniEntry {
|
||||||
|
let all = WidgetDataStore.loadTasks()
|
||||||
|
let today = all.filter { $0.isToday }
|
||||||
|
let overdue = all.filter { $0.isOverdue }
|
||||||
|
let workout = WidgetDataStore.loadWorkout()
|
||||||
|
return KisaniEntry(date: Date(), todayTasks: today, overdueTasks: overdue, workout: workout)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var previewTasks: [WidgetDataStore.WidgetTask] {
|
||||||
|
[
|
||||||
|
.init(id: UUID(), title: "Azure exam prep", dueDate: Date(), isToday: true, isOverdue: false),
|
||||||
|
.init(id: UUID(), title: "Push AWS project", dueDate: Date(), isToday: true, isOverdue: false),
|
||||||
|
.init(id: UUID(), title: "Review PR #42", dueDate: Date(), isToday: false, isOverdue: true),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Widget Bundle
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct KisaniCalWidgetBundle: WidgetBundle {
|
||||||
|
var body: some Widget {
|
||||||
|
KisaniCalWidget()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct KisaniCalWidget: Widget {
|
||||||
|
let kind = "KisaniCalWidget"
|
||||||
|
|
||||||
|
var body: some WidgetConfiguration {
|
||||||
|
StaticConfiguration(kind: kind, provider: KisaniProvider()) { entry in
|
||||||
|
KisaniWidgetView(entry: entry)
|
||||||
|
.containerBackground(for: .widget) { Color.clear }
|
||||||
|
}
|
||||||
|
.configurationDisplayName("Kisani Cal")
|
||||||
|
.description("Today's tasks and workout at a glance.")
|
||||||
|
.supportedFamilies([.systemSmall, .systemMedium, .systemLarge])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Root Widget View
|
||||||
|
|
||||||
|
struct KisaniWidgetView: View {
|
||||||
|
@Environment(\.widgetFamily) private var family
|
||||||
|
let entry: KisaniEntry
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
switch family {
|
||||||
|
case .systemSmall: SmallWidget(entry: entry)
|
||||||
|
case .systemMedium: MediumWidget(entry: entry)
|
||||||
|
default: LargeWidget(entry: entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Shared Sub-Views
|
||||||
|
|
||||||
|
private struct DateBadge: View {
|
||||||
|
@Environment(\.colorScheme) private var cs
|
||||||
|
let size: CGFloat
|
||||||
|
|
||||||
|
private static let monthFmt: DateFormatter = {
|
||||||
|
let f = DateFormatter(); f.dateFormat = "MMM"; return f
|
||||||
|
}()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
let day = Calendar.current.component(.day, from: Date())
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
Text(Self.monthFmt.string(from: Date()).uppercased())
|
||||||
|
.font(.system(size: size * 0.18, weight: .bold, design: .monospaced))
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.kerning(1)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.frame(height: size * 0.32)
|
||||||
|
.background(WC.accent)
|
||||||
|
Text("\(day)")
|
||||||
|
.font(.system(size: size * 0.48, weight: .bold, design: .monospaced))
|
||||||
|
.foregroundColor(WC.text(cs))
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.frame(height: size * 0.68)
|
||||||
|
.background(WC.surface(cs))
|
||||||
|
}
|
||||||
|
.frame(width: size, height: size)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: size * 0.22))
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: size * 0.22)
|
||||||
|
.stroke(WC.border(cs), lineWidth: 1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct TaskRow: View {
|
||||||
|
@Environment(\.colorScheme) private var cs
|
||||||
|
let task: WidgetDataStore.WidgetTask
|
||||||
|
|
||||||
|
private static let fmt: DateFormatter = {
|
||||||
|
let f = DateFormatter(); f.dateFormat = "d MMM"; return f
|
||||||
|
}()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(spacing: 5) {
|
||||||
|
Circle()
|
||||||
|
.stroke(task.isOverdue ? WC.accent : WC.text3(cs), lineWidth: 1.2)
|
||||||
|
.frame(width: 7, height: 7)
|
||||||
|
Text(task.title)
|
||||||
|
.font(.system(size: 11, weight: .regular, design: .default))
|
||||||
|
.foregroundColor(WC.text(cs))
|
||||||
|
.lineLimit(1)
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
if let d = task.dueDate {
|
||||||
|
Text(task.isOverdue ? "Overdue" : Self.fmt.string(from: d))
|
||||||
|
.font(.system(size: 9, weight: .regular, design: .monospaced))
|
||||||
|
.foregroundColor(task.isOverdue ? WC.accent : WC.text3(cs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct WorkoutRow: View {
|
||||||
|
@Environment(\.colorScheme) private var cs
|
||||||
|
let workout: WidgetDataStore.WidgetWorkout
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(spacing: 6) {
|
||||||
|
WorkoutSegBar(count: workout.sectionsCount)
|
||||||
|
.frame(width: 22, height: 22)
|
||||||
|
Text(workout.name)
|
||||||
|
.font(.system(size: 11, weight: .semibold, design: .default))
|
||||||
|
.foregroundColor(WC.text(cs))
|
||||||
|
.lineLimit(1)
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
Text("Workout")
|
||||||
|
.font(.system(size: 9, weight: .regular, design: .monospaced))
|
||||||
|
.foregroundColor(WC.blue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct WorkoutSegBar: View {
|
||||||
|
@Environment(\.colorScheme) private var cs
|
||||||
|
let count: Int
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
let n = max(1, min(6, count))
|
||||||
|
VStack(spacing: 1.5) {
|
||||||
|
ForEach(0..<n, id: \.self) { _ in
|
||||||
|
RoundedRectangle(cornerRadius: 1)
|
||||||
|
.fill(WC.blue)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(4)
|
||||||
|
.background(WC.blue.opacity(0.12))
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 6))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Small Widget
|
||||||
|
|
||||||
|
private struct SmallWidget: View {
|
||||||
|
@Environment(\.colorScheme) private var cs
|
||||||
|
let entry: KisaniEntry
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
WC.bg(cs).ignoresSafeArea()
|
||||||
|
|
||||||
|
VStack(spacing: 10) {
|
||||||
|
DateBadge(size: 72)
|
||||||
|
|
||||||
|
VStack(spacing: 2) {
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Text("kisani")
|
||||||
|
.font(.system(size: 11, weight: .regular, design: .monospaced))
|
||||||
|
.foregroundColor(WC.text2(cs))
|
||||||
|
Text("CAL")
|
||||||
|
.font(.system(size: 11, weight: .bold, design: .monospaced))
|
||||||
|
.foregroundColor(WC.text(cs))
|
||||||
|
Text(".")
|
||||||
|
.font(.system(size: 11, weight: .bold, design: .monospaced))
|
||||||
|
.foregroundColor(WC.accent)
|
||||||
|
}
|
||||||
|
|
||||||
|
let todayCount = entry.todayTasks.count
|
||||||
|
let overdueCount = entry.overdueTasks.count
|
||||||
|
Group {
|
||||||
|
if overdueCount > 0 {
|
||||||
|
Text("\(overdueCount) overdue · \(todayCount) today")
|
||||||
|
.foregroundColor(WC.accent)
|
||||||
|
} else if todayCount > 0 {
|
||||||
|
Text("\(todayCount) task\(todayCount == 1 ? "" : "s") today")
|
||||||
|
.foregroundColor(WC.text2(cs))
|
||||||
|
} else {
|
||||||
|
Text("All clear")
|
||||||
|
.foregroundColor(WC.text3(cs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.font(.system(size: 9, weight: .regular, design: .monospaced))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(14)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Medium Widget
|
||||||
|
|
||||||
|
private struct MediumWidget: View {
|
||||||
|
@Environment(\.colorScheme) private var cs
|
||||||
|
let entry: KisaniEntry
|
||||||
|
|
||||||
|
private var displayTasks: [WidgetDataStore.WidgetTask] {
|
||||||
|
let all = entry.overdueTasks + entry.todayTasks
|
||||||
|
return Array(all.prefix(3))
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
WC.bg(cs).ignoresSafeArea()
|
||||||
|
|
||||||
|
HStack(alignment: .top, spacing: 14) {
|
||||||
|
// Left: badge + brand
|
||||||
|
VStack(spacing: 8) {
|
||||||
|
DateBadge(size: 64)
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Text("kisani")
|
||||||
|
.font(.system(size: 9, weight: .regular, design: .monospaced))
|
||||||
|
.foregroundColor(WC.text2(cs))
|
||||||
|
Text("CAL.")
|
||||||
|
.font(.system(size: 9, weight: .bold, design: .monospaced))
|
||||||
|
.foregroundColor(WC.accent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right: tasks + workout
|
||||||
|
VStack(alignment: .leading, spacing: 5) {
|
||||||
|
if displayTasks.isEmpty && entry.workout == nil {
|
||||||
|
Spacer()
|
||||||
|
Text("Nothing scheduled")
|
||||||
|
.font(.system(size: 11, design: .monospaced))
|
||||||
|
.foregroundColor(WC.text3(cs))
|
||||||
|
Spacer()
|
||||||
|
} else {
|
||||||
|
ForEach(displayTasks) { task in
|
||||||
|
TaskRow(task: task)
|
||||||
|
}
|
||||||
|
if let workout = entry.workout, workout.hasWorkoutToday,
|
||||||
|
displayTasks.count < 3 {
|
||||||
|
if !displayTasks.isEmpty {
|
||||||
|
Divider().opacity(0.4)
|
||||||
|
}
|
||||||
|
WorkoutRow(workout: workout)
|
||||||
|
}
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
.padding(14)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Large Widget
|
||||||
|
|
||||||
|
private struct LargeWidget: View {
|
||||||
|
@Environment(\.colorScheme) private var cs
|
||||||
|
let entry: KisaniEntry
|
||||||
|
|
||||||
|
private static let dayFmt: DateFormatter = {
|
||||||
|
let f = DateFormatter(); f.dateFormat = "EEEE, MMM d"; return f
|
||||||
|
}()
|
||||||
|
|
||||||
|
private var allTasks: [WidgetDataStore.WidgetTask] {
|
||||||
|
Array((entry.overdueTasks + entry.todayTasks).prefix(6))
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
WC.bg(cs).ignoresSafeArea()
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
|
// Header row
|
||||||
|
HStack(spacing: 12) {
|
||||||
|
DateBadge(size: 56)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 3) {
|
||||||
|
HStack(spacing: 0) {
|
||||||
|
Text("kisani")
|
||||||
|
.font(.system(size: 14, weight: .regular, design: .monospaced))
|
||||||
|
.foregroundColor(WC.text2(cs))
|
||||||
|
Text("CAL")
|
||||||
|
.font(.system(size: 14, weight: .bold, design: .monospaced))
|
||||||
|
.foregroundColor(WC.text(cs))
|
||||||
|
Text(".")
|
||||||
|
.font(.system(size: 14, weight: .bold, design: .monospaced))
|
||||||
|
.foregroundColor(WC.accent)
|
||||||
|
}
|
||||||
|
Text(Self.dayFmt.string(from: Date()))
|
||||||
|
.font(.system(size: 10, design: .monospaced))
|
||||||
|
.foregroundColor(WC.text2(cs))
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
let total = entry.todayTasks.count + entry.overdueTasks.count
|
||||||
|
VStack(alignment: .trailing, spacing: 2) {
|
||||||
|
Text("\(total)")
|
||||||
|
.font(.system(size: 22, weight: .bold, design: .monospaced))
|
||||||
|
.foregroundColor(entry.overdueTasks.isEmpty ? WC.text(cs) : WC.accent)
|
||||||
|
Text("tasks")
|
||||||
|
.font(.system(size: 9, design: .monospaced))
|
||||||
|
.foregroundColor(WC.text3(cs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 14)
|
||||||
|
.padding(.top, 14)
|
||||||
|
.padding(.bottom, 12)
|
||||||
|
|
||||||
|
Divider().opacity(0.4).padding(.horizontal, 14)
|
||||||
|
|
||||||
|
// Task list
|
||||||
|
VStack(alignment: .leading, spacing: 7) {
|
||||||
|
if allTasks.isEmpty {
|
||||||
|
HStack {
|
||||||
|
Spacer()
|
||||||
|
Text("Nothing due today")
|
||||||
|
.font(.system(size: 11, design: .monospaced))
|
||||||
|
.foregroundColor(WC.text3(cs))
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.padding(.vertical, 8)
|
||||||
|
} else {
|
||||||
|
ForEach(allTasks) { task in
|
||||||
|
TaskRow(task: task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal, 14)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
|
||||||
|
Spacer(minLength: 0)
|
||||||
|
|
||||||
|
// Workout row
|
||||||
|
if let workout = entry.workout, workout.hasWorkoutToday {
|
||||||
|
Divider().opacity(0.4).padding(.horizontal, 14)
|
||||||
|
WorkoutRow(workout: workout)
|
||||||
|
.padding(.horizontal, 14)
|
||||||
|
.padding(.vertical, 10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
Shared/WidgetDataStore.swift
Normal file
61
Shared/WidgetDataStore.swift
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
// Shared between app and widget extension via App Group UserDefaults.
|
||||||
|
// Types are pure Codable — no UIKit / AppColors dependency.
|
||||||
|
|
||||||
|
struct WidgetDataStore {
|
||||||
|
static let suiteName = "group.com.kutesir.KisaniCal"
|
||||||
|
static let tasksKey = "kisani.widget.tasks"
|
||||||
|
static let workoutKey = "kisani.widget.workout"
|
||||||
|
|
||||||
|
// MARK: - Shared types
|
||||||
|
|
||||||
|
struct WidgetTask: Codable, Identifiable {
|
||||||
|
let id: UUID
|
||||||
|
let title: String
|
||||||
|
let dueDate: Date?
|
||||||
|
let isToday: Bool
|
||||||
|
let isOverdue: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WidgetWorkout: Codable {
|
||||||
|
let name: String
|
||||||
|
let sectionsCount: Int
|
||||||
|
let hasWorkoutToday: Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Write (app target)
|
||||||
|
|
||||||
|
static func saveTasks(_ tasks: [WidgetTask]) {
|
||||||
|
guard let data = try? JSONEncoder().encode(tasks),
|
||||||
|
let ud = UserDefaults(suiteName: suiteName) else { return }
|
||||||
|
ud.set(data, forKey: tasksKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func saveWorkout(_ workout: WidgetWorkout?) {
|
||||||
|
guard let ud = UserDefaults(suiteName: suiteName) else { return }
|
||||||
|
if let workout, let data = try? JSONEncoder().encode(workout) {
|
||||||
|
ud.set(data, forKey: workoutKey)
|
||||||
|
} else {
|
||||||
|
ud.removeObject(forKey: workoutKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Read (widget target)
|
||||||
|
|
||||||
|
static func loadTasks() -> [WidgetTask] {
|
||||||
|
guard let ud = UserDefaults(suiteName: suiteName),
|
||||||
|
let data = ud.data(forKey: tasksKey),
|
||||||
|
let list = try? JSONDecoder().decode([WidgetTask].self, from: data)
|
||||||
|
else { return [] }
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
static func loadWorkout() -> WidgetWorkout? {
|
||||||
|
guard let ud = UserDefaults(suiteName: suiteName),
|
||||||
|
let data = ud.data(forKey: workoutKey),
|
||||||
|
let workout = try? JSONDecoder().decode(WidgetWorkout.self, from: data)
|
||||||
|
else { return nil }
|
||||||
|
return workout
|
||||||
|
}
|
||||||
|
}
|
||||||
23
project.yml
23
project.yml
@@ -13,6 +13,7 @@ targets:
|
|||||||
deploymentTarget: "16.0"
|
deploymentTarget: "16.0"
|
||||||
sources:
|
sources:
|
||||||
- path: KisaniCal
|
- path: KisaniCal
|
||||||
|
- path: Shared
|
||||||
settings:
|
settings:
|
||||||
base:
|
base:
|
||||||
SWIFT_VERSION: 5.9
|
SWIFT_VERSION: 5.9
|
||||||
@@ -28,5 +29,27 @@ targets:
|
|||||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor
|
||||||
CODE_SIGN_STYLE: Automatic
|
CODE_SIGN_STYLE: Automatic
|
||||||
ENABLE_PREVIEWS: YES
|
ENABLE_PREVIEWS: YES
|
||||||
|
CODE_SIGN_ENTITLEMENTS: KisaniCal/KisaniCal.entitlements
|
||||||
INFOPLIST_KEY_NSCalendarsUsageDescription: "Kisani Cal shows your calendar events alongside tasks."
|
INFOPLIST_KEY_NSCalendarsUsageDescription: "Kisani Cal shows your calendar events alongside tasks."
|
||||||
INFOPLIST_KEY_NSCalendarsFullAccessUsageDescription: "Kisani Cal shows your calendar events alongside tasks."
|
INFOPLIST_KEY_NSCalendarsFullAccessUsageDescription: "Kisani Cal shows your calendar events alongside tasks."
|
||||||
|
dependencies:
|
||||||
|
- target: KisaniCalWidget
|
||||||
|
embed: true
|
||||||
|
|
||||||
|
KisaniCalWidget:
|
||||||
|
type: app-extension
|
||||||
|
platform: iOS
|
||||||
|
deploymentTarget: "16.0"
|
||||||
|
sources:
|
||||||
|
- path: KisaniCalWidget
|
||||||
|
- path: Shared
|
||||||
|
settings:
|
||||||
|
base:
|
||||||
|
SWIFT_VERSION: 5.9
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER: com.kutesir.KisaniCal.widget
|
||||||
|
GENERATE_INFOPLIST_FILE: YES
|
||||||
|
INFOPLIST_KEY_NSExtensionPointIdentifier: com.apple.widgetkit-extension
|
||||||
|
CODE_SIGN_STYLE: Automatic
|
||||||
|
CODE_SIGN_ENTITLEMENTS: KisaniCalWidget/KisaniCalWidget.entitlements
|
||||||
|
SKIP_INSTALL: YES
|
||||||
|
LD_RUNPATH_SEARCH_PATHS: "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"
|
||||||
|
|||||||
Reference in New Issue
Block a user