Skip to content

fix(remoteconfig): update property wrapper when activation removes its key - #16823

Open
paulb777 wants to merge 3 commits into
mainfrom
pb-rc-property-wrapper-removed-key
Open

paulb777 wants to merge 3 commits into
mainfrom
pb-rc-property-wrapper-removed-key

Conversation

@paulb777

@paulb777 paulb777 commented Oct 2, 2026

Copy link
Copy Markdown
Member

Problem

RemoteConfigValueObservable backs @RemoteConfigProperty. When it's created, it uses the key's
remote or in-app default value, and the wrapper's fallback value if the key has neither. On
activation, though, configDidActivate only updated the value if the key still had a remote value.

Activation replaces the whole active config. So when the newly activated template no longer contains
the key (for example, the parameter was deleted or set to "Use in-app default", the scenario
reported in #11095), RemoteConfig returns the in-app default value (or a static value), but the
property kept the previous remote value. SwiftUI views showed stale configuration until the view's
state object was recreated, e.g. after a relaunch.

Change

  • Move the value selection from init into updateConfigValue() and call it from both init and
    configDidActivate. Activation now uses remote, then in-app default, then fallback, like init
    does. That matches the documented fallback semantics: "The value to fall back to if the key
    doesn't exist in remote or default configs".
  • As at initialization, a value that can't be decoded on activation now falls back to the fallback
    value instead of keeping the previous value.
  • Add PropertyWrapperTests.testActivationThatRemovesKeyUsesDefaultOrFallback. It activates a
    template with two keys, one with an in-app default and one without, then activates a template
    without them. It creates RemoteConfigValueObservable directly (hence @testable import),
    because reading a @StateObject outside a SwiftUI view creates a new object on every read.
  • CHANGELOG entry.

Related: #11095 is an open 2023 PR that took the same approach against the old
FirebaseRemoteConfigSwift/Sources/PropertyWrapper path, which no longer exists.

Testing

xcodebuild -scheme RemoteConfigFakeConsole test -destination 'platform=macOS' \
  -only-testing:RemoteConfigFakeConsole/PropertyWrapperTests/testActivationThatRemovesKeyUsesDefaultOrFallback
  • Without the fix, the test fails: XCTAssertEqual failed: ("remote1") is not equal to ("default")
    and XCTAssertEqual failed: ("remote2") is not equal to ("fallback").
  • With the fix, it passes.
  • xcodebuild -scheme RemoteConfigFakeConsole test -destination 'platform=macOS': 49 tests, 0
    failures.
  • -only-testing:RemoteConfigFakeConsole/PropertyWrapperTests -test-iterations 5 -run-tests-until-failure: 20 test runs, 0 failures.

Fixes #16816

…s key

`RemoteConfigValueObservable`, which backs `@RemoteConfigProperty`, starts
with the key's remote or in-app default value, or with the wrapper's
fallback value if the key has neither. On activation, though, it only
updated its value if the key still had a remote value. If the activated
template no longer contained the key, the property kept the previous
remote value instead of the default or fallback value that Remote Config
itself now returns, so SwiftUI views showed stale configuration.

Use the same remote/default/fallback selection on activation as at
initialization. As at initialization, a value that can't be decoded now
falls back to the fallback value instead of keeping the previous value.

Fixes #16816
@gemini-code-assist

Copy link
Copy Markdown
Contributor
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

@paulb777

paulb777 commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates @RemoteConfigProperty to correctly use the in-app default or fallback value when an activation removes its key. The logic for updating the configuration value has been refactored into a helper method updateConfigValue() in RemoteConfigValueObservable, and a new unit test has been added to verify this behavior. The review feedback suggests renaming the local variable configValue within updateConfigValue() to remoteValue to avoid shadowing the class property self.configValue, which improves code readability and safety.

Comment on lines +64 to 75
private func updateConfigValue() {
do {
let configValue: RemoteConfigValue = remoteConfig[key]
if configValue.source == .remote {
if configValue.source == .remote || configValue.source == .default {
self.configValue = try configValue.decoded()
} else {
self.configValue = fallbackValue
}
} catch {
// Suppresses a hard failure if decoding failed.
configValue = fallbackValue
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The local variable configValue shadows the class property self.configValue of the same name but with a different type (RemoteConfigValue vs T). This makes the code harder to read and maintain, especially in the catch block where configValue refers to self.configValue because the local variable is out of scope.

Renaming the local variable to remoteValue and using self.configValue consistently improves clarity and prevents potential bugs during future refactoring.

Suggested change
private func updateConfigValue() {
do {
let configValue: RemoteConfigValue = remoteConfig[key]
if configValue.source == .remote {
if configValue.source == .remote || configValue.source == .default {
self.configValue = try configValue.decoded()
} else {
self.configValue = fallbackValue
}
} catch {
// Suppresses a hard failure if decoding failed.
configValue = fallbackValue
}
}
private func updateConfigValue() {
do {
let remoteValue: RemoteConfigValue = remoteConfig[key]
if remoteValue.source == .remote || remoteValue.source == .default {
self.configValue = try remoteValue.decoded()
} else {
self.configValue = fallbackValue
}
} catch {
self.configValue = fallbackValue
}
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[paulb777-bot] Done in fdadf24. SwiftFormat then removed the now-redundant self., so the helper assigns configValue directly.

Rename the local RemoteConfigValue to remoteValue so that it no longer shadows the configValue property, as suggested in review. No behavior change.

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RemoteConfigProperty stays stale when activation removes a remote key

1 participant