Skip to content

[iOS] Settings no longer handles setting key to null #51083

Closed

Description

@gfreeman-karmak

Description

We use the Settings API to store various user settings in our app. For some of those, reverting to the default device setting is an option, e.g. language and theme. When a user selects default, we use Settings to set that key to null. Prior to upgrading to new architecture, that worked fine. Now, if you change the setting to something, then change back to default, it will be stuck on the previous setting, i.e. setting to null does nothing. If I turn new arch back off it works again and setting to null clears the value.

We do not use Expo, but this is seen through the expo snack provided.

Steps to reproduce

Use this expo snack
Press Set to 1. Notice the setting is displayed as 1
Press Set to null. Notice the setting is displayed as null
Open the expo dev menu and press Reload
Notice the setting has reverted back to 1

If you switch the expo SDK from v52 to v51 and repeat the above steps, setting to null will clear the value and it will be back to undefined when reloading the app. v51 used the old architecture.

React Native Version

0.79.1

Affected Platforms

Runtime - iOS

Areas

TurboModule - The New Native Module System

Output of npx @react-native-community/cli info

System:
  OS: macOS 15.4.1
  CPU: (10) arm64 Apple M1 Max
  Memory: 1.09 GB / 64.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 22.14.0
    path: /opt/homebrew/bin/node
  Yarn:
    version: 1.22.22
    path: /opt/homebrew/bin/yarn
  npm:
    version: 10.9.2
    path: /opt/homebrew/bin/npm
  Watchman:
    version: 2025.04.14.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /Users/.../.rbenv/shims/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 24.4
      - iOS 18.4
      - macOS 15.4
      - tvOS 18.4
      - visionOS 2.4
      - watchOS 11.4
  Android SDK:
    API Levels:
      - "34"
      - "35"
    Build Tools:
      - 34.0.0
      - 35.0.0
    System Images:
      - android-29 | Google Play ARM 64 v8a
      - android-34 | Google APIs ARM 64 v8a
      - android-34 | Google Play ARM 64 v8a
      - android-35 | Google Play ARM 64 v8a
    Android NDK: 22.1.7171670
IDEs:
  Android Studio: 2024.3 AI-243.24978.46.2431.13363775
  Xcode:
    version: 16.3/16E140
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.15
    path: /usr/bin/javac
  Ruby:
    version: 3.4.3
    path: /Users/.../.rbenv/shims/ruby
npmPackages:
  "@react-native-community/cli":
    installed: 18.0.0
    wanted: 18.0.0
  react:
    installed: 19.0.0
    wanted: 19.0.0
  react-native:
    installed: 0.79.1
    wanted: 0.79.1
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: true
  newArchEnabled: true

Stacktrace or Logs

NA

MANDATORY Reproducer

https://snack.expo.dev/EFzAocYOcozcgkRg83AVT

Screenshots and Videos

No response

Activity

  1. react-native-bot commented on May 2, 2025

    @react-native-bot
    Collaborator

    Tip

    Newer version available: You are on a supported minor version, but it looks like there's a newer patch available - 0.79.2. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases.

  2. react-native-bot commented on May 2, 2025

    @react-native-bot
    Collaborator

    Tip

    Newer version available: You are on a supported minor version, but it looks like there's a newer patch available - undefined. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases.

  3. gfreeman-karmak commented on May 2, 2025

    @gfreeman-karmak
    Author

    confirmed, still an issue in 0.79.2

  4. cipolleschi commented on May 6, 2025

    @cipolleschi
    Contributor

    HI @gfreeman-karmak, thanks for opening the issue. This is a change in the semantic of how modules handles null values between the Old and the New Architecture. This might be a deliberate design decision, made before my time.
    We already have a lot of code depending on this behavior and external libraries might relies on that too. By changing this semantic, we might break a lot of libraries that now relies on the null values being stripped out.

    I'm not saying that we can't change this, but it requires a bit of discussion internally.

  5. gfreeman-karmak commented on May 6, 2025

    @gfreeman-karmak
    Author

    @cipolleschi hey thanks for the update. I see the PR is iOS specific, so does that mean the Android implementation of turbo modules does not strip null values out? I haven't converted our own native modules to turbo modules yet, but the platforms should be consistent.

  6. cipolleschi commented on May 9, 2025

    @cipolleschi
    Contributor

    @gfreeman-karmak Settings is supported only on iOS, as you can see from here and by exploring the SettingsFallback.js.

    I need to setup a couple of more complex reproducers to verify that on Android, but yes, it could be an iOS only bug.

  7. cipolleschi commented on May 9, 2025

    @cipolleschi
    Contributor

    I can confirm that this is an iOS bug only. As you see in the image, the Android implementeation won't drop the null value.

    Image

    I also checked what a legacy native module would receive.
    On the Legacy architecture, it receives a dictionary like this:

    {
      "key": "myKey";
      "value": "<null>"; 
    }
    

    In the New Architecture, it receives a dictionary like this:

    {
      "key": "myKey";
    }
    

    I believe that the New Arch has been implemented like that because if you try to put the value of "<null>" in some places, e.g. user defaults and collections, the app might crash. However, the proper approach would be to keep the "<null>" and handle the null value properly.

    Note: the "<null>" string is the string representation of [NSNull null].

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions