Skip to content

fix: activate a plugin's hooks only after it has registered - #525

Open
abelonogov-ld wants to merge 4 commits into
v11from
andrey/register-plugin2
Open

fix: activate a plugin's hooks only after it has registered#525
abelonogov-ld wants to merge 4 commits into
v11from
andrey/register-plugin2

Conversation

@abelonogov-ld

@abelonogov-ld abelonogov-ld commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #522. A plugin's hooks now go live only once every plugin has registered, and this applies to plugins in LDConfig.plugins and to registerPlugin alike, so a plugin behaves the same however it was registered.

Previously registerPlugin activated a plugin's hooks before calling register, which meant a plugin's own hooks observed the evaluations and identify calls its register made. Activating them last also means no plugin's hooks observe another plugin's register, rather than that depending on where the plugin sat in the list.

Both paths now share one registerPlugins method, so the two cannot drift apart again.

Keeping plugin hooks in the initial identify

Activating hooks after register is only safe if registration still happens before the first identify series opens. On iOS it did not: the series began inside init, while plugin.register ran afterwards in start. Left alone, configured plugin hooks would have missed the initial identify entirely, since afterIdentify replays the snapshot taken at init.

So the tail of init moved into startIdentifyAndGoOnline, and start now:

  1. constructs the instance,
  2. publishes it to LDClient.instances (so a plugin calling LDClient.get() from register still finds it),
  3. registers the configured plugins,
  4. opens the first identify series and goes online.

collectHooks is gone, since init now seeds only the configuration's own hooks. executeBeforeIdentifyHooks takes a methodName so the startup path can reuse it rather than duplicating the series setup.

Test plan

  • Full suite passes (642 tests)
  • testPluginHooksFireDuringInit still passes, confirming configured plugin hooks continue to take part in the initial identify
  • testRegisterPluginDoesNotRunTheRegisteringPluginsOwnHooks covers the new ordering for the runtime path
  • New testConfiguredPluginHooksDoNotObserveAnotherPluginsRegister covers the cross-plugin case

Note

Overview
Plugin hooks now activate after each plugin’s register returns, and only once all plugins in a batch have registered. registerPlugin and LDConfig.plugins both use shared registerPlugins, so runtime and config-time registration behave the same: a plugin’s hooks no longer see its own (or another plugin’s) flag evaluations or identify calls made inside register.

Startup was reordered so configured plugin hooks still participate in the initial identify. The first identify / go-online work moved from init into startIdentifyAndGoOnline, and start now publishes the client to LDClient.instances, runs registerPlugins, then opens that series. init seeds storedHooks from config hooks only ( collectHooks removed). executeBeforeIdentifyHooks accepts a methodName so startup can label the series "init".

Tests were updated for the new hook timing and add coverage that one configured plugin’s hooks do not run during another plugin’s register.

Reviewed by Cursor Bugbot for commit 4002fce. Bugbot is set up for automated code reviews on this repo. Configure here.

abelonogov-ld and others added 4 commits August 21, 2026 13:31
…arts

Plugins could only be supplied through LDConfig, so an integration that
learns about a plugin later — or that wants to instrument a client it did
not configure — had no way in.

Hooks were held in a constant array, which registration after start cannot
extend, so they now live behind a lock and are replaced rather than mutated
in place. Each series reads a snapshot once, so the hooks a series ends with
are the hooks it began with: read again mid-series, a hook registered in
between would be handed an "after" stage for a series whose "before" stage
it was never in.

Hooks go live only once register returns, matching the Android and .NET
ordering, so a plugin's own hooks do not observe its register call.
Retaining EnvironmentMetadata on the instance lets a plugin registered
later be handed the same environment description as one configured up
front, and removes the duplicate construction in start and collectHooks.

Co-authored-by: Cursor <cursoragent@cursor.com>
* v11:
  feat: add registerPlugin so a plugin can be added after the client starts (#522)
  chore: pin the docs toolchain with a Gemfile so jazzy builds reproducibly (#523)

# Conflicts:
#	LaunchDarkly/LaunchDarkly/LDClient.swift
#	LaunchDarkly/LaunchDarkly/LDClientIdentifyHook.swift
#	LaunchDarkly/LaunchDarklyTests/LDClientPluginsSpec.swift
@tanderson-ld

Copy link
Copy Markdown
Contributor

I don't understand the need for this change? I think this violates the intent of getHooks existing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants