-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathFeatureFlagManager.swift
More file actions
109 lines (88 loc) · 3.54 KB
/
FeatureFlagManager.swift
File metadata and controls
109 lines (88 loc) · 3.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import Client
import Combine
import Foundation
import GitHubCopilotService
import Logger
import SwiftUI
/// Centralized manager for GitHub Copilot feature flags in the HostApp
/// Use as @StateObject or @ObservedObject in SwiftUI views
@MainActor
public class FeatureFlagManager: ObservableObject {
public static let shared = FeatureFlagManager()
// MARK: - Published Properties
@Published public private(set) var isAgentModeEnabled = true
@Published public private(set) var isBYOKEnabled = true
@Published public private(set) var isMCPEnabled = true
@Published public private(set) var isEditorPreviewEnabled = true
@Published public private(set) var isChatEnabled = true
@Published public private(set) var isCodeReviewEnabled = true
// MARK: - Private Properties
private var cancellables = Set<AnyCancellable>()
private var lastUpdateTime: Date?
private let updateThrottle: TimeInterval = 1.0 // Prevent excessive updates
// MARK: - Initialization
private init() {
setupNotificationObserver()
Task {
await updateFeatureFlags()
}
}
// MARK: - Public Methods
/// Manually refresh feature flags from the service
public func refresh() async {
await updateFeatureFlags()
}
// MARK: - Private Methods
private func setupNotificationObserver() {
DistributedNotificationCenter.default()
.publisher(for: .gitHubCopilotFeatureFlagsDidChange)
.sink { [weak self] _ in
Task { @MainActor [weak self] in
await self?.updateFeatureFlags()
}
}
.store(in: &cancellables)
}
private func updateFeatureFlags() async {
// Throttle updates to prevent excessive calls
if let lastUpdate = lastUpdateTime,
Date().timeIntervalSince(lastUpdate) < updateThrottle {
return
}
lastUpdateTime = Date()
do {
let service = try getService()
guard let featureFlags = try await service.getCopilotFeatureFlags() else {
Logger.client.info("Feature flags returned nil, using defaults")
return
}
// Update all flags at once
isAgentModeEnabled = featureFlags.agentMode
isBYOKEnabled = featureFlags.byok
isMCPEnabled = featureFlags.mcp
isEditorPreviewEnabled = featureFlags.editorPreviewFeatures
isChatEnabled = featureFlags.chat
isCodeReviewEnabled = featureFlags.ccr
Logger.client.info("Feature flags updated: agentMode=\(featureFlags.agentMode), byok=\(featureFlags.byok), mcp=\(featureFlags.mcp), editorPreview=\(featureFlags.editorPreviewFeatures)")
} catch {
Logger.client.error("Failed to update feature flags: \(error.localizedDescription)")
}
}
}
// MARK: - Environment Key
private struct FeatureFlagManagerKey: EnvironmentKey {
static let defaultValue = FeatureFlagManager.shared
}
public extension EnvironmentValues {
var featureFlagManager: FeatureFlagManager {
get { self[FeatureFlagManagerKey.self] }
set { self[FeatureFlagManagerKey.self] = newValue }
}
}
// MARK: - View Extension
public extension View {
/// Inject the feature flag manager into the environment
func withFeatureFlagManager(_ manager: FeatureFlagManager = .shared) -> some View {
self.environment(\.featureFlagManager, manager)
}
}