@shaykec/app-agent 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/.claude/skills/ui-tester/SKILL.md +59 -0
  2. package/.cursor/skills/android-customizer/SKILL.md +50 -16
  3. package/.cursor/skills/appconfig-customization/SKILL.md +15 -0
  4. package/.cursor/skills/customization-planner/SKILL.md +17 -1
  5. package/.cursor/skills/ios-customizer/SKILL.md +54 -13
  6. package/.cursor/skills/mock-data-update/SKILL.md +11 -3
  7. package/.cursor/skills/ui-tester/SKILL.md +59 -0
  8. package/dist/cli.d.ts +18 -4
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +134 -14
  11. package/dist/cli.js.map +1 -1
  12. package/dist/github.d.ts +5 -0
  13. package/dist/github.d.ts.map +1 -1
  14. package/dist/github.js +27 -0
  15. package/dist/github.js.map +1 -1
  16. package/dist/index.js +218 -24
  17. package/dist/index.js.map +1 -1
  18. package/dist/preview-generator.d.ts +35 -0
  19. package/dist/preview-generator.d.ts.map +1 -0
  20. package/dist/preview-generator.js +546 -0
  21. package/dist/preview-generator.js.map +1 -0
  22. package/package.json +4 -1
  23. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/AppConfig.kt +9 -0
  24. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/data/DataSourceResolver.kt +23 -0
  25. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/booking/BookingScreen.kt +6 -4
  26. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/bookings/BookingsScreen.kt +7 -5
  27. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/discovery/DiscoveryViewModel.kt +3 -2
  28. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/discovery/ProviderDetailScreen.kt +1 -1
  29. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/discovery/SearchScreen.kt +1 -1
  30. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/features/profile/ProfileScreen.kt +4 -2
  31. package/templates/android/BookTemplate/app/src/main/kotlin/com/appship/book/mock/MockDataProvider.kt +12 -11
  32. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/AppConfig.kt +9 -0
  33. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/data/DataRepository.kt +23 -0
  34. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/data/DataSourceResolver.kt +23 -0
  35. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/calls/CallsScreen.kt +4 -3
  36. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/contacts/ContactsScreen.kt +3 -2
  37. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/conversations/ChatScreen.kt +11 -10
  38. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/conversations/ConversationsScreen.kt +5 -5
  39. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/navigation/Navigation.kt +4 -2
  40. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/features/settings/SettingsScreen.kt +6 -4
  41. package/templates/android/ChatTemplate/app/src/main/kotlin/com/appship/chat/mock/MockDataProvider.kt +7 -7
  42. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/data/DataRepository.kt +46 -0
  43. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/data/DataSourceResolver.kt +23 -0
  44. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/alerts/AlertsScreen.kt +4 -3
  45. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/dashboard/DashboardScreen.kt +3 -2
  46. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/datasources/DataSourcesScreen.kt +3 -2
  47. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/features/reports/ReportsScreen.kt +3 -2
  48. package/templates/android/DashTemplate/app/src/main/kotlin/com/appship/dash/mock/MockDataProvider.kt +16 -15
  49. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/AppConfig.kt +9 -0
  50. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/calendar/CalendarScreen.kt +5 -5
  51. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/chores/ChoreBoardScreen.kt +3 -3
  52. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/familyhub/FamilyHubScreen.kt +4 -4
  53. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/gallery/GalleryScreen.kt +3 -3
  54. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/members/MembersListScreen.kt +5 -5
  55. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/rewards/RewardsScreen.kt +5 -5
  56. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/features/settings/SettingsScreen.kt +3 -3
  57. package/templates/android/FamilyTemplate/app/src/main/java/com/appship/family/mock/MockDataProvider.kt +29 -32
  58. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/AppConfig.kt +9 -0
  59. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/data/DataRepository.kt +70 -0
  60. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/data/DataSourceResolver.kt +23 -0
  61. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/budgets/BudgetsScreen.kt +3 -2
  62. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/goals/GoalsScreen.kt +5 -3
  63. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/home/HomeScreen.kt +8 -7
  64. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/settings/SettingsScreen.kt +0 -1
  65. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/features/transactions/TransactionsScreen.kt +9 -4
  66. package/templates/android/FinanceTemplate/app/src/main/kotlin/com/appship/finance/mock/MockDataProvider.kt +103 -11
  67. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/data/DataRepository.kt +19 -0
  68. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/leaderboard/LeaderboardScreen.kt +2 -1
  69. package/templates/android/GameTemplate/app/src/main/kotlin/com/appship/game/features/results/ResultsScreen.kt +3 -2
  70. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/data/DataRepository.kt +46 -0
  71. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/data/DataSourceResolver.kt +23 -0
  72. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/features/dashboard/DashboardScreen.kt +3 -2
  73. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/features/profile/ProfileScreen.kt +3 -2
  74. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/features/workouts/WorkoutListScreen.kt +3 -2
  75. package/templates/android/HealthTemplate/app/src/main/kotlin/com/appship/health/mock/MockDataProvider.kt +13 -13
  76. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/AppConfig.kt +9 -0
  77. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/data/DataRepository.kt +48 -0
  78. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/data/DataSourceResolver.kt +23 -0
  79. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/courses/CourseListScreen.kt +4 -3
  80. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/leaderboard/LeaderboardScreen.kt +3 -2
  81. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/profile/ProfileScreen.kt +3 -2
  82. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/features/progress/ProgressDashboardScreen.kt +3 -2
  83. package/templates/android/LearnTemplate/app/src/main/kotlin/com/appship/learn/mock/MockDataProvider.kt +41 -11
  84. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/AppConfig.kt +9 -0
  85. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/data/DataRepository.kt +33 -0
  86. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/data/DataSourceResolver.kt +28 -0
  87. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/di/AppModule.kt +11 -2
  88. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/favorites/FavoritesViewModel.kt +4 -4
  89. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/map/MapViewModel.kt +4 -4
  90. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/place/PlaceDetailViewModel.kt +5 -5
  91. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/place/PlaceListViewModel.kt +4 -4
  92. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/profile/ProfileViewModel.kt +5 -5
  93. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/route/RouteViewModel.kt +3 -3
  94. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/search/SearchViewModel.kt +3 -3
  95. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/features/settings/SettingsViewModel.kt +3 -3
  96. package/templates/android/MapTemplate/app/src/main/kotlin/com/appship/map/mock/MockDataProvider.kt +54 -41
  97. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/AppConfig.kt +9 -0
  98. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/browse/BrowseScreen.kt +2 -1
  99. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/detail/DetailScreen.kt +3 -1
  100. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/features/home/HomeScreen.kt +3 -2
  101. package/templates/android/MediaTemplate/app/src/main/kotlin/com/appship/media/mock/MockDataProvider.kt +63 -132
  102. package/templates/android/ReferenceTemplate/app/src/main/kotlin/com/appship/reference/AppConfig.kt +9 -0
  103. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/AppConfig.kt +9 -0
  104. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/data/DataRepository.kt +62 -0
  105. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/data/DataSourceResolver.kt +23 -0
  106. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/features/home/HomeViewModel.kt +2 -1
  107. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/features/products/ProductViewModels.kt +12 -7
  108. package/templates/android/ShopTemplate/app/src/main/kotlin/com/appship/shop/mock/MockDataProvider.kt +31 -31
  109. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/AppConfig.kt +9 -0
  110. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/data/DataRepository.kt +30 -0
  111. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/data/DataSourceResolver.kt +23 -0
  112. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/features/create/CreateScreen.kt +3 -2
  113. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/features/detail/DetailScreen.kt +4 -3
  114. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/features/explore/ExploreScreen.kt +7 -6
  115. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/features/favorites/FavoritesScreen.kt +4 -3
  116. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/features/home/HomeScreen.kt +5 -4
  117. package/templates/android/Skeleton/app/src/main/kotlin/com/appship/skeleton/mock/MockDataProvider.kt +12 -11
  118. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/AppConfig.kt +9 -0
  119. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/data/DataRepository.kt +37 -0
  120. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/data/DataSourceResolver.kt +23 -0
  121. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/createpost/CreatePostScreen.kt +3 -2
  122. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/feed/CommentsScreen.kt +4 -3
  123. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/feed/FeedScreen.kt +5 -4
  124. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/messages/MessagesScreen.kt +9 -8
  125. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/notifications/NotificationsScreen.kt +3 -2
  126. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/profile/ProfileScreen.kt +6 -5
  127. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/features/search/SearchScreen.kt +4 -3
  128. package/templates/android/SocialTemplate/app/src/main/kotlin/com/appship/social/mock/MockDataProvider.kt +7 -6
  129. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/AppConfig.kt +9 -0
  130. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/data/DataRepository.kt +38 -0
  131. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/data/DataSourceResolver.kt +23 -0
  132. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/calendar/CalendarScreen.kt +0 -1
  133. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/inbox/InboxScreen.kt +4 -2
  134. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/kanban/KanbanBoardScreen.kt +5 -3
  135. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/projects/ProjectDetailScreen.kt +9 -5
  136. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/projects/ProjectListScreen.kt +6 -4
  137. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/search/SearchScreen.kt +1 -1
  138. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/features/tasks/TaskDetailScreen.kt +0 -1
  139. package/templates/android/TaskTemplate/app/src/main/kotlin/com/appship/task/mock/MockDataProvider.kt +13 -12
  140. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/AppConfig.kt +9 -0
  141. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/data/DataRepository.kt +34 -0
  142. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/data/DataSourceResolver.kt +23 -0
  143. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/achievements/AchievementsViewModel.kt +4 -2
  144. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/dashboard/TodayViewModel.kt +4 -2
  145. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/habits/HabitDetailViewModel.kt +4 -2
  146. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/features/habits/HabitsListViewModel.kt +4 -2
  147. package/templates/android/TrackTemplate/app/src/main/kotlin/com/appship/track/mock/MockDataProvider.kt +13 -11
  148. package/templates/ios/BookTemplate/BookTemplate/App/AppConfig.swift +12 -2
  149. package/templates/ios/BookTemplate/BookTemplate/Data/DataRepository.swift +33 -0
  150. package/templates/ios/BookTemplate/BookTemplate/Data/DataSourceResolver.swift +20 -0
  151. package/templates/ios/BookTemplate/BookTemplate/Features/Booking/BookingView.swift +1 -1
  152. package/templates/ios/BookTemplate/BookTemplate/Features/Discovery/DiscoveryView.swift +3 -3
  153. package/templates/ios/BookTemplate/BookTemplate/Features/Discovery/MapExploreView.swift +1 -1
  154. package/templates/ios/BookTemplate/BookTemplate/Features/Discovery/ProviderDetailView.swift +1 -1
  155. package/templates/ios/BookTemplate/BookTemplate/Features/Discovery/SearchView.swift +2 -2
  156. package/templates/ios/BookTemplate/BookTemplate/MockData/MockDataProvider.swift +18 -1
  157. package/templates/ios/ChatTemplate/ChatTemplate/App/AppConfig.swift +9 -0
  158. package/templates/ios/ChatTemplate/ChatTemplate/Data/DataRepository.swift +43 -0
  159. package/templates/ios/ChatTemplate/ChatTemplate/Data/DataSourceResolver.swift +20 -0
  160. package/templates/ios/DashTemplate/DashTemplate/App/AppConfig.swift +9 -0
  161. package/templates/ios/DashTemplate/DashTemplate/Data/DataRepository.swift +53 -0
  162. package/templates/ios/DashTemplate/DashTemplate/MockData/MockDataProvider.swift +1 -1
  163. package/templates/ios/FamilyTemplate/FamilyTemplate/App/AppConfig.swift +9 -0
  164. package/templates/ios/FamilyTemplate/FamilyTemplate/App/FamilyTemplateApp.swift +4 -2
  165. package/templates/ios/FamilyTemplate/FamilyTemplate/MockData/MockDataProvider.swift +1 -1
  166. package/templates/ios/FinanceTemplate/FinanceTemplate/App/AppConfig.swift +9 -0
  167. package/templates/ios/FinanceTemplate/FinanceTemplate/Data/DataRepository.swift +75 -0
  168. package/templates/ios/FinanceTemplate/FinanceTemplate/Data/DataSourceResolver.swift +20 -0
  169. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Budgets/BudgetsView.swift +3 -1
  170. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Goals/GoalsView.swift +3 -1
  171. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Home/HomeView.swift +13 -6
  172. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Settings/SettingsView.swift +6 -2
  173. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Transactions/AddTransactionView.swift +4 -2
  174. package/templates/ios/FinanceTemplate/FinanceTemplate/Features/Transactions/TransactionsView.swift +3 -1
  175. package/templates/ios/FinanceTemplate/FinanceTemplate/MockData/MockDataProvider.swift +1 -1
  176. package/templates/ios/GameTemplate/GameTemplate/App/AppConfig.swift +9 -0
  177. package/templates/ios/GameTemplate/GameTemplate/Data/DataRepository.swift +30 -0
  178. package/templates/ios/GameTemplate/GameTemplate/Data/DataSourceResolver.swift +20 -0
  179. package/templates/ios/GameTemplate/GameTemplate/MockData/MockDataProvider.swift +1 -1
  180. package/templates/ios/HealthTemplate/HealthTemplate/App/AppConfig.swift +9 -0
  181. package/templates/ios/HealthTemplate/HealthTemplate/Data/DataRepository.swift +60 -0
  182. package/templates/ios/HealthTemplate/HealthTemplate/MockData/MockDataProvider.swift +1 -1
  183. package/templates/ios/LearnTemplate/LearnTemplate/App/AppConfig.swift +9 -0
  184. package/templates/ios/LearnTemplate/LearnTemplate/Features/Courses/CourseDetailView.swift +3 -2
  185. package/templates/ios/LearnTemplate/LearnTemplate/Features/Courses/CourseListView.swift +3 -2
  186. package/templates/ios/LearnTemplate/LearnTemplate/Features/Flashcards/FlashcardView.swift +2 -1
  187. package/templates/ios/LearnTemplate/LearnTemplate/Features/Leaderboard/LeaderboardView.swift +2 -1
  188. package/templates/ios/LearnTemplate/LearnTemplate/Features/Lessons/LessonPlayerView.swift +4 -2
  189. package/templates/ios/LearnTemplate/LearnTemplate/Features/Profile/ProfileView.swift +4 -2
  190. package/templates/ios/LearnTemplate/LearnTemplate/Features/Progress/ProgressDashboardView.swift +3 -2
  191. package/templates/ios/LearnTemplate/LearnTemplate/Features/Quiz/QuizView.swift +2 -1
  192. package/templates/ios/LearnTemplate/LearnTemplate/MockData/MockDataProvider.swift +1 -1
  193. package/templates/ios/MapTemplate/MapTemplate/App/AppConfig.swift +11 -9
  194. package/templates/ios/MapTemplate/MapTemplate/Data/DataSourceResolver.swift +20 -0
  195. package/templates/ios/MapTemplate/MapTemplate/Features/Favorites/FavoritesView.swift +1 -1
  196. package/templates/ios/MapTemplate/MapTemplate/Features/PlaceDetail/PlaceDetailView.swift +2 -2
  197. package/templates/ios/MapTemplate/MapTemplate/Features/Profile/ProfileView.swift +1 -1
  198. package/templates/ios/MapTemplate/MapTemplate/Features/Route/RouteView.swift +1 -1
  199. package/templates/ios/MapTemplate/MapTemplate/Features/Search/SearchView.swift +1 -1
  200. package/templates/ios/MediaTemplate/MediaTemplate/App/AppConfig.swift +9 -0
  201. package/templates/ios/MediaTemplate/MediaTemplate/Data/DataRepository.swift +52 -0
  202. package/templates/ios/MediaTemplate/MediaTemplate/Data/DataSourceResolver.swift +20 -0
  203. package/templates/ios/MediaTemplate/MediaTemplate/MockData/MockDataProvider.swift +1 -1
  204. package/templates/ios/ReferenceTemplate/ReferenceTemplate/App/AppConfig.swift +9 -0
  205. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Data/DataRepository.swift +28 -0
  206. package/templates/ios/ReferenceTemplate/ReferenceTemplate/Data/DataSourceResolver.swift +20 -0
  207. package/templates/ios/ReferenceTemplate/ReferenceTemplate/MockData/MockDataProvider.swift +1 -1
  208. package/templates/ios/ShopTemplate/ShopTemplate/App/AppConfig.swift +9 -0
  209. package/templates/ios/ShopTemplate/ShopTemplate/Data/DataSourceResolver.swift +20 -0
  210. package/templates/ios/ShopTemplate/ShopTemplate/Features/Home/HomeView.swift +9 -7
  211. package/templates/ios/ShopTemplate/ShopTemplate/Features/Orders/OrdersView.swift +7 -3
  212. package/templates/ios/ShopTemplate/ShopTemplate/Features/Products/ProductDetailView.swift +4 -2
  213. package/templates/ios/ShopTemplate/ShopTemplate/Features/Products/ProductListView.swift +6 -3
  214. package/templates/ios/ShopTemplate/ShopTemplate/MockData/MockDataProvider.swift +1 -1
  215. package/templates/ios/Skeleton/Skeleton/App/AppConfig.swift +9 -0
  216. package/templates/ios/Skeleton/Skeleton/Data/DataRepository.swift +30 -0
  217. package/templates/ios/Skeleton/Skeleton/Data/DataSourceResolver.swift +20 -0
  218. package/templates/ios/Skeleton/Skeleton/Features/Explore/ExploreViewModel.swift +7 -6
  219. package/templates/ios/Skeleton/Skeleton/Features/Home/HomeViewModel.swift +8 -7
  220. package/templates/ios/Skeleton/Skeleton/MockData/MockDataProvider.swift +1 -1
  221. package/templates/ios/SocialTemplate/SocialTemplate/App/AppConfig.swift +4 -3
  222. package/templates/ios/SocialTemplate/SocialTemplate/Data/DataRepository.swift +37 -0
  223. package/templates/ios/SocialTemplate/SocialTemplate/Data/DataSourceResolver.swift +20 -0
  224. package/templates/ios/SocialTemplate/SocialTemplate/Data/SyncManager.swift +1 -1
  225. package/templates/ios/SocialTemplate/SocialTemplate/Features/CreatePost/CreatePostView.swift +2 -2
  226. package/templates/ios/SocialTemplate/SocialTemplate/Features/Feed/CommentsView.swift +2 -2
  227. package/templates/ios/SocialTemplate/SocialTemplate/Features/Feed/FeedView.swift +1 -1
  228. package/templates/ios/SocialTemplate/SocialTemplate/Features/Messages/MessagesView.swift +4 -4
  229. package/templates/ios/SocialTemplate/SocialTemplate/Features/Notifications/NotificationsView.swift +1 -1
  230. package/templates/ios/SocialTemplate/SocialTemplate/Features/Profile/ProfileView.swift +1 -1
  231. package/templates/ios/SocialTemplate/SocialTemplate/Features/Search/SearchView.swift +5 -4
  232. package/templates/ios/SocialTemplate/SocialTemplate/MockData/MockDataProvider.swift +1 -1
  233. package/templates/ios/SocialTemplate/SocialTemplate/Models/Models.swift +2 -2
  234. package/templates/ios/TaskTemplate/TaskTemplate/App/AppConfig.swift +9 -0
  235. package/templates/ios/TaskTemplate/TaskTemplate/Data/DataRepository.swift +38 -0
  236. package/templates/ios/TaskTemplate/TaskTemplate/Data/DataSourceResolver.swift +20 -0
  237. package/templates/ios/TaskTemplate/TaskTemplate/Features/Calendar/CalendarView.swift +6 -2
  238. package/templates/ios/TaskTemplate/TaskTemplate/Features/Inbox/InboxView.swift +2 -1
  239. package/templates/ios/TaskTemplate/TaskTemplate/Features/Kanban/KanbanBoardView.swift +9 -3
  240. package/templates/ios/TaskTemplate/TaskTemplate/Features/Projects/ProjectDetailView.swift +3 -1
  241. package/templates/ios/TaskTemplate/TaskTemplate/Features/Projects/ProjectListView.swift +3 -1
  242. package/templates/ios/TaskTemplate/TaskTemplate/Features/Search/SearchView.swift +3 -1
  243. package/templates/ios/TaskTemplate/TaskTemplate/MockData/MockDataProvider.swift +1 -1
  244. package/templates/ios/TrackTemplate/TrackTemplate/App/AppConfig.swift +9 -0
  245. package/templates/ios/TrackTemplate/TrackTemplate/Data/DataRepository.swift +29 -0
  246. package/templates/ios/TrackTemplate/TrackTemplate/Data/DataSourceResolver.swift +20 -0
  247. package/templates/ios/TrackTemplate/TrackTemplate/MockData/MockDataProvider.swift +1 -1
  248. package/templates/ios/TrackTemplate/TrackTemplate/Services/HabitManager.swift +1 -1
