How to force Safari browser to allow DeviceOrientation permission? (SOLVED)

I managed to test an application on an iOS device (iPhone Xs, iOS 16.3) , on Safari web browser.

It’s a Defold HTML5 project where I want to use DeviceOrientation Event, however since iOS 13, there is a requestPermission() method necessary to get permissions to get user’s orientation data. It’s in excperimental state, afaik. Additionally, it must be triggered after any user interaction.

I failed triggering it from regular user input in Defold (on_input calling native extension code to request permission).

So I modified an HTML5 template to get such request on user clicking an additional (awful) button, but I also failed, but this time I managed to force Safari to display a prompt to ask users about it! But failed, because even without clicking "Cancel" - the response was immediately 'denied' and even after I clicked "Accept" the response is still 'denied'. Because of this I can’t get gyroscope data. The same extension is working on Android mobile browsers like Opera, Chrome

Here’s related stack, seems like a very huge issue:

And I wonder if anyone had such experience and can give some tips on how to deal with it?

For future me and you searching for the solution to my question:

function requestDevicePermissions() {
    if (typeof DeviceOrientationEvent.requestPermission === 'function' && typeof DeviceMotionEvent.requestPermission === 'function') {

       const requestOrientationPermission = () => {
           DeviceOrientationEvent.requestPermission().then(permissionState => {
               if (permissionState === 'granted') {
                   window.addEventListener('deviceorientation', function(event) {
                        _processGyroscopeData(event.alpha, event.beta, event.gamma, event.absolute);
                    });
               } else {
                   const button = document.createElement('button');
                   button.innerText = "Enable Orientation";
                    button.style.position = 'absolute';
                    button.style.top = '100%';
                    button.style.left = '100%';
                    button.style.transform = 'translate(50%, 50%)';
                    document.body.appendChild(button);

                    button.addEventListener('click', () => {
                        requestOrientationPermission();
                        requestMotionPermission();
                        document.body.removeChild(button); // Remove button after requesting permissions
                    });
                }
            }).catch(err => {
                console.error("Error requesting deviceorientation permission:", err);
            });
        };

        const requestOrientationAbsolutePermission = () => {
            DeviceOrientationEvent.requestPermission(true).then(permissionState => {
                if (permissionState === 'granted') {
                    window.addEventListener('deviceorientationabsolute', function(event) {
                         _processGyroscopeData(event.alpha, event.beta, event.gamma, event.absolute);
                    });
                } else {
                    console.log("deviceorientationabsolute permission denied.");
                }
            }).catch(err => {
                console.error("Error requesting deviceorientationabsolute permission:", err);
            });
        };

        const requestMotionPermission = () => {
            DeviceMotionEvent.requestPermission().then(response => {
                if (response === 'granted') {
                    window.addEventListener('devicemotion', function(event) {
                        _processAccelerometerData(event.acceleration.x, event.acceleration.y, event.acceleration.z);
                        if (event.accelerationIncludingGravity) {
                            _processAccelerometerDataGravity(
                                event.accelerationIncludingGravity.x,
                                event.accelerationIncludingGravity.y,
                                event.accelerationIncludingGravity.z
                            );
                        }
                        if (event.rotationRate) {
                            _processRotationRateData(
                                event.rotationRate.alpha,
                                event.rotationRate.beta,
                                event.rotationRate.gamma
                            );
                        }
                        window.addEventListener('deviceorientation', function(event) {
                            _processGyroscopeData(event.alpha, event.beta, event.gamma, event.absolute);
                        });
                    });
                    console.log("DeviceMotionEvent permission granted.");
                } else {
                     console.error("DeviceMotionEvent permission denied.");
                }
            }).catch(err => {
                console.error("Error requesting DeviceMotionEvent permission:", err);
            });
        };

        requestOrientationPermission();
        const button = document.createElement('button');
        button.innerText = "Enable Orientation";
        button.style.position = 'absolute';
        button.style.top = '100%';
        button.style.left = '100%';
        button.style.transform = 'translate(50%, 50%)';
        document.body.appendChild(button);

        button.addEventListener('click', () => {
            requestOrientationPermission();
            requestMotionPermission();
            document.body.removeChild(button); // Remove button after requesting permissions
        });
    } else {
        // Automatically start listeners on non-iOS 13+ devices
        window.addEventListener('deviceorientation', function(event) {
            _processGyroscopeData(event.alpha, event.beta, event.gamma);
            console.log("DeviceOrientationEvent listener added automatically.");
        });
        window.addEventListener('devicemotion', function(event) {
            _processAccelerometerData(event.acceleration.x, event.acceleration.y, event.acceleration.z);
            console.log("DeviceMotionEvent listener added automatically.");
        });
    }

It might be too much and too defensive, perhaps some lines are not needed, but at least this code works on both Android and iOS. I put this function in Defold NE in a function startSensorsOnUserInteraction() which includes the above block in EM_ASM() and add call to it:

requestDevicePermissions();

Proper functions in Emscripten should be added to process the acquired data of course (processGyroscopeData, processAccelerometerData, e.g.) and perhaps then either use for calculations or pass to Lua in Native Extension.

1 Like