launchdarkly/react-native-client-sdk

[iOS] Thread 8: Fatal error: Unexpectedly found nil while unwrapping an Optional value

Freire71 opened this issue · 10 comments

Describe the bug
After upgrading to version 8.0.0 (before I was on 7.16.0) the app crashes on startup after the build. I'm using React Native 0.71.13

To reproduce

  • Upgrade from 7.16.0 to 8.0.0
  • Clean build and Pods folders
  • Run pod install
  • Build the project using XCode

Expected behavior
The app should not crash
Logs
launchdarkly_react_native_client_sdk/LaunchdarklyReactNativeClient.swift:171: Fatal error: Unexpectedly found nil while unwrapping an Optional value

SDK version
8.0.0

OS/platform
MacOS: 12.5
XCode: 14.0.1

Evidence
image

Thank you for reporting this issue. Could you provide a snippet of your context creation code?

To follow up on this @Freire71, we suspected the removal of the deprecated LDUser which we documented here is what is causing the issue, and we will look into updating the documentation to explain that better.

Zeraan commented

I have this problem too. Android side works fine, but iOS crashes with the above error posted. I am upgrading to latest version of React Native, 0.72.4, due to another library's version requirements, and am using those versions:
"launchdarkly-react-client-sdk": "^3.0.9",
"launchdarkly-react-native-client-sdk": "^8.0.0",

This is how I set my context up:

const config = {
  mobileKey: session.launchDarklyClientId,
} as LDConfig
const userContext = { kind: 'user', key: user.id, ...buildLaunchDarklyObject() }
const isInitialized = await checkIfLDClientIsInitialized()
if (!isInitialized) {
  await ldclient.configure(config, userContext)
}
if (ldclient && (isInitialized || (await checkIfLDClientIsInitialized()))) {
  flags = await ldclient.allFlags()
}

buildLaunchDarklyObject() is the custom parameters (used to be embedded as object for "custom: ")
with the checkIfLDClientIsInitialized() being this:

  const checkIfLDClientIsInitialized = async (): Promise<boolean> => {
    let isInitialized = false
    await ldclient
      .isInitialized()
      .then(
        (result: boolean) => {
          isInitialized = result
        },
        (reason: any) => {
          isInitialized = false
        },
      )
      .catch(exception => {
        isInitialized = false
      })
    return isInitialized
  }

Hello @Zeraan, do you know which line of code that is tossing the error? Given you have the kind within the context, this looks like something different. I am also curious about the isInitialized check before calling configure, as the logic in the test app doesn't have that: https://github.com/launchdarkly/react-native-client-sdk/blob/main/ManualTestApp/App.tsx#L68

Zeraan commented

The error is the same as the screenshot in the original post, with the same stack trace. It looks like when I call LDClient.configure, that's when it throws that exception.

I have tried commenting out the call to check for initialization, but that didn't make any difference. From what I understood, checking isInitialized lets you know if you've got LDClient configured, preventing repeated calls in case the screen refreshes?

This is defined within a TSX element which is embedded within NavigationContainer from @react-navigation/native, like this:

        <NavigationContainer>
          <MyLDClient>
            <Stack.Navigator
              ...
            </Stack.Navigator>
          </MyLDClient>
        </NavigationContainer>

Hello @Zeraan, for the isInitialized part, I checked the iOS adapter code - it is handling the behavior before the configure call correctly - if there is no configure call yet, it should not crash and should return a rejected promise.

I will reach out to the engineers to get more information.

Zeraan commented

To clarify, it doesn't look like "isInitialized" is the part that's crashing. The callstack shows "configure" which I believe is the issue. I tried commenting out "kind: 'user'" to see if there's a difference, but it still crashes. Expanding the contextDict, it is missing "kind" in both cases
NoKindProperty

It is weird that the kind is missing even when the input has that value. Do you know some of your code modifying that before passing into the SDK? Also, we updated https://github.com/launchdarkly/hello-react-native-typescript (still upgrading the example within this repo) to use the 8.x version of the RN SDK, can you try your context there and see does it work?

Zeraan commented

Weird thing is, it's now working. I put a hold on this and focused on finishing up the upgrade to RN 0.72.4. One part was updating ESLint to newer version and fixing lint errors. It pointed out one warning about "any" type in my LD client code and I made changes. I wonder if that was it. I will post my before/after codes and maybe it'll aid others.

Old code:

  const buildLaunchDarklyObject = () => {
    const userPropertiesFromDatabase = user.firebaseUserProperties || []
    const userProperties: any = {}
    userPropertiesFromDatabase.forEach((prop: any) => {
      userProperties[prop.key] = prop.value
    })
    
    return {
      ...userProperties,
      .
      .
      .

New code:

  const buildLaunchDarklyObject = () => {
    const userPropertiesFromDatabase = user.firebaseUserProperties || []
    const userProperties: { [key: string]: string } = {}
    userPropertiesFromDatabase.forEach((prop: UserProperty) => {
      userProperties[prop.key] = prop.value
    })
    
    return {
      ...userProperties,
      .
      .
      .

UserProperty is a custom class that simply have "key" and "value" string properties. I'm not sure if this was the culprit, or updating some dependencies/extra libraries, or if me deep cleaning and rebuilding everything fixed it. I will keep an eye out if this pops up again, but it does seem like there's some kind of configuration/dependency requirement that might not be explicitly stated or that we need to ensure to clear caches when updating versions or something. I could've swore that I did clean/build, many times.

Hello @Zeraan, thank you for reporting back and the issue is resolved for you. I hope this is only due to some cached code and will not break again for you. Please feel free to reach out to us if there is anything else.