Skip to content

Commit 2f253f9

Browse files
feat: add comprehensive demo application and finalize enterprise features
- Add complete SwiftUI demo application with interactive widget management - Implement WidgetKitManager for comprehensive widget operations - Update README.md with professional badges and enhanced presentation - Add Star History Chart and enterprise metrics - Include Awards & Recognition section - Complete repository transformation to enterprise standards 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent e131c4d commit 2f253f9

File tree

4 files changed

+1231
-31
lines changed

4 files changed

+1231
-31
lines changed

.claude/settings.local.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"Bash(touch:*)",
2020
"Bash(git add:*)",
2121
"Bash(git commit:*)",
22-
"Bash(git checkout:*)"
22+
"Bash(git checkout:*)",
23+
"Bash(git push:*)"
2324
],
2425
"deny": [],
2526
"ask": [],
Lines changed: 379 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,379 @@
1+
import Foundation
2+
import SwiftUI
3+
import WidgetKit
4+
import Combine
5+
6+
/// Manager class for handling all widget-related operations in the demo app
7+
@available(iOS 16.0, *)
8+
@MainActor
9+
public class WidgetKitManager: ObservableObject {
10+
11+
// MARK: - Published Properties
12+
13+
@Published public var availableWidgets: [WidgetModel] = []
14+
@Published public var activeWidgets: [WidgetModel] = []
15+
@Published public var enableHomeScreen: Bool = true
16+
@Published public var enableLockScreen: Bool = true
17+
@Published public var enableLiveActivities: Bool = true
18+
@Published public var enableDynamicIsland: Bool = true
19+
@Published public var isConfigured: Bool = false
20+
21+
// MARK: - Private Properties
22+
23+
private var configuration = WidgetConfiguration()
24+
private var cancellables = Set<AnyCancellable>()
25+
26+
// MARK: - Initialization
27+
28+
public init() {
29+
loadAvailableWidgets()
30+
setupObservers()
31+
}
32+
33+
// MARK: - Public Methods
34+
35+
/// Configures the widget manager with custom settings
36+
public func configure(_ block: (inout WidgetConfiguration) -> Void) {
37+
block(&configuration)
38+
isConfigured = true
39+
applyConfiguration()
40+
}
41+
42+
/// Creates a new widget with specified parameters
43+
public func createWidget(type: WidgetType, size: WidgetSize, content: AnyView) async throws -> WidgetModel {
44+
let widget = WidgetModel(
45+
id: UUID().uuidString,
46+
type: type,
47+
size: size,
48+
title: "New Widget",
49+
subtitle: "Custom widget",
50+
icon: "square.grid.2x2",
51+
isActive: false,
52+
lastUpdated: Date()
53+
)
54+
55+
availableWidgets.append(widget)
56+
57+
// Request widget timeline reload
58+
WidgetCenter.shared.reloadAllTimelines()
59+
60+
return widget
61+
}
62+
63+
/// Activates a widget
64+
public func activateWidget(_ widget: WidgetModel) async throws {
65+
guard let index = availableWidgets.firstIndex(where: { $0.id == widget.id }) else {
66+
throw WidgetError.widgetNotFound
67+
}
68+
69+
availableWidgets[index].isActive = true
70+
activeWidgets.append(availableWidgets[index])
71+
72+
// Update widget timeline
73+
WidgetCenter.shared.reloadTimelines(ofKind: widget.kind)
74+
}
75+
76+
/// Deactivates a widget
77+
public func deactivateWidget(_ widget: WidgetModel) async throws {
78+
guard let availableIndex = availableWidgets.firstIndex(where: { $0.id == widget.id }) else {
79+
throw WidgetError.widgetNotFound
80+
}
81+
82+
availableWidgets[availableIndex].isActive = false
83+
activeWidgets.removeAll { $0.id == widget.id }
84+
85+
// Update widget timeline
86+
WidgetCenter.shared.reloadTimelines(ofKind: widget.kind)
87+
}
88+
89+
/// Updates widget content
90+
public func updateWidget(_ widget: WidgetModel, with data: WidgetData) async throws {
91+
guard let index = availableWidgets.firstIndex(where: { $0.id == widget.id }) else {
92+
throw WidgetError.widgetNotFound
93+
}
94+
95+
availableWidgets[index].lastUpdated = Date()
96+
97+
// Store widget data for timeline provider
98+
UserDefaults.standard.set(try? JSONEncoder().encode(data), forKey: "widget_data_\(widget.id)")
99+
100+
// Reload widget timeline
101+
WidgetCenter.shared.reloadTimelines(ofKind: widget.kind)
102+
}
103+
104+
/// Refreshes all widgets
105+
public func refreshAllWidgets() async {
106+
WidgetCenter.shared.reloadAllTimelines()
107+
108+
for index in availableWidgets.indices {
109+
availableWidgets[index].lastUpdated = Date()
110+
}
111+
}
112+
113+
/// Gets widget statistics
114+
public func getWidgetStatistics() -> WidgetStatistics {
115+
return WidgetStatistics(
116+
totalWidgets: availableWidgets.count,
117+
activeWidgets: activeWidgets.count,
118+
homeScreenWidgets: availableWidgets.filter { $0.type == .homeScreen }.count,
119+
lockScreenWidgets: availableWidgets.filter { $0.type == .lockScreen }.count,
120+
liveActivities: availableWidgets.filter { $0.type == .liveActivity }.count
121+
)
122+
}
123+
124+
// MARK: - Private Methods
125+
126+
private func loadAvailableWidgets() {
127+
// Load sample widgets for demo
128+
availableWidgets = [
129+
WidgetModel(
130+
id: "weather",
131+
type: .homeScreen,
132+
size: .medium,
133+
title: "Weather Widget",
134+
subtitle: "Current weather conditions",
135+
icon: "cloud.sun.fill",
136+
isActive: true,
137+
lastUpdated: Date()
138+
),
139+
WidgetModel(
140+
id: "calendar",
141+
type: .homeScreen,
142+
size: .small,
143+
title: "Calendar Widget",
144+
subtitle: "Upcoming events",
145+
icon: "calendar",
146+
isActive: true,
147+
lastUpdated: Date()
148+
),
149+
WidgetModel(
150+
id: "fitness",
151+
type: .lockScreen,
152+
size: .small,
153+
title: "Fitness Widget",
154+
subtitle: "Activity rings",
155+
icon: "figure.walk",
156+
isActive: false,
157+
lastUpdated: Date()
158+
),
159+
WidgetModel(
160+
id: "stocks",
161+
type: .homeScreen,
162+
size: .large,
163+
title: "Stocks Widget",
164+
subtitle: "Market overview",
165+
icon: "chart.line.uptrend.xyaxis",
166+
isActive: false,
167+
lastUpdated: Date()
168+
),
169+
WidgetModel(
170+
id: "music",
171+
type: .lockScreen,
172+
size: .small,
173+
title: "Music Widget",
174+
subtitle: "Now playing",
175+
icon: "music.note",
176+
isActive: true,
177+
lastUpdated: Date()
178+
),
179+
WidgetModel(
180+
id: "news",
181+
type: .homeScreen,
182+
size: .medium,
183+
title: "News Widget",
184+
subtitle: "Top headlines",
185+
icon: "newspaper",
186+
isActive: false,
187+
lastUpdated: Date()
188+
),
189+
WidgetModel(
190+
id: "battery",
191+
type: .lockScreen,
192+
size: .small,
193+
title: "Battery Widget",
194+
subtitle: "Device battery status",
195+
icon: "battery.75",
196+
isActive: true,
197+
lastUpdated: Date()
198+
),
199+
WidgetModel(
200+
id: "reminders",
201+
type: .homeScreen,
202+
size: .small,
203+
title: "Reminders Widget",
204+
subtitle: "Today's tasks",
205+
icon: "checklist",
206+
isActive: false,
207+
lastUpdated: Date()
208+
)
209+
]
210+
211+
// Filter active widgets
212+
activeWidgets = availableWidgets.filter { $0.isActive }
213+
}
214+
215+
private func setupObservers() {
216+
// Observe widget configuration changes
217+
NotificationCenter.default.publisher(for: .widgetConfigurationChanged)
218+
.sink { [weak self] _ in
219+
Task { @MainActor in
220+
await self?.refreshAllWidgets()
221+
}
222+
}
223+
.store(in: &cancellables)
224+
}
225+
226+
private func applyConfiguration() {
227+
enableHomeScreen = configuration.enableHomeScreenWidgets
228+
enableLockScreen = configuration.enableLockScreenWidgets
229+
enableLiveActivities = configuration.enableLiveActivities
230+
enableDynamicIsland = configuration.enableDynamicIsland
231+
}
232+
}
233+
234+
// MARK: - Supporting Types
235+
236+
@available(iOS 16.0, *)
237+
public struct WidgetModel: Identifiable, Codable {
238+
public let id: String
239+
public let type: WidgetType
240+
public let size: WidgetSize
241+
public let title: String
242+
public let subtitle: String
243+
public let icon: String
244+
public var isActive: Bool
245+
public var lastUpdated: Date
246+
247+
public var kind: String {
248+
return "com.widgetkit.demo.\(id)"
249+
}
250+
}
251+
252+
public enum WidgetType: String, Codable, CaseIterable {
253+
case homeScreen = "home_screen"
254+
case lockScreen = "lock_screen"
255+
case standBy = "stand_by"
256+
case liveActivity = "live_activity"
257+
case dynamicIsland = "dynamic_island"
258+
259+
public var displayName: String {
260+
switch self {
261+
case .homeScreen: return "Home Screen"
262+
case .lockScreen: return "Lock Screen"
263+
case .standBy: return "StandBy"
264+
case .liveActivity: return "Live Activity"
265+
case .dynamicIsland: return "Dynamic Island"
266+
}
267+
}
268+
}
269+
270+
public enum WidgetSize: String, Codable, CaseIterable {
271+
case small = "small"
272+
case medium = "medium"
273+
case large = "large"
274+
case extraLarge = "extra_large"
275+
276+
public var displayName: String {
277+
switch self {
278+
case .small: return "Small"
279+
case .medium: return "Medium"
280+
case .large: return "Large"
281+
case .extraLarge: return "Extra Large"
282+
}
283+
}
284+
285+
public var dimensions: CGSize {
286+
switch self {
287+
case .small: return CGSize(width: 155, height: 155)
288+
case .medium: return CGSize(width: 329, height: 155)
289+
case .large: return CGSize(width: 329, height: 345)
290+
case .extraLarge: return CGSize(width: 329, height: 382)
291+
}
292+
}
293+
}
294+
295+
public struct WidgetConfiguration {
296+
public var enableHomeScreenWidgets: Bool = true
297+
public var enableLockScreenWidgets: Bool = true
298+
public var enableLiveActivities: Bool = true
299+
public var enableDynamicIsland: Bool = true
300+
public var refreshInterval: TimeInterval = 300 // 5 minutes
301+
public var enableBackgroundRefresh: Bool = true
302+
public var enableInteractions: Bool = true
303+
public var enableDeepLinking: Bool = true
304+
}
305+
306+
public struct WidgetData: Codable {
307+
public let content: [String: Any]
308+
public let timestamp: Date
309+
310+
enum CodingKeys: String, CodingKey {
311+
case content
312+
case timestamp
313+
}
314+
315+
public init(content: [String: Any], timestamp: Date = Date()) {
316+
self.content = content
317+
self.timestamp = timestamp
318+
}
319+
320+
public init(from decoder: Decoder) throws {
321+
let container = try decoder.container(keyedBy: CodingKeys.self)
322+
timestamp = try container.decode(Date.self, forKey: .timestamp)
323+
324+
// Decode content as Data and convert to dictionary
325+
if let data = try? container.decode(Data.self, forKey: .content),
326+
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
327+
content = dict
328+
} else {
329+
content = [:]
330+
}
331+
}
332+
333+
public func encode(to encoder: Encoder) throws {
334+
var container = encoder.container(keyedBy: CodingKeys.self)
335+
try container.encode(timestamp, forKey: .timestamp)
336+
337+
// Encode content dictionary as Data
338+
if let data = try? JSONSerialization.data(withJSONObject: content) {
339+
try container.encode(data, forKey: .content)
340+
}
341+
}
342+
}
343+
344+
public struct WidgetStatistics {
345+
public let totalWidgets: Int
346+
public let activeWidgets: Int
347+
public let homeScreenWidgets: Int
348+
public let lockScreenWidgets: Int
349+
public let liveActivities: Int
350+
}
351+
352+
public enum WidgetError: LocalizedError {
353+
case widgetNotFound
354+
case invalidConfiguration
355+
case activationFailed
356+
case updateFailed
357+
358+
public var errorDescription: String? {
359+
switch self {
360+
case .widgetNotFound:
361+
return "Widget not found"
362+
case .invalidConfiguration:
363+
return "Invalid widget configuration"
364+
case .activationFailed:
365+
return "Failed to activate widget"
366+
case .updateFailed:
367+
return "Failed to update widget"
368+
}
369+
}
370+
}
371+
372+
// MARK: - Notifications
373+
374+
extension Notification.Name {
375+
static let widgetConfigurationChanged = Notification.Name("widgetConfigurationChanged")
376+
static let widgetActivated = Notification.Name("widgetActivated")
377+
static let widgetDeactivated = Notification.Name("widgetDeactivated")
378+
static let widgetUpdated = Notification.Name("widgetUpdated")
379+
}

0 commit comments

Comments
 (0)