@@ -253,15 +253,17 @@ class ProductDetailViewModel: ObservableObject {
253
253
  @Published var isInWishlist = false
254
254
 
255
255
  private let wishlistManager = WishlistManager()
256
+ private let repository: any DataRepository
256
257
 
257
- init(product: Product) {
258
+ init(product: Product, repository: any DataRepository = DataSourceResolver.repository) {
258
259
  self.product = product
260
+ self.repository = repository
259
261
  self.isInWishlist = wishlistManager.isInWishlist(product)
260
262
  loadReviews()
261
263
  }
262
264
 
263
265
  func loadReviews() {
264
- reviews = MockDataProvider.shared.reviews(for: product.id)
266
+ reviews = repository.reviews(for: product.id)
265
267
  }
266
268
 
267
269
  func toggleWishlist() {
@@ -222,8 +222,11 @@ class ProductListViewModel: ObservableObject {
222
222
  return result
223
223
  }
224
224
 
225
- init(category: Category?, products: [Product]? = nil) {
225
+ private let repository: any DataRepository
226
+
227
+ init(category: Category?, products: [Product]? = nil, repository: any DataRepository = DataSourceResolver.repository) {
226
228
  self.category = category
229
+ self.repository = repository
227
230
  if let products = products {
228
231
  self.products = products
229
232
  } else {
@@ -237,9 +240,9 @@ class ProductListViewModel: ObservableObject {
237
240
  DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
238
241
  guard let self = self else { return }
239
242
  if let category = self.category {
240
- self.products = MockDataProvider.shared.products(for: category)
243
+ self.products = self.repository.products(for: category)
241
244
  } else {
242
- self.products = MockDataProvider.shared.products
245
+ self.products = self.repository.products
243
246
  }
244
247
  self.isLoading = false
245
248
  }
@@ -5,7 +5,7 @@ import Combine
5
5
  // CUSTOMIZE:API - Replace mock data with real API calls
6
6
  // Mutable observable singleton. Seed data lives in `default*` static properties.
7
7
  // The agent customizes seed data; CRUD methods and infrastructure are generic.
8
- class MockDataProvider: ObservableObject {
8
+ class MockDataProvider: ObservableObject, DataRepository {
9
9
  static let shared = MockDataProvider()
10
10
 
11
11
  // MARK: - Published Mutable State
@@ -83,6 +83,15 @@ struct AppConfig {
83
83
  }
84
84
  }
85
85
 
86
+ // CUSTOMIZE:DATASOURCE - Data source configuration
87
+ struct DataSource {
88
+ enum SourceType {
89
+ case localStorage // Core Data — real local database (DEFAULT)
90
+ case mock // In-memory seed data — for development/testing only
91
+ }
92
+ static let active: SourceType = .mock
93
+ }
94
+
86
95
  // CUSTOMIZE:CATEGORIES - Domain categories for content
87
96
  static let categories: [(name: String, icon: String)] = [
88
97
  ("Category 1", "folder.fill"),
@@ -0,0 +1,30 @@
1
+ import Foundation
2
+
3
+ /// Protocol abstracting the data layer. Both MockDataProvider and LocalStorageProvider
4
+ /// conform to this, allowing the app to swap data sources via AppConfig.DataSource.active.
5
+ ///
6
+ /// ViewModels depend on this protocol (via `any DataRepository`) rather than on a concrete
7
+ /// implementation. Use `DataSourceResolver.repository` to get the active provider.
8
+ protocol DataRepository: AnyObject {
9
+ // MARK: - Observable State
10
+
11
+ var items: [Item] { get }
12
+ var categories: [Category] { get }
13
+ var favoriteItems: [Item] { get }
14
+
15
+ // MARK: - Data Loading
16
+
17
+ func loadData()
18
+
19
+ // MARK: - CRUD Operations
20
+
21
+ func addItem(_ item: Item)
22
+ func updateItem(_ item: Item)
23
+ func deleteItem(_ item: Item)
24
+ func toggleFavorite(_ item: Item)
25
+
26
+ // MARK: - Queries
27
+
28
+ func searchItems(query: String) -> [Item]
29
+ func itemsByCategory(_ categoryName: String) -> [Item]
30
+ }
@@ -0,0 +1,20 @@
1
+ import Foundation
2
+
3
+ /// Resolves the active DataRepository implementation based on AppConfig.DataSource.active.
4
+ /// ViewModels use this to obtain the correct data source without knowing the concrete type.
5
+ ///
6
+ /// Usage:
7
+ /// let repo = DataSourceResolver.repository
8
+ /// let items = repo.items
9
+ enum DataSourceResolver {
10
+ /// Returns the active DataRepository based on the current configuration.
11
+ static var repository: any DataRepository {
12
+ switch AppConfig.DataSource.active {
13
+ case .localStorage:
14
+ // TODO: Return LocalStorageProvider.shared once implemented
15
+ return MockDataProvider.shared
16
+ case .mock:
17
+ return MockDataProvider.shared
18
+ }
19
+ }
20
+ }
@@ -6,21 +6,22 @@ class ExploreViewModel: ObservableObject {
6
6
  @Published var categories: [Category] = []
7
7
  @Published var searchResults: [Item] = []
8
8
 
9
- private let dataProvider = MockDataProvider.shared
9
+ private let repository: any DataRepository
10
10
 
11
- init() {
12
- categories = dataProvider.categories
11
+ init(repository: any DataRepository = DataSourceResolver.repository) {
12
+ self.repository = repository
13
+ categories = repository.categories
13
14
  }
14
15
 
15
16
  func search(query: String) {
16
- searchResults = dataProvider.searchItems(query: query)
17
+ searchResults = repository.searchItems(query: query)
17
18
  }
18
19
 
19
20
  func itemsForCategory(_ name: String) -> [Item] {
20
- dataProvider.itemsByCategory(name)
21
+ repository.itemsByCategory(name)
21
22
  }
22
23
 
23
24
  func toggleFavorite(_ item: Item) {
24
- dataProvider.toggleFavorite(item)
25
+ repository.toggleFavorite(item)
25
26
  }
26
27
  }
@@ -9,20 +9,21 @@ class HomeViewModel: ObservableObject {
9
9
  @Published var filteredItems: [Item] = []
10
10
  @Published var selectedCategory: Category?
11
11
 
12
- private let dataProvider = MockDataProvider.shared
12
+ private let repository: any DataRepository
13
13
 
14
- init() {
14
+ init(repository: any DataRepository = DataSourceResolver.repository) {
15
+ self.repository = repository
15
16
  loadData()
16
17
  }
17
18
 
18
19
  func loadData() {
19
- items = dataProvider.items
20
- categories = dataProvider.categories
20
+ items = repository.items
21
+ categories = repository.categories
21
22
  filteredItems = items
22
23
  }
23
24
 
24
25
  func refresh() {
25
- dataProvider.loadData()
26
+ repository.loadData()
26
27
  loadData()
27
28
  }
28
29
 
@@ -30,7 +31,7 @@ class HomeViewModel: ObservableObject {
30
31
  if query.isEmpty {
31
32
  applyFilters()
32
33
  } else {
33
- let searched = dataProvider.searchItems(query: query)
34
+ let searched = repository.searchItems(query: query)
34
35
  if let cat = selectedCategory {
35
36
  filteredItems = searched.filter { $0.category == cat.name }
36
37
  } else {
@@ -49,7 +50,7 @@ class HomeViewModel: ObservableObject {
49
50
  }
50
51
 
51
52
  func toggleFavorite(_ item: Item) {
52
- dataProvider.toggleFavorite(item)
53
+ repository.toggleFavorite(item)
53
54
  loadData()
54
55
  }
55
56
 
@@ -4,7 +4,7 @@ import SwiftUI
4
4
  // PLACEHOLDER: Replace sample data with domain-specific content.
5
5
  // Keep the singleton pattern and CRUD interface — just change the data.
6
6
 
7
- class MockDataProvider: ObservableObject {
7
+ class MockDataProvider: ObservableObject, DataRepository {
8
8
  static let shared = MockDataProvider()
9
9
 
10
10
  @Published var items: [Item] = []
@@ -111,7 +111,7 @@ class AuthManager: ObservableObject {
111
111
  try await Task.sleep(nanoseconds: 1_000_000_000)
112
112
 
113
113
  await MainActor.run {
114
- currentUser = MockDataProvider.shared.currentUser
114
+ currentUser = DataSourceResolver.repository.currentUser
115
115
  isAuthenticated = true
116
116
  isLoading = false
117
117
  }
@@ -136,8 +136,9 @@ class FeedManager: ObservableObject {
136
136
  try? await Task.sleep(nanoseconds: 500_000_000)
137
137
 
138
138
  await MainActor.run {
139
- posts = MockDataProvider.shared.posts
140
- stories = MockDataProvider.shared.stories
139
+ let repo = DataSourceResolver.repository
140
+ posts = repo.posts
141
+ stories = repo.stories
141
142
  isLoading = false
142
143
  }
143
144
  }
@@ -0,0 +1,37 @@
1
+ import Foundation
2
+
3
+ /// Protocol abstracting the data layer. Both MockDataProvider and LocalStorageProvider
4
+ /// conform to this, allowing the app to swap data sources via AppConfig.DataSource.active.
5
+ ///
6
+ /// ViewModels depend on this protocol (via `any DataRepository`) rather than on a concrete
7
+ /// implementation. Use `DataSourceResolver.repository` to get the active provider.
8
+ protocol DataRepository: AnyObject {
9
+ // MARK: - Observable State
10
+
11
+ var currentUser: User { get }
12
+ var users: [User] { get }
13
+ var posts: [Post] { get }
14
+ var stories: [Story] { get }
15
+ var conversations: [Conversation] { get }
16
+ var notifications: [AppNotification] { get }
17
+ var trendingHashtags: [Hashtag] { get }
18
+
19
+ // MARK: - Helper Functions
20
+
21
+ func comments(for postId: String) -> [Comment]
22
+
23
+ // MARK: - CRUD Operations
24
+
25
+ func addPost(_ post: Post)
26
+ func likePost(_ postId: String)
27
+ func unlikePost(_ postId: String)
28
+ func savePost(_ postId: String)
29
+ func unsavePost(_ postId: String)
30
+ func addComment(_ comment: Comment, to postId: String)
31
+ func likeComment(_ commentId: String, in postId: String)
32
+ func followUser(_ userId: String)
33
+ func unfollowUser(_ userId: String)
34
+ func markNotificationAsRead(_ notificationId: String)
35
+ func sendMessage(_ message: Message, in conversationId: String)
36
+ func reset()
37
+ }
@@ -0,0 +1,20 @@
1
+ import Foundation
2
+
3
+ /// Resolves the active DataRepository implementation based on AppConfig.DataSource.active.
4
+ /// ViewModels use this to obtain the correct data source without knowing the concrete type.
5
+ ///
6
+ /// Usage:
7
+ /// let repo = DataSourceResolver.repository
8
+ /// let posts = repo.posts
9
+ enum DataSourceResolver {
10
+ /// Returns the active DataRepository based on the current configuration.
11
+ static var repository: any DataRepository {
12
+ switch AppConfig.DataSource.active {
13
+ case .localStorage:
14
+ // TODO: Return LocalStorageProvider.shared once implemented
15
+ return MockDataProvider.shared
16
+ case .mock:
17
+ return MockDataProvider.shared
18
+ }
19
+ }
20
+ }
@@ -7,7 +7,7 @@ import Combine
7
7
  class SyncManager: ObservableObject {
8
8
  static let shared = SyncManager()
9
9
 
10
- private let store = MockDataProvider.shared
10
+ private let store: any DataRepository = DataSourceResolver.repository
11
11
  private let network = NetworkMonitor.shared
12
12
 
13
13
  @Published var isSyncing: Bool = false
@@ -18,7 +18,7 @@ struct CreatePostView: View {
18
18
  VStack(alignment: .leading, spacing: 16) {
19
19
  // User Header
20
20
  HStack(spacing: 12) {
21
- AsyncImage(url: URL(string: MockDataProvider.shared.currentUser.avatarURL ?? "")) { image in
21
+ AsyncImage(url: URL(string: DataSourceResolver.repository.currentUser.avatarURL ?? "")) { image in
22
22
  image
23
23
  .resizable()
24
24
  .aspectRatio(contentMode: .fill)
@@ -30,7 +30,7 @@ struct CreatePostView: View {
30
30
  .clipShape(Circle())
31
31
 
32
32
  VStack(alignment: .leading) {
33
- Text(MockDataProvider.shared.currentUser.username)
33
+ Text(DataSourceResolver.repository.currentUser.username)
34
34
  .font(.subheadline)
35
35
  .fontWeight(.semibold)
36
36
 
@@ -23,7 +23,7 @@ struct CommentsView: View {
23
23
 
24
24
  // Comment Input
25
25
  HStack(spacing: 12) {
26
- AsyncImage(url: URL(string: MockDataProvider.shared.currentUser.avatarURL ?? "")) { image in
26
+ AsyncImage(url: URL(string: DataSourceResolver.repository.currentUser.avatarURL ?? "")) { image in
27
27
  image
28
28
  .resizable()
29
29
  .aspectRatio(contentMode: .fill)
@@ -57,7 +57,7 @@ struct CommentsView: View {
57
57
  }
58
58
  }
59
59
  .onAppear {
60
- comments = MockDataProvider.shared.comments(for: post.id)
60
+ comments = DataSourceResolver.repository.comments(for: post.id)
61
61
  }
62
62
  }
63
63
  }
@@ -58,7 +58,7 @@ struct StoriesBarView: View {
58
58
  // Add story button
59
59
  VStack(spacing: 4) {
60
60
  ZStack(alignment: .bottomTrailing) {
61
- AsyncImage(url: URL(string: MockDataProvider.shared.currentUser.avatarURL ?? "")) { image in
61
+ AsyncImage(url: URL(string: DataSourceResolver.repository.currentUser.avatarURL ?? "")) { image in
62
62
  image
63
63
  .resizable()
64
64
  .aspectRatio(contentMode: .fill)
@@ -40,7 +40,7 @@ struct MessagesView: View {
40
40
  }
41
41
  }
42
42
  .onAppear {
43
- conversations = MockDataProvider.shared.conversations
43
+ conversations = DataSourceResolver.repository.conversations
44
44
  }
45
45
  }
46
46
  }
@@ -113,7 +113,7 @@ struct ChatView: View {
113
113
  ForEach(messages) { message in
114
114
  MessageBubble(
115
115
  message: message,
116
- isFromCurrentUser: message.senderId == MockDataProvider.shared.currentUser.id
116
+ isFromCurrentUser: message.senderId == DataSourceResolver.repository.currentUser.id
117
117
  )
118
118
  }
119
119
  }
@@ -187,7 +187,7 @@ struct ChatView: View {
187
187
  messages = (1...20).map { i in
188
188
  Message(
189
189
  id: "m\(i)",
190
- senderId: i % 3 == 0 ? MockDataProvider.shared.currentUser.id : conversation.participants.first!.id,
190
+ senderId: i % 3 == 0 ? DataSourceResolver.repository.currentUser.id : conversation.participants.first!.id,
191
191
  content: ["Hey!", "How are you?", "Just saw your post!", "That looks amazing!", "Let's catch up soon"][i % 5],
192
192
  mediaURL: nil,
193
193
  mediaType: .none,
@@ -200,7 +200,7 @@ struct ChatView: View {
200
200
  private func sendMessage() {
201
201
  let message = Message(
202
202
  id: UUID().uuidString,
203
- senderId: MockDataProvider.shared.currentUser.id,
203
+ senderId: DataSourceResolver.repository.currentUser.id,
204
204
  content: newMessage,
205
205
  mediaURL: nil,
206
206
  mediaType: .none,
@@ -30,7 +30,7 @@ struct NotificationsView: View {
30
30
  }
31
31
  .navigationTitle("Activity")
32
32
  .onAppear {
33
- notifications = MockDataProvider.shared.notifications
33
+ notifications = DataSourceResolver.repository.notifications
34
34
  }
35
35
  }
36
36
  }
@@ -9,7 +9,7 @@ struct ProfileView: View {
9
9
  @State private var showEditProfile = false
10
10
 
11
11
  private var isOwnProfile: Bool {
12
- userId == nil || userId == MockDataProvider.shared.currentUser.id
12
+ userId == nil || userId == DataSourceResolver.repository.currentUser.id
13
13
  }
14
14
 
15
15
  var body: some View {
@@ -47,7 +47,8 @@ struct SearchView: View {
47
47
  return
48
48
  }
49
49
 
50
- searchResults = MockDataProvider.shared.users.filter {
50
+ let repo = DataSourceResolver.repository
51
+ searchResults = repo.users.filter {
51
52
  $0.username.lowercased().contains(query.lowercased()) ||
52
53
  $0.displayName.lowercased().contains(query.lowercased())
53
54
  }
@@ -61,7 +62,7 @@ struct TrendingHashtagsSection: View {
61
62
  .font(.headline)
62
63
  .padding(.horizontal)
63
64
 
64
- ForEach(MockDataProvider.shared.trendingHashtags.prefix(5)) { hashtag in
65
+ ForEach(DataSourceResolver.repository.trendingHashtags.prefix(5)) { hashtag in
65
66
  HStack {
66
67
  VStack(alignment: .leading, spacing: 2) {
67
68
  Text(hashtag.name)
@@ -103,7 +104,7 @@ struct SuggestedUsersSection: View {
103
104
 
104
105
  ScrollView(.horizontal, showsIndicators: false) {
105
106
  HStack(spacing: 12) {
106
- ForEach(MockDataProvider.shared.users.prefix(5)) { user in
107
+ ForEach(DataSourceResolver.repository.users.prefix(5)) { user in
107
108
  SuggestedUserCard(user: user)
108
109
  }
109
110
  }
@@ -177,7 +178,7 @@ struct ExploreGridSection: View {
177
178
  .padding(.horizontal)
178
179
 
179
180
  LazyVGrid(columns: columns, spacing: 2) {
180
- ForEach(Array(MockDataProvider.shared.posts.enumerated()), id: \.element.id) { index, post in
181
+ ForEach(Array(DataSourceResolver.repository.posts.enumerated()), id: \.element.id) { index, post in
181
182
  NavigationLink(destination: PostDetailView(post: post)) {
182
183
  AsyncImage(url: URL(string: post.mediaURLs.first ?? "")) { image in
183
184
  image
@@ -1,7 +1,7 @@
1
1
  import Foundation
2
2
  import Combine
3
3
 
4
- class MockDataProvider: ObservableObject {
4
+ class MockDataProvider: ObservableObject, DataRepository {
5
5
  static let shared = MockDataProvider()
6
6
 
7
7
  // MARK: - Published Mutable State
@@ -104,14 +104,14 @@ struct Conversation: Codable, Identifiable {
104
104
  if isGroup {
105
105
  return groupName ?? participants.map { $0.displayName }.joined(separator: ", ")
106
106
  }
107
- return participants.first { $0.id != MockDataProvider.shared.currentUser.id }?.displayName ?? ""
107
+ return participants.first { $0.id != DataSourceResolver.repository.currentUser.id }?.displayName ?? ""
108
108
  }
109
109
 
110
110
  var avatarURL: String? {
111
111
  if isGroup {
112
112
  return groupAvatarURL
113
113
  }
114
- return participants.first { $0.id != MockDataProvider.shared.currentUser.id }?.avatarURL
114
+ return participants.first { $0.id != DataSourceResolver.repository.currentUser.id }?.avatarURL
115
115
  }
116
116
  }
117
117
 
@@ -42,6 +42,15 @@ struct AppConfig {
42
42
  static let termsURL = "https://example.com/terms"
43
43
  }
44
44
 
45
+ // CUSTOMIZE:DATASOURCE - Data source configuration
46
+ struct DataSource {
47
+ enum SourceType {
48
+ case localStorage // Core Data — real local database (DEFAULT)
49
+ case mock // In-memory seed data — for development/testing only
50
+ }
51
+ static let active: SourceType = .mock
52
+ }
53
+
45
54
  // CUSTOMIZE:THEME
46
55
  struct Theme {
47
56
  static let primaryColor = Color(hex: "3B82F6") // Blue
@@ -0,0 +1,38 @@
1
+ import Foundation
2
+
3
+ /// Protocol abstracting the data layer. Both MockDataProvider and LocalStorageProvider
4
+ /// conform to this, allowing the app to swap data sources via AppConfig.DataSource.active.
5
+ ///
6
+ /// Views depend on this protocol (via `any DataRepository`) rather than on a concrete
7
+ /// implementation. Use `DataSourceResolver.repository` to get the active provider.
8
+ protocol DataRepository: AnyObject {
9
+ // MARK: - Observable State
10
+
11
+ var tasks: [TaskItem] { get }
12
+ var projects: [Project] { get }
13
+ var tags: [Tag] { get }
14
+ var kanbanColumns: [KanbanColumn] { get }
15
+
16
+ // MARK: - Task Operations
17
+
18
+ func getTasks() -> [TaskItem]
19
+ func getTask(id: String) -> TaskItem?
20
+ func createTask(_ task: TaskItem)
21
+ func updateTask(_ task: TaskItem)
22
+ func deleteTask(id: String)
23
+
24
+ // MARK: - Project Operations
25
+
26
+ func getProjects() -> [Project]
27
+ func getProject(id: String) -> Project?
28
+
29
+ // MARK: - Query Operations
30
+
31
+ func getTasksByTag(tagId: String) -> [TaskItem]
32
+ func getTasksByDueDate(_ date: Date) -> [TaskItem]
33
+
34
+ // MARK: - Kanban Operations
35
+
36
+ func getKanbanColumns() -> [KanbanColumn]
37
+ func moveTaskToColumn(taskId: String, columnId: String)
38
+ }
@@ -0,0 +1,20 @@
1
+ import Foundation
2
+
3
+ /// Resolves the active DataRepository implementation based on AppConfig.DataSource.active.
4
+ /// Views use this to obtain the correct data source without knowing the concrete type.
5
+ ///
6
+ /// Usage:
7
+ /// let repo = DataSourceResolver.repository
8
+ /// let tasks = repo.tasks
9
+ enum DataSourceResolver {
10
+ /// Returns the active DataRepository based on the current configuration.
11
+ static var repository: any DataRepository {
12
+ switch AppConfig.DataSource.active {
13
+ case .localStorage:
14
+ // TODO: Return LocalStorageProvider.shared once implemented
15
+ return MockDataProvider.shared
16
+ case .mock:
17
+ return MockDataProvider.shared
18
+ }
19
+ }
20
+ }
@@ -18,7 +18,9 @@ struct CalendarEvent: Identifiable {
18
18
  }
19
19
 
20
20
  struct CalendarView: View {
21
- @StateObject private var dataProvider = MockDataProvider.shared
21
+ @StateObject private var dataProvider: MockDataProvider = {
22
+ DataSourceResolver.repository as! MockDataProvider
23
+ }()
22
24
  @State private var selectedDate = Date()
23
25
  @State private var currentMonth = Date()
24
26
 
@@ -179,7 +181,9 @@ struct CalendarView: View {
179
181
 
180
182
  struct EventCardView: View {
181
183
  let event: CalendarEvent
182
- @StateObject private var dataProvider = MockDataProvider.shared
184
+ @StateObject private var dataProvider: MockDataProvider = {
185
+ DataSourceResolver.repository as! MockDataProvider
186
+ }()
183
187
 
184
188
  private var task: TaskItem? {
185
189
  dataProvider.getTask(id: event.taskId)
@@ -104,7 +104,8 @@ struct TaskRowView: View {
104
104
  }
105
105
 
106
106
  if !task.tags.isEmpty {
107
- let tags = MockDataProvider.shared.tags.filter { task.tags.contains($0.id) }
107
+ let repository = DataSourceResolver.repository as! MockDataProvider
108
+ let tags = repository.tags.filter { task.tags.contains($0.id) }
108
109
  TagChipListView(tags: tags)
109
110
  }
110
111
  }
@@ -1,7 +1,9 @@
1
1
  import SwiftUI
2
2
 
3
3
  struct KanbanBoardView: View {
4
- @StateObject private var dataProvider = MockDataProvider.shared
4
+ @StateObject private var dataProvider: MockDataProvider = {
5
+ DataSourceResolver.repository as! MockDataProvider
6
+ }()
5
7
  @State private var draggedTask: Task?
6
8
 
7
9
  private var columns: [KanbanColumn] {
@@ -27,7 +29,9 @@ struct KanbanBoardView: View {
27
29
  struct KanbanColumnView: View {
28
30
  let column: KanbanColumn
29
31
  @Binding var draggedTask: Task?
30
- @StateObject private var dataProvider = MockDataProvider.shared
32
+ @StateObject private var dataProvider: MockDataProvider = {
33
+ DataSourceResolver.repository as! MockDataProvider
34
+ }()
31
35
 
32
36
  private var columnTasks: [Task] {
33
37
  dataProvider.getTasks()
@@ -96,7 +100,9 @@ struct KanbanColumnView: View {
96
100
 
97
101
  struct KanbanTaskCard: View {
98
102
  let task: Task
99
- @StateObject private var dataProvider = MockDataProvider.shared
103
+ @StateObject private var dataProvider: MockDataProvider = {
104
+ DataSourceResolver.repository as! MockDataProvider
105
+ }()
100
106
 
101
107
  var body: some View {
102
108
  VStack(alignment: .leading, spacing: 8) {
@@ -2,7 +2,9 @@ import SwiftUI
2
2
 
3
3
  struct ProjectDetailView: View {
4
4
  let project: Project
5
- @StateObject private var dataProvider = MockDataProvider.shared
5
+ @StateObject private var dataProvider: MockDataProvider = {
6
+ DataSourceResolver.repository as! MockDataProvider
7
+ }()
6
8
  @State private var searchText = ""
7
9
  @State private var showingAddTask = false
8
10
 
@@ -1,7 +1,9 @@
1
1
  import SwiftUI
2
2
 
3
3
  struct ProjectListView: View {
4
- @StateObject private var dataProvider = MockDataProvider.shared
4
+ @StateObject private var dataProvider: MockDataProvider = {
5
+ DataSourceResolver.repository as! MockDataProvider
6
+ }()
5
7
  @State private var searchText = ""
6
8
  @State private var showingAddProject = false
7
9
 
@@ -1,7 +1,9 @@
1
1
  import SwiftUI
2
2
 
3
3
  struct SearchView: View {
4
- @StateObject private var dataProvider = MockDataProvider.shared
4
+ @StateObject private var dataProvider: MockDataProvider = {
5
+ DataSourceResolver.repository as! MockDataProvider
6
+ }()
5
7
  @State private var searchText = ""
6
8
  @State private var selectedFilter: SearchFilter = .all
7
9