Skip to content

Commit 41c8ad5

Browse files
chore: adds experimental plugin functionality (#442)
# Add Plugin Support to iOS SDK This PR adds plugin support to the iOS client SDK, implementing Swift equivalents of the Android plugin architecture from PR #303. ## Changes Made ### New Files Created - **LaunchDarkly/LaunchDarkly/Models/Plugins/EnvironmentMetadata.swift** - Contains application info, SDK metadata, and credential for environment context - **LaunchDarkly/LaunchDarkly/Models/Plugins/SdkMetadata.swift** - SDK name and version metadata - **LaunchDarkly/LaunchDarkly/Models/Plugins/PluginMetadata.swift** - Plugin identification metadata - **LaunchDarkly/LaunchDarkly/Models/Plugin.swift** - Main Plugin protocol with register() and getHooks() methods ### Modified Files - **LaunchDarkly/LaunchDarkly/Models/LDConfig.swift** - Added plugin support similar to existing hooks configuration ## Implementation Details The implementation follows the same architectural patterns established by the existing Hooks feature: - Uses Swift protocols instead of abstract classes (following Swift conventions) - Maintains the same folder structure and naming patterns as Hooks - Integrates with LDConfig using the same pattern as hooks configuration - Provides default implementations in protocol extensions where appropriate ## Link to Devin run https://app.devin.ai/sessions/e688c304311243a7a3948545dcd754ca ## Requested by tanderson@launchdarkly.com --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: tanderson@launchdarkly.com <tanderson@launchdarkly.com>
1 parent 0b7feff commit 41c8ad5

File tree

9 files changed

+384
-4
lines changed

9 files changed

+384
-4
lines changed

LaunchDarkly.xcodeproj/project.pbxproj

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,23 @@
1515
29FE1299280413D4008CC918 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29FE1297280413D4008CC918 /* Util.swift */; };
1616
29FE129A280413D4008CC918 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29FE1297280413D4008CC918 /* Util.swift */; };
1717
29FE129B280413D4008CC918 /* Util.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29FE1297280413D4008CC918 /* Util.swift */; };
18+
3D2406142E0D90E000F91253 /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406102E0D90E000F91253 /* Plugin.swift */; };
19+
3D2406152E0D90E000F91253 /* PluginMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406112E0D90E000F91253 /* PluginMetadata.swift */; };
20+
3D2406162E0D90E000F91253 /* EnvironmentMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D24060F2E0D90E000F91253 /* EnvironmentMetadata.swift */; };
21+
3D2406172E0D90E000F91253 /* SdkMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406122E0D90E000F91253 /* SdkMetadata.swift */; };
22+
3D2406182E0D90E000F91253 /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406102E0D90E000F91253 /* Plugin.swift */; };
23+
3D2406192E0D90E000F91253 /* PluginMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406112E0D90E000F91253 /* PluginMetadata.swift */; };
24+
3D24061A2E0D90E000F91253 /* EnvironmentMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D24060F2E0D90E000F91253 /* EnvironmentMetadata.swift */; };
25+
3D24061B2E0D90E000F91253 /* SdkMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406122E0D90E000F91253 /* SdkMetadata.swift */; };
26+
3D24061C2E0D90E000F91253 /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406102E0D90E000F91253 /* Plugin.swift */; };
27+
3D24061D2E0D90E000F91253 /* PluginMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406112E0D90E000F91253 /* PluginMetadata.swift */; };
28+
3D24061E2E0D90E000F91253 /* EnvironmentMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D24060F2E0D90E000F91253 /* EnvironmentMetadata.swift */; };
29+
3D24061F2E0D90E000F91253 /* SdkMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406122E0D90E000F91253 /* SdkMetadata.swift */; };
30+
3D2406202E0D90E000F91253 /* Plugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406102E0D90E000F91253 /* Plugin.swift */; };
31+
3D2406212E0D90E000F91253 /* PluginMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406112E0D90E000F91253 /* PluginMetadata.swift */; };
32+
3D2406222E0D90E000F91253 /* EnvironmentMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D24060F2E0D90E000F91253 /* EnvironmentMetadata.swift */; };
33+
3D2406232E0D90E000F91253 /* SdkMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406122E0D90E000F91253 /* SdkMetadata.swift */; };
34+
3D2406252E0D90EA00F91253 /* LDClientPluginsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D2406242E0D90EA00F91253 /* LDClientPluginsSpec.swift */; };
1835
3D3AB9432A4F16FE003AECF1 /* ReportingConsts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3AB9422A4F16FE003AECF1 /* ReportingConsts.swift */; };
1936
3D3AB9442A4F16FE003AECF1 /* ReportingConsts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3AB9422A4F16FE003AECF1 /* ReportingConsts.swift */; };
2037
3D3AB9452A4F16FE003AECF1 /* ReportingConsts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D3AB9422A4F16FE003AECF1 /* ReportingConsts.swift */; };
@@ -288,7 +305,6 @@
288305
A3C6F7652B84EF0C005B3B61 /* IdentifyTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3C6F7632B84EF0C005B3B61 /* IdentifyTypes.swift */; };
289306
A3C6F7662B84EF0C005B3B61 /* IdentifyTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3C6F7632B84EF0C005B3B61 /* IdentifyTypes.swift */; };
290307
A3C6F7672B84EF0C005B3B61 /* IdentifyTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3C6F7632B84EF0C005B3B61 /* IdentifyTypes.swift */; };
291-
A3F4A4812CC2F640006EF480 /* CwlPreconditionTesting in Frameworks */ = {isa = PBXBuildFile; productRef = A3F4A4802CC2F640006EF480 /* CwlPreconditionTesting */; };
292308
A3FFE1132B7D4BA2009EF93F /* LDValueDecoderSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3FFE1122B7D4BA2009EF93F /* LDValueDecoderSpec.swift */; };
293309
B40B419C249ADA6B00CD0726 /* DiagnosticCacheSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40B419B249ADA6B00CD0726 /* DiagnosticCacheSpec.swift */; };
294310
B4265EB124E7390C001CFD2C /* TestUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4265EB024E7390C001CFD2C /* TestUtil.swift */; };
@@ -405,6 +421,11 @@
405421
/* Begin PBXFileReference section */
406422
29F9D19D2812E005008D12C0 /* ObjcLDValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjcLDValue.swift; sourceTree = "<group>"; };
407423
29FE1297280413D4008CC918 /* Util.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Util.swift; sourceTree = "<group>"; };
424+
3D24060F2E0D90E000F91253 /* EnvironmentMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentMetadata.swift; sourceTree = "<group>"; };
425+
3D2406102E0D90E000F91253 /* Plugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Plugin.swift; sourceTree = "<group>"; };
426+
3D2406112E0D90E000F91253 /* PluginMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PluginMetadata.swift; sourceTree = "<group>"; };
427+
3D2406122E0D90E000F91253 /* SdkMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SdkMetadata.swift; sourceTree = "<group>"; };
428+
3D2406242E0D90EA00F91253 /* LDClientPluginsSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LDClientPluginsSpec.swift; sourceTree = "<group>"; };
408429
3D3AB9422A4F16FE003AECF1 /* ReportingConsts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportingConsts.swift; sourceTree = "<group>"; };
409430
3D3AB9472A570F3A003AECF1 /* ModifierSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifierSpec.swift; sourceTree = "<group>"; };
410431
3D9A12572A73236800698B8D /* UtilSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UtilSpec.swift; sourceTree = "<group>"; };
@@ -582,6 +603,17 @@
582603
/* End PBXFrameworksBuildPhase section */
583604

