Skip to content

[Analytics] Expo: first_open parameter update_with_analytics = 1 after iOS device reset #15643

@ezranbayantemur

Description

@ezranbayantemur

Description

Note: I'm not sure that is a bug or something else but I asked this on StackOverflow too
I’m using Google Firebase Analytics on my React Native iOS app and I’m seeing an unexpected behavior with the first_open event and the update_with_analytics parameter.

According to the Firebase documentation, the parameter update_with_analytics is sent when Analytics is added to an app that was already installed on the device (i.e. Analytics was not present at the time of the original install and was added later).

However, my case does not match that scenario.

Important context

  • The app is more than 2 years old.
  • Firebase Analytics has been integrated in the app since the very first release.
  • Analytics was never added later; it has always been part of the app binary.
  • The iOS device is fully reset using Erase All Content and Settings.
  • No iCloud backup is restored after the reset.

Observed behavior (scenario 1)

  1. The app already includes Firebase Analytics.
  2. The iOS device is fully reset.
  3. No iCloud backup is restored.
  4. The app is installed again from the App Store.
  5. On first launch, Firebase sends a first_open event where
    update_with_analytics = 1.

Based on the documentation, I would expect update_with_analytics to be 0 in this case, since:

  • Analytics is already present in the app binary.
  • This is a clean install on a freshly reset device.
  • The app was not upgraded from a non-Analytics version.
  • No iCloud backup was restored.

Additional observation (scenario 2)

If, after the device reset, the app is deleted again and reinstalled once more from the App Store, then on the next first_open event:

  • update_with_analytics = 0

So the behavior is:

  • First install after device resetupdate_with_analytics = 1
  • Subsequent reinstall from the App Storeupdate_with_analytics = 0

This makes the behavior even more confusing, since both installs are coming from the same App Store binary and both include Analytics.

Question

Why does Firebase Analytics send update_with_analytics = 1 on the first install after an iOS device reset, even though Analytics has always been part of the app and no iCloud backup was restored?

Is update_with_analytics actually triggered by conditions other than “Analytics was added later”, despite what the documentation suggests?

If possible, I’d appreciate clarification on what actually causes update_with_analytics to be set to 1 for first_open events on iOS, especially in device reset scenarios.

Reproducing the issue

No response

Firebase SDK Version

11.15.0

Xcode Version

16.2

Installation Method

CocoaPods

Firebase Product(s)

Analytics

Targeted Platforms

iOS

If using CocoaPods, the project's Podfile.lock

Expand Podfile.lock snippet
# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
  'require.resolve(
    "react-native/scripts/react_native_pods.rb",
    {paths: [process.argv[1]]},
  )', __dir__]).strip

$FirebaseSDKVersion = '11.15.0'

# Disable New Architecture
ENV['RCT_NEW_ARCH_ENABLED'] = '0'
ENV['USE_HERMES'] = '1'

platform :ios, '15.5'
prepare_react_native_project!
linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
  Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
  use_frameworks! :linkage => linkage.to_sym
end

use_frameworks! :linkage => :static

$RNFirebaseAsStaticFramework = true

project 'xxx',
  'Development Debug' => :debug,
  'Development Release' => :release,
  'Production Debug' => :debug,
  'Production Release' => :release

  # Convert all permission pods into static libraries
pre_install do |installer|
  Pod::Installer::Xcode::TargetValidator.send(:define_method, :verify_no_static_framework_transitive_dependencies) {}

  installer.pod_targets.each do |pod|
    if pod.name.eql?('RNPermissions') || pod.name.start_with?('Permission-') || pod.name.eql?('vision-camera-code-scanner') || pod.name.eql?('VisionCamera') || pod.name.eql?('RNReanimated')
      def pod.build_type;
        Pod::BuildType.static_library
      end
    end
  end
end

target 'xxx' do
  config = use_native_modules!

   # ✅ Enable on-device conversion for Firebase Analytics
   $RNFirebaseAnalyticsGoogleAppMeasurementOnDeviceConversion = true
  
  use_react_native!(
    :path => config[:reactNativePath],
    # An absolute path to your application root.
    :app_path => "#{Pod::Config.instance.installation_root}/.."
  )

  #use_flipper!({ 'Flipper' => '0.99.0' }, configurations: ['Development Debug', 'Production Debug'])

  #pod 'Firebase', :modular_headers => true
  #pod 'FirebaseCore', :modular_headers => true
  #pod 'GoogleUtilities', :modular_headers => true
  #$RNFirebaseAsStaticFramework = true

  pod 'GoogleMLKit/FaceDetection', :modular_headers => true
  pod 'GoogleMLKit/ImageLabeling', :modular_headers => true

  post_install do |installer|
    # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
    react_native_post_install(
      installer,
      # Set `mac_catalyst_enabled` to `true` in order to apply patches
      # necessary for Mac Catalyst builds
      config[:reactNativePath],
      :mac_catalyst_enabled => false
    )
    #__apply_Xcode_12_5_M1_post_install_workaround(installer)
    
    installer.pods_project.build_configurations.each do |config|
      config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
    end
    
    installer.pods_project.targets.each do |target|
      if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
        target.build_configurations.each do |config|
            config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
            config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.5'
        end
      end
    end
    
    bitcode_strip_path = `xcrun --find bitcode_strip`.chomp

    def strip_bitcode_from_framework(bitcode_strip_path, framework_relative_path)
      framework_path = File.join(Dir.pwd, framework_relative_path)
      command = "#{bitcode_strip_path} #{framework_path} -r -o #{framework_path}"
      puts "Stripping bitcode: #{command}"
      system(command)
    end

    framework_paths = [
      "Pods/OneSignalXCFramework/iOS_SDK/OneSignalSDK/OneSignal_XCFramework/OneSignal.xcframework/ios-arm64_armv7_armv7s/OneSignal.framework/OneSignal",
      "Pods/OneSignalXCFramework/iOS_SDK/OneSignalSDK/OneSignal_Extension/OneSignalExtension.xcframework/ios-arm64_armv7_armv7s/OneSignalExtension.framework/OneSignalExtension",
      "Pods/OneSignalXCFramework/iOS_SDK/OneSignalSDK/OneSignal_Core/OneSignalCore.xcframework/ios-arm64_armv7_armv7s/OneSignalCore.framework/OneSignalCore",
      "Pods/OneSignalXCFramework/iOS_SDK/OneSignalSDK/OneSignal_Outcomes/OneSignalOutcomes.xcframework/ios-arm64_armv7_armv7s/OneSignalOutcomes.framework/OneSignalOutcomes"
    ]

    framework_paths.each do |framework_relative_path|
      strip_bitcode_from_framework(bitcode_strip_path, framework_relative_path)
    end
  end

  permissions_path = '../node_modules/react-native-permissions/ios'

  pod 'Permission-Camera', :path => "#{permissions_path}/Camera"
  pod 'Permission-PhotoLibrary', :path => "#{permissions_path}/PhotoLibrary"
end

target 'OneSignalNotificationServiceExtension' do
  pod 'OneSignalXCFramework', '>= 3.0', '< 4.0'
end

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