mrousavy / react-native-tracking-transparency

🕵️ A React Native Library for interacting with the tracking API from iOS 14.
MIT License
237 stars 25 forks source link

Not possible to request Tracking Transparency in iOS 15 #15

Open ErickMaeda opened 3 years ago

ErickMaeda commented 3 years ago

Hello,

I've tested in iOS 15 as it was officially released in 2021/09/20

However when calling method from iOS 15 requestTrackingPermission() I receive not-determined.

The same code executed in iOS 14 is working properly.

Do you have any idea why it's not requesting?

Thanks

import React, { useEffect } from 'react';
import { View } from 'react-native'
import {  requestTrackingPermission } from 'react-native-tracking-transparency';

const requestPermission = async () => {
    const trackingStatus = await requestTrackingPermission();
    alert(`trackingStatus ${trackingStatus}`);
}

const App = () => {
   useEffect(() => {
      requestPermission();
   }, []); 
   return <View />;
};

export default App;
josebrc commented 3 years ago

i don't know why doesn't work on iOS 15 , but i changed the requestTrackingPermission call on onPress event in my login button and works fine again.

ErickMaeda commented 3 years ago

i don't know why doesn't work on iOS 15 , but i changed the requestTrackingPermission call on onPress event in my login button and works fine again.

It's weird. But it's working as you said, maybe we cannot ask for tracking transparency on app startup anymore?

josebrc commented 3 years ago

i don't know why doesn't work on iOS 15 , but i changed the requestTrackingPermission call on onPress event in my login button and works fine again.

It's weird. But it's working as you said, maybe we cannot ask for tracking transparency on app startup anymore?

i don'd know, but another way is using a time out function.


setTimeout(async () => {
      if(Platform.OS=='ios'){
        const trackingStatus = await getTrackingStatus();
        if(trackingStatus === 'not-determined'){
          try {
            await requestTrackingPermission();
          } catch (e) {
            Alert.alert('Error', e?.toString?.() ?? e);
          }
        }

      }
    }, 1000);
antoinewg commented 3 years ago

Interested to see why the ATT pop up doesn't show anymore with iOS 15. The fix with setTimeout doesn't work for me, but upon an action of the user, like onPress the pop up does show ✅. Thanks for the suggestion

danielmark0116 commented 3 years ago

In one of our apps, I discovered that the ATT prompt is triggered even when the app is in inactive state, that means when you e.g. prompt a user with push notifications consent at the same time as with the ATT at startup time, it will be skipped. It was not happening on iOS 14 though 🤷🏽‍♂️

What I did in the end, was listening for the app state changes: whenever the app goes from inactive to active and has the ATT permission not defined, it would trigger the prompt. Seems to work

As it turns out, you can also do it later e.g. upon clicking on the login button as well

musti-91 commented 3 years ago

For me in iOS 15 the pop up shows, but any user choice will freeze the app. this lead to close the app and open it again.

liho00 commented 3 years ago

Having this issue

ErickMaeda commented 3 years ago

Sometimes the iOS user can disable the request from all apps. So you cannot display the request when this option is enabled (Idk if it's enabled by default or not)

Apple-App-Tracking-Transparency-1

leonardocroda commented 2 years ago

I was having the same issue, this is caused because in iOS 15 we can only raise the pop up when the app is in active state. To solve this, I added a listener for the AppState change, which can be imported from react-native itself and I just run the function to raise the pop up when the AppState is equal to active.

useEffect(() => {
    const listener = AppState.addEventListener('change', status => {
      if (status === 'active') {
        (async () => {
          const trackingStatus = await getTrackingStatus();
          if (trackingStatus === 'not-determined') {
            requestTrackingPermission();
          }
        })();
      }
    });

    return () => {
      listener && listener.remove();
    };
  }, []);
simoneantonicchio commented 2 years ago

@leonardocroda your work around works, thank you.

fernandopascoalbr commented 2 years ago

May be can trying to change condition @available(iOS 14, *)) to @available(iOS 14, iOS 15, *).

Captura de Tela 2021-11-08 às 00 10 11

I'm trying it now.

zoom2009 commented 2 years ago

i don't know why doesn't work on iOS 15 , but i changed the requestTrackingPermission call on onPress event in my login button and works fine again.

It's weird. But it's working as you said, maybe we cannot ask for tracking transparency on app startup anymore?

i don'd know, but another way is using a time out function.


setTimeout(async () => {
      if(Platform.OS=='ios'){
        const trackingStatus = await getTrackingStatus();
        if(trackingStatus === 'not-determined'){
          try {
            await requestTrackingPermission();
          } catch (e) {
            Alert.alert('Error', e?.toString?.() ?? e);
          }
        }

      }
    }, 1000);

It's work but why ? =.="

a-eid commented 2 years ago

@fpgce how'd it go ?

hubciorz commented 2 years ago

I can confirm that an additional setTimeout is needed. Without it, our app got rejected.

jongha commented 2 years ago

I had the same issue. It was solved by updating the OS to Monterey, execute xcode (click the install menu that appears after running), and then rebuilding.

kuznetsov-from-wonderland commented 2 years ago

I can confirm that an additional setTimeout is needed. Without it, our app got rejected.

Same here

renatomserra commented 2 years ago

Facing the same issue, on a real device even with timeout it doesnt pop up

patik commented 2 years ago

@leonardocroda's solution with AppState.addEventListener worked, however it only asked for permission after the state changed (i.e. put the app in the background, then back to the foreground). To make it also work on launch I had to modify it a little:

React.useEffect(() => {
    if (Platform.OS !== 'ios') {
        return
    }

    const updateTrackingStatus = (status: AppStateStatus) => {
        if (status === 'active') {
            ;(async () => {
                const trackingStatus = await getTrackingStatus()

                if (trackingStatus === 'not-determined') {
                    requestTrackingPermission()
                }
            })()
        }
    }

    // Ready to check the permission now
    if (AppState.currentState === 'active') {
        updateTrackingStatus(AppState.currentState)
    } else {
        // Need to wait until the app is ready before checking the permission
        AppState.addEventListener('change', updateTrackingStatus)

        return () => {
            AppState.removeEventListener('change', updateTrackingStatus)
        }
    }
}, [AppState.currentState])
hugoh59 commented 2 years ago

Where do you import AppStateStatus from ?

patik commented 2 years ago

Where do you import AppStateStatus from ?

import { AppStateStatus } from 'react-native'
Jedidiah commented 1 week ago

Thank you @patik, that helped me out.

For anyone who finds this post 2024 AppState.removeEventListener has been deprecated so you need to update it to call remove on the listener directly like this

React.useEffect(() => {
    if (Platform.OS !== 'ios') {
        return () => {};
    }

    const updateTrackingStatus = (status: AppStateStatus) => {
        if (status === 'active') {
            ;(async () => {
                const trackingStatus = await getTrackingStatus()

                if (trackingStatus === 'not-determined') {
                    requestTrackingPermission()
                }
            })()
        }
    }

    // Ready to check the permission now
    if (AppState.currentState === 'active') {
        updateTrackingStatus(AppState.currentState)
        return () => {}
    } else {
        // Need to wait until the app is ready before checking the permission
        const listener = AppState.addEventListener('change', updateTrackingStatus)
        return () => {
            listener.remove()
        }
    }
}, [AppState.currentState])