584605
/* Begin PBXGroup section */
606+
3D2406132E0D90E000F91253 /* Plugins */ = {
607+
isa = PBXGroup;
608+
children = (
609+
3D24060F2E0D90E000F91253 /* EnvironmentMetadata.swift */,
610+
3D2406102E0D90E000F91253 /* Plugin.swift */,
611+
3D2406112E0D90E000F91253 /* PluginMetadata.swift */,
612+
3D2406122E0D90E000F91253 /* SdkMetadata.swift */,
613+
);
614+
path = Plugins;
615+
sourceTree = "<group>";
616+
};
585617
831D8B701F71D3A600ED65E8 /* Networking */ = {
586618
isa = PBXGroup;
587619
children = (
@@ -700,6 +732,7 @@
700732
8354EFCF1F22491C00C05156 /* LaunchDarklyTests */ = {
701733
isa = PBXGroup;
702734
children = (
735+
3D2406242E0D90EA00F91253 /* LDClientPluginsSpec.swift */,
703736
A3BA7D012BD192240000DB28 /* LDClientHookSpec.swift */,
704737
838F96731FB9F024009CFC45 /* LDClientSpec.swift */,
705738
3D9A12572A73236800698B8D /* UtilSpec.swift */,
@@ -720,6 +753,7 @@
720753
8354EFE61F263E4200C05156 /* Models */ = {
721754
isa = PBXGroup;
722755
children = (
756+
3D2406132E0D90E000F91253 /* Plugins */,
723757
A3BA7CE72BD056780000DB28 /* Hooks */,
724758
A31088132837DC0400184942 /* Context */,
725759
C408884823033B7500420721 /* ConnectionInformation.swift */,
@@ -1309,6 +1343,10 @@
13091343
8311885F2113AE2D00D77CB5 /* HTTPURLRequest.swift in Sources */,
13101344
A36EDFD02853C50B00D91B05 /* ObjcLDContext.swift in Sources */,
13111345
B4C9D4362489C8FD004A9B03 /* DiagnosticCache.swift in Sources */,
1346+
3D2406142E0D90E000F91253 /* Plugin.swift in Sources */,
1347+
3D2406152E0D90E000F91253 /* PluginMetadata.swift in Sources */,
1348+
3D2406162E0D90E000F91253 /* EnvironmentMetadata.swift in Sources */,
1349+
3D2406172E0D90E000F91253 /* SdkMetadata.swift in Sources */,
13121350
A358D6DA2A4DE6A500270C60 /* ApplicationInfoEnvironmentReporter.swift in Sources */,
13131351
831188452113ADC500D77CB5 /* LDClient.swift in Sources */,
13141352
A3BA7CF12BD059180000DB28 /* Metadata.swift in Sources */,
@@ -1389,6 +1427,10 @@
13891427
831EF34B20655E730001C643 /* LDChangedFlag.swift in Sources */,
13901428
A36EDFCA2853883400D91B05 /* ObjcLDReference.swift in Sources */,
13911429
8354AC722243166900CDE602 /* FeatureFlagCache.swift in Sources */,
1430+
3D2406182E0D90E000F91253 /* Plugin.swift in Sources */,
1431+
3D2406192E0D90E000F91253 /* PluginMetadata.swift in Sources */,
1432+
3D24061A2E0D90E000F91253 /* EnvironmentMetadata.swift in Sources */,
1433+
3D24061B2E0D90E000F91253 /* SdkMetadata.swift in Sources */,
13921434
A310881D2837DC0400184942 /* Kind.swift in Sources */,
13931435
A35AD4622A619E45005A8DCB /* SystemCapabilities.swift in Sources */,
13941436
C443A40423145FBE00145710 /* ConnectionInformation.swift in Sources */,
@@ -1454,6 +1496,10 @@
14541496
830BF933202D188E006DF9B1 /* HTTPURLRequest.swift in Sources */,
14551497
B4C9D4332489C8FD004A9B03 /* DiagnosticCache.swift in Sources */,
14561498
A36EDFCD2853C50B00D91B05 /* ObjcLDContext.swift in Sources */,
1499+
3D24061C2E0D90E000F91253 /* Plugin.swift in Sources */,
1500+
3D24061D2E0D90E000F91253 /* PluginMetadata.swift in Sources */,
1501+
3D24061E2E0D90E000F91253 /* EnvironmentMetadata.swift in Sources */,
1502+
3D24061F2E0D90E000F91253 /* SdkMetadata.swift in Sources */,
14571503
A358D6D72A4DE6A500270C60 /* ApplicationInfoEnvironmentReporter.swift in Sources */,
14581504
8354EFE51F263DAC00C05156 /* FeatureFlag.swift in Sources */,
14591505
A3BA7CEE2BD059180000DB28 /* Metadata.swift in Sources */,
@@ -1518,6 +1564,7 @@
15181564
isa = PBXSourcesBuildPhase;
15191565
buildActionMask = 2147483647;
15201566
files = (
1567+
3D2406252E0D90EA00F91253 /* LDClientPluginsSpec.swift in Sources */,
15211568
A3047D662A606B6000F568E0 /* EnvironmentReporterChainBaseSpec.swift in Sources */,
15221569
A31088272837DCA900184942 /* LDContextSpec.swift in Sources */,
15231570
83CFE7CE1F7AD81D0010544E /* EventReporterSpec.swift in Sources */,
@@ -1580,6 +1627,10 @@
15801627
83D9EC7C2062DEAB004D7FA6 /* FeatureFlag.swift in Sources */,
15811628
A36EDFCE2853C50B00D91B05 /* ObjcLDContext.swift in Sources */,
15821629
8372668D20D4439600BD1088 /* DateFormatter.swift in Sources */,
1630+
3D2406202E0D90E000F91253 /* Plugin.swift in Sources */,
1631+
3D2406212E0D90E000F91253 /* PluginMetadata.swift in Sources */,
1632+
3D2406222E0D90E000F91253 /* EnvironmentMetadata.swift in Sources */,
1633+
3D2406232E0D90E000F91253 /* SdkMetadata.swift in Sources */,
15831634
A358D6D82A4DE6A500270C60 /* ApplicationInfoEnvironmentReporter.swift in Sources */,
15841635
83D9EC7D2062DEAB004D7FA6 /* LDChangedFlag.swift in Sources */,
15851636
A3BA7CEF2BD059180000DB28 /* Metadata.swift in Sources */,

LaunchDarkly/GeneratedCode/mocks.generated.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,17 @@ final class FeatureFlagCachingMock: FeatureFlagCaching {
255255
return getCachedDataReturnValue
256256
}
257257

258+
var getCachedDataLastUpdatedDateCallCount = 0
259+
var getCachedDataLastUpdatedDateCallback: (() throws -> Void)?
260+
var getCachedDataLastUpdatedDateReceivedArguments: (cacheKey: String, contextHash: String)?
261+
var getCachedDataLastUpdatedDateReturnValue: Date?
262+
func getCachedDataLastUpdatedDate(cacheKey: String, contextHash: String) -> Date? {
263+
getCachedDataLastUpdatedDateCallCount += 1
264+
getCachedDataLastUpdatedDateReceivedArguments = (cacheKey: cacheKey, contextHash: contextHash)
265+
try! getCachedDataLastUpdatedDateCallback?()
266+
return getCachedDataLastUpdatedDateReturnValue
267+
}
268+
258269
var saveCachedDataCallCount = 0
259270
var saveCachedDataCallback: (() throws -> Void)?
260271
var saveCachedDataReceivedArguments: (storedItems: StoredItems, cacheKey: String, contextHash: String, lastUpdated: Date, etag: String?)?

LaunchDarkly/LaunchDarkly/LDClient.swift

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ public class LDClient {
273273

274274
let config: LDConfig
275275
let service: DarklyServiceProvider
276-
let hooks: [Hook]
276+
var hooks: [Hook]
277277
private(set) var context: LDContext
278278

279279
/**
@@ -801,10 +801,37 @@ public class LDClient {
801801
for (name, mobileKey) in mobileKeys {
802802
var internalConfig = config
803803
internalConfig.mobileKey = mobileKey
804-
let instance = LDClient(serviceFactory: serviceFactory, configuration: internalConfig, startContext: context, completion: completionCheck)
804+
let instance: LDClient = LDClient(serviceFactory: serviceFactory, configuration: internalConfig, startContext: context, completion: completionCheck)
805805
instancesQueue.sync(flags: .barrier) {
806806
LDClient.instances?[name] = instance
807807
}
808+
809+
let sdkMetadata = SdkMetadata(name: SystemCapabilities.systemName, version: ReportingConsts.sdkVersion)
810+
let environmentMetadata = EnvironmentMetadata(
811+
applicationInfo: instance.environmentReporter.applicationInfo,
812+
sdkMetadata: sdkMetadata,
813+
credential: mobileKey
814+
)
815+
816+
// add all the plugin hooks
817+
for plugin in config.plugins {
818+
// Catch to protect against any runtime exceptions from plugin
819+
do {
820+
let pluginHooks = try plugin.getHooks(metadata: environmentMetadata)
821+
instance.hooks.append(contentsOf: pluginHooks)
822+
} catch {
823+
os_log("Exception thrown getting hooks for plugin %@. Unable to get hooks, plugin will not be registered.", log: config.logger, type: .error, plugin.getMetadata().getName())
824+
}
825+
}
826+
827+
// now register the client with all the plugins
828+
for plugin in config.plugins {
829+
do {
830+
plugin.register(client: instance, metadata: environmentMetadata)
831+
} catch {
832+
os_log("Exception thrown registering plugin %@.", log: config.logger, type: .error, plugin.getMetadata().getName())
833+
}
834+
}
808835
}
809836

810837
completionCheck()
@@ -894,7 +921,7 @@ public class LDClient {
894921

895922
private init(serviceFactory: ClientServiceCreating, configuration: LDConfig, startContext: LDContext?, completion: (() -> Void)? = nil) {
896923
self.serviceFactory = serviceFactory
897-
self.hooks = configuration.hooks
924+
self.hooks = Array(configuration.hooks)
898925
environmentReporter = self.serviceFactory.makeEnvironmentReporter(config: configuration)
899926
flagCache = self.serviceFactory.makeFeatureFlagCache(mobileKey: configuration.mobileKey, maxCachedContexts: configuration.maxCachedContexts)
900927
flagStore = self.serviceFactory.makeFlagStore()

LaunchDarkly/LaunchDarkly/Models/LDConfig.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,8 @@ public struct LDConfig {
253253

254254
static let hooks: [Hook] = []
255255

256+
static let plugins: [Plugin] = []
257+
256258
/// The default logger for the SDK. Can be overridden to provide customization.
257259
static let logger: OSLog = OSLog(subsystem: "com.launchdarkly", category: "ios-client-sdk")
258260

@@ -445,6 +447,11 @@ public struct LDConfig {
445447
/// Hooks provide entry points which allow for observation of SDK functions.
446448
public var hooks: [Hook] = Defaults.hooks
447449

450+
/// Initial set of plugins for the client.
451+
///
452+
/// Plugins provide a way to extend the functionality of the LaunchDarkly SDK. Each plugin can register hooks
453+
public var plugins: [Plugin] = Defaults.plugins
454+
448455
/// A Dictionary of identifying names to unique mobile keys for all environments
449456
private var mobileKeys: [String: String] {
450457
var internalMobileKeys = getSecondaryMobileKeys()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Foundation
2+
3+
/// Metadata about the environment that flag evaluations or other functionalities are being performed in.
4+
///
5+
/// This class provides context information to plugins about the environment they are running in,
6+
/// including application information, SDK metadata, and authentication credentials.
7+
public class EnvironmentMetadata {
8+
/// Application information for the application this SDK is used in.
9+
public let applicationInfo: ApplicationInfo?
10+
11+
/// SDK metadata for the LaunchDarkly SDK.
12+
public let sdkMetadata: SdkMetadata
13+
14+
/// Credential for authentication to LaunchDarkly endpoints for this environment.
15+
public let credential: String
16+
17+
/// Initialize environment metadata.
18+
///
19+
/// - Parameters:
20+
/// - applicationInfo: Application information for the application this SDK is used in
21+
/// - sdkMetadata: SDK metadata for the LaunchDarkly SDK
22+
/// - credential: Credential for authentication to LaunchDarkly endpoints for this environment
23+
public init(applicationInfo: ApplicationInfo?, sdkMetadata: SdkMetadata, credential: String) {
24+
self.applicationInfo = applicationInfo
25+
self.sdkMetadata = sdkMetadata
26+
self.credential = credential
27+
}
28+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import Foundation
2+
3+
/// Protocol for extending SDK functionality via plugins.
4+
///
5+
/// Plugins provide a way to extend the functionality of the LaunchDarkly SDK. A plugin can register hooks
6+
/// that are called during flag evaluation, and can perform initialization when registered with a client instance.
7+
///
8+
/// Plugin implementations should be thread-safe as they may be called concurrently from multiple threads.
9+
///
10+
/// ## Usage Example
11+
///
12+
/// ```swift
13+
/// class MyPlugin: Plugin {
14+
/// func getMetadata() -> PluginMetadata {
15+
/// return PluginMetadata(name: "MyPlugin")
16+
/// }
17+
///
18+
/// func register(client: LDClient, metadata: EnvironmentMetadata) {
19+
/// // Perform plugin initialization
20+
/// }
21+
///
22+
/// func getHooks(metadata: EnvironmentMetadata) -> [Hook] {
23+
/// return [MyHook()]
24+
/// }
25+
/// }
26+
///
27+
/// let config = LDConfig.Builder(mobileKey: "your-mobile-key")
28+
/// .plugins([MyPlugin()])
29+
/// .build()
30+
/// ```
31+
public protocol Plugin {
32+
/// Get metadata about the plugin implementation.
33+
///
34+
/// - Returns: The plugin metadata containing identifying information about the plugin.
35+
func getMetadata() -> PluginMetadata
36+
37+
/// Register the plugin with a client instance.
38+
///
39+
/// This method is called once for each client instance when the SDK is initialized. If
40+
/// the SDK is configured with multiple environments, this method will be called once for each
41+
/// environment with the respective credential. Use the metadata to distinguish environments.
42+
///
43+
/// - Parameters:
44+
/// - client: The client instance for the plugin to use
45+
/// - metadata: Metadata about the environment where the plugin is running
46+
func register(client: LDClient, metadata: EnvironmentMetadata)
47+
48+
/// Get hooks that should be registered with the SDK.
49+
///
50+
/// This method is called during SDK initialization to collect hooks from all plugins. If
51+
/// the SDK is configured with multiple environments, this method will be called once for each
52+
/// environment. Use the metadata to distinguish environments.
53+
///
54+
/// - Parameter metadata: Metadata about the environment where the plugin is running
55+
/// - Returns: An array of hooks to be registered with the SDK
56+
func getHooks(metadata: EnvironmentMetadata) -> [Hook]
57+
}
58+
59+
public extension Plugin {
60+
/// Get hooks that should be registered with the SDK.
61+
///
62+
/// Default implementation returns an empty array. Override this method to provide hooks.
63+
///
64+
/// - Parameter metadata: Metadata about the environment where the plugin is running
65+
/// - Returns: An empty array of hooks by default
66+
func getHooks(metadata: EnvironmentMetadata) -> [Hook] {
67+
return []
68+
}
69+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import Foundation
2+
3+
/// Metadata used for annotating plugin implementations.
4+
///
5+
/// This class provides identifying information about a plugin, primarily used
6+
/// for logging and debugging purposes.
7+
public class PluginMetadata {
8+
/// The name of the plugin.
9+
private let name: String
10+
11+
/// Initialize plugin metadata.
12+
///
13+
/// - Parameter name: The name of the plugin for identification purposes
14+
public init(name: String) {
15+
self.name = name
16+
}
17+
18+
/// Get the name of the plugin.
19+
///
20+
/// - Returns: The name of the plugin for identification purposes
21+
public func getName() -> String {
22+
return name
23+
}
24+
}

0 commit comments

Comments
 (